/**************************************************************************** ** ** Copyright (C) 2016 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of Qt Creator. ** ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 as published by the Free Software ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ****************************************************************************/ #include "clangquery.h" #include "sourcelocationsutils.h" #include "sourcerangeextractor.h" #include #include #include #include #include #include #include using clang::ast_matchers::dynamic::Diagnostics; using clang::ast_matchers::dynamic::Parser; using clang::ast_matchers::BoundNodes; using clang::ast_matchers::MatchFinder; namespace ClangBackEnd { struct CollectBoundNodes : MatchFinder::MatchCallback { std::vector &Bindings; CollectBoundNodes(std::vector &Bindings) : Bindings(Bindings) {} void run(const MatchFinder::MatchResult &Result) override { Bindings.push_back(Result.Nodes); } }; ClangQuery::ClangQuery(FilePathCachingInterface &filePathCache, Utils::SmallString &&query) : m_query(std::move(query)), m_filePathCache(filePathCache) { } void ClangQuery::setQuery(Utils::SmallString &&query) { this->m_query = std::move(query); } void ClangQuery::findLocations() { auto tool = createTool(); std::vector> asts; tool.buildASTs(asts); std::for_each (std::make_move_iterator(asts.begin()), std::make_move_iterator(asts.end()), [&] (std::unique_ptr &&ast) { Diagnostics diagnostics; auto optionalMatcher = Parser::parseMatcherExpression({m_query.data(), m_query.size()}, nullptr, &diagnostics); parseDiagnostics(diagnostics); matchLocation(optionalMatcher, std::move(ast)); }); } SourceRangesContainer ClangQuery::takeSourceRanges() { return std::move(m_sourceRangesContainer); } DynamicASTMatcherDiagnosticContainers ClangQuery::takeDiagnosticContainers() { return std::move(m_diagnosticContainers_); } namespace { V2::SourceRangeContainer convertToContainer(const clang::ast_matchers::dynamic::SourceRange sourceRange) { return V2::SourceRangeContainer(0, sourceRange.Start.Line, sourceRange.Start.Column, 0, sourceRange.End.Line, sourceRange.End.Column, 0); } #define ERROR_RETURN_CASE(name) \ case Diagnostics::ET_##name: return ClangQueryDiagnosticErrorType::name; ClangQueryDiagnosticErrorType convertToErrorType(Diagnostics::ErrorType clangErrorType) { switch (clangErrorType) { ERROR_RETURN_CASE(None) ERROR_RETURN_CASE(RegistryMatcherNotFound) ERROR_RETURN_CASE(RegistryWrongArgCount) ERROR_RETURN_CASE(RegistryWrongArgType) ERROR_RETURN_CASE(RegistryNotBindable) ERROR_RETURN_CASE(RegistryAmbiguousOverload) ERROR_RETURN_CASE(RegistryValueNotFound) ERROR_RETURN_CASE(ParserStringError) ERROR_RETURN_CASE(ParserNoOpenParen) ERROR_RETURN_CASE(ParserNoCloseParen) ERROR_RETURN_CASE(ParserNoComma) ERROR_RETURN_CASE(ParserNoCode) ERROR_RETURN_CASE(ParserNotAMatcher) ERROR_RETURN_CASE(ParserInvalidToken) ERROR_RETURN_CASE(ParserMalformedBindExpr) ERROR_RETURN_CASE(ParserTrailingCode) ERROR_RETURN_CASE(ParserNumberError) ERROR_RETURN_CASE(ParserOverloadedType) } Q_UNREACHABLE(); } #define CONTEXT_RETURN_CASE(name) \ case Diagnostics::CT_##name: return ClangQueryDiagnosticContextType::name; ClangQueryDiagnosticContextType convertToContextType(Diagnostics::ContextType clangContextType) { switch (clangContextType) { CONTEXT_RETURN_CASE(MatcherArg) CONTEXT_RETURN_CASE(MatcherConstruct) } Q_UNREACHABLE(); } } void ClangQuery::parseDiagnostics(const clang::ast_matchers::dynamic::Diagnostics &diagnostics) { auto errors = diagnostics.errors(); for (const auto &errorContent : errors) { m_diagnosticContainers_.emplace_back(); DynamicASTMatcherDiagnosticContainer &diagnosticContainer = m_diagnosticContainers_.back(); for (const auto &message : errorContent.Messages) { diagnosticContainer.insertMessage(convertToContainer(message.Range), convertToErrorType(message.Type), Utils::SmallStringVector(message.Args)); } for (const auto &message : errorContent.ContextStack) { diagnosticContainer.insertContext(convertToContainer(message.Range), convertToContextType(message.Type), Utils::SmallStringVector(message.Args)); } } } namespace { std::vector generateSourceRangesFromMatches(const std::vector &matches) { std::vector sourceRanges; sourceRanges.reserve(matches.size()); for (const auto boundNodes : matches) { for (const auto &mapEntry : boundNodes.getMap()) { const auto sourceRange = mapEntry.second.getSourceRange(); if (sourceRange.isValid()) sourceRanges.push_back(sourceRange); } } return sourceRanges; } } void ClangQuery::matchLocation( const llvm::Optional< clang::ast_matchers::internal::DynTypedMatcher> &optionalStartMatcher, std::unique_ptr ast) { if (optionalStartMatcher) { auto matcher = *optionalStartMatcher; auto optionalMatcher = matcher.tryBind("root"); matcher = *optionalMatcher; MatchFinder finder; std::vector matches; CollectBoundNodes collectBoundNodes(matches); finder.addDynamicMatcher(matcher, &collectBoundNodes); finder.matchAST(ast->getASTContext()); auto sourceRanges = generateSourceRangesFromMatches(matches); SourceRangeExtractor extractor(ast->getSourceManager(), ast->getLangOpts(), m_filePathCache, m_sourceRangesContainer); extractor.addSourceRanges(sourceRanges); } } } // namespace ClangBackEnd