147 lines · cpp
1//===--- QueryCheck.cpp - clang-tidy --------------------------------------===//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===//8 9#include "QueryCheck.h"10#include "../../clang-query/Query.h"11#include "../../clang-query/QueryParser.h"12#include "clang/ASTMatchers/ASTMatchers.h"13#include "clang/ASTMatchers/Dynamic/VariantValue.h"14#include "clang/Basic/DiagnosticIDs.h"15#include "llvm/ADT/SmallVector.h"16#include "llvm/ADT/StringRef.h"17#include <string>18 19using namespace clang::ast_matchers;20 21namespace clang::tidy::custom {22 23static void emitConfigurationDiag(ClangTidyContext *Context, StringRef Message,24 StringRef CheckName) {25 Context->configurationDiag("%0 in '%1'", DiagnosticIDs::Warning)26 << Message << CheckName;27}28 29static SmallVector<ast_matchers::dynamic::DynTypedMatcher>30parseQuery(const ClangTidyOptions::CustomCheckValue &V,31 ClangTidyContext *Context) {32 SmallVector<ast_matchers::dynamic::DynTypedMatcher> Matchers{};33 clang::query::QuerySession QS({});34 llvm::StringRef QueryStringRef{V.Query};35 while (!QueryStringRef.empty()) {36 const query::QueryRef Q = query::QueryParser::parse(QueryStringRef, QS);37 switch (Q->Kind) {38 case query::QK_Match: {39 const auto &MatchQuery = llvm::cast<query::MatchQuery>(*Q);40 Matchers.push_back(MatchQuery.Matcher);41 break;42 }43 case query::QK_Let: {44 const auto &LetQuery = llvm::cast<query::LetQuery>(*Q);45 LetQuery.run(llvm::errs(), QS);46 break;47 }48 case query::QK_NoOp: {49 const auto &NoOpQuery = llvm::cast<query::NoOpQuery>(*Q);50 NoOpQuery.run(llvm::errs(), QS);51 break;52 }53 case query::QK_Invalid: {54 const auto &InvalidQuery = llvm::cast<query::InvalidQuery>(*Q);55 emitConfigurationDiag(Context, InvalidQuery.ErrStr, V.Name);56 return {};57 }58 // FIXME: TODO59 case query::QK_File: {60 emitConfigurationDiag(Context, "unsupported query kind 'File'", V.Name);61 return {};62 }63 case query::QK_DisableOutputKind: {64 emitConfigurationDiag(65 Context, "unsupported query kind 'DisableOutputKind'", V.Name);66 return {};67 }68 case query::QK_EnableOutputKind: {69 emitConfigurationDiag(70 Context, "unsupported query kind 'EnableOutputKind'", V.Name);71 return {};72 }73 case query::QK_SetOutputKind: {74 emitConfigurationDiag(Context, "unsupported query kind 'SetOutputKind'",75 V.Name);76 return {};77 }78 case query::QK_SetTraversalKind: {79 emitConfigurationDiag(80 Context, "unsupported query kind 'SetTraversalKind'", V.Name);81 return {};82 }83 case query::QK_SetBool: {84 emitConfigurationDiag(Context, "unsupported query kind 'SetBool'",85 V.Name);86 return {};87 }88 case query::QK_Help: {89 emitConfigurationDiag(Context, "unsupported query kind 'Help'", V.Name);90 return {};91 }92 case query::QK_Quit: {93 emitConfigurationDiag(Context, "unsupported query kind 'Quit'", V.Name);94 return {};95 }96 }97 QueryStringRef = Q->RemainingContent;98 }99 return Matchers;100}101 102QueryCheck::QueryCheck(llvm::StringRef Name,103 const ClangTidyOptions::CustomCheckValue &V,104 ClangTidyContext *Context)105 : ClangTidyCheck(Name, Context) {106 for (const ClangTidyOptions::CustomCheckDiag &D : V.Diags) {107 auto DiagnosticIdIt =108 Diags109 .try_emplace(D.Level.value_or(DiagnosticIDs::Warning),110 llvm::StringMap<llvm::SmallVector<std::string>>{})111 .first;112 auto DiagMessageIt =113 DiagnosticIdIt->getSecond()114 .try_emplace(D.BindName, llvm::SmallVector<std::string>{})115 .first;116 DiagMessageIt->second.emplace_back(D.Message);117 }118 Matchers = parseQuery(V, Context);119}120 121void QueryCheck::registerMatchers(MatchFinder *Finder) {122 for (const ast_matchers::dynamic::DynTypedMatcher &M : Matchers)123 Finder->addDynamicMatcher(M, this);124}125 126void QueryCheck::check(const MatchFinder::MatchResult &Result) {127 auto Emit = [this](const DiagMaps &DiagMaps, const std::string &BindName,128 const DynTypedNode &Node, DiagnosticIDs::Level Level) {129 const DiagMaps::const_iterator DiagMapIt = DiagMaps.find(Level);130 if (DiagMapIt == DiagMaps.end())131 return;132 const BindNameMapToDiagMessage &BindNameMap = DiagMapIt->second;133 const BindNameMapToDiagMessage::const_iterator BindNameMapIt =134 BindNameMap.find(BindName);135 if (BindNameMapIt == BindNameMap.end())136 return;137 for (const std::string &Message : BindNameMapIt->second)138 diag(Node.getSourceRange().getBegin(), Message, Level);139 };140 for (const auto &[Name, Node] : Result.Nodes.getMap())141 Emit(Diags, Name, Node, DiagnosticIDs::Warning);142 // place Note last, otherwise it will not be emitted143 for (const auto &[Name, Node] : Result.Nodes.getMap())144 Emit(Diags, Name, Node, DiagnosticIDs::Note);145}146} // namespace clang::tidy::custom147