clang-tools 22.0.0git
QueryCheck.cpp
Go to the documentation of this file.
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-exception
6//
7//===----------------------------------------------------------------------===//
8
9#include "QueryCheck.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>
31 ClangTidyContext *Context) {
32 SmallVector<ast_matchers::dynamic::DynTypedMatcher> Matchers{};
34 llvm::StringRef QueryStringRef{V.Query};
35 while (!QueryStringRef.empty()) {
36 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: TODO
59 case query::QK_File: {
60 emitConfigurationDiag(Context, "unsupported query kind 'File'", V.Name);
61 return {};
62 }
65 Context, "unsupported query kind 'DisableOutputKind'", V.Name);
66 return {};
67 }
70 Context, "unsupported query kind 'EnableOutputKind'", V.Name);
71 return {};
72 }
74 emitConfigurationDiag(Context, "unsupported query kind 'SetOutputKind'",
75 V.Name);
76 return {};
77 }
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,
104 ClangTidyContext *Context)
105 : ClangTidyCheck(Name, Context) {
106 for (const ClangTidyOptions::CustomCheckDiag &D : V.Diags) {
107 auto DiagnosticIdIt =
108 Diags
109 .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 DiagMaps::const_iterator DiagMapIt = DiagMaps.find(Level);
130 if (DiagMapIt == DiagMaps.end())
131 return;
132 const BindNameMapToDiagMessage &BindNameMap = DiagMapIt->second;
133 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 emitted
143 for (const auto &[Name, Node] : Result.Nodes.getMap())
144 Emit(Diags, Name, Node, DiagnosticIDs::Note);
145}
146} // namespace clang::tidy::custom
static QueryRef parse(StringRef Line, const QuerySession &QS)
Parse Line as a query.
Represents the state for a particular clang-query session.
Every ClangTidyCheck reports errors through a DiagnosticsEngine provided by this context.
DiagnosticBuilder configurationDiag(StringRef Message, DiagnosticIDs::Level Level=DiagnosticIDs::Warning)
Report any errors to do with reading the configuration using this method.
QueryCheck(llvm::StringRef Name, const ClangTidyOptions::CustomCheckValue &V, ClangTidyContext *Context)
void check(const ast_matchers::MatchFinder::MatchResult &Result) override
void registerMatchers(ast_matchers::MatchFinder *Finder) override
llvm::IntrusiveRefCntPtr< Query > QueryRef
Definition Query.h:52
@ QK_Invalid
Definition Query.h:23
@ QK_DisableOutputKind
Definition Query.h:32
@ QK_EnableOutputKind
Definition Query.h:31
@ QK_SetOutputKind
Definition Query.h:29
@ QK_SetTraversalKind
Definition Query.h:30
@ QK_SetBool
Definition Query.h:28
static void emitConfigurationDiag(ClangTidyContext *Context, StringRef Message, StringRef CheckName)
static SmallVector< ast_matchers::dynamic::DynTypedMatcher > parseQuery(const ClangTidyOptions::CustomCheckValue &V, ClangTidyContext *Context)
llvm::SmallVector< CustomCheckDiag > Diags