Skip to content

Commit a686695

Browse files
DeNiCoNHerrCai0907
authored andcommitted
origin pr
1 parent 3b1e18c commit a686695

File tree

9 files changed

+254
-1
lines changed

9 files changed

+254
-1
lines changed

clang-tools-extra/clang-tidy/CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ include_directories(BEFORE ${CMAKE_CURRENT_BINARY_DIR})
1111
add_clang_library(clangTidy STATIC
1212
ClangTidy.cpp
1313
ClangTidyCheck.cpp
14+
ClangQueryCheck.cpp
1415
ClangTidyModule.cpp
1516
ClangTidyDiagnosticConsumer.cpp
1617
ClangTidyOptions.cpp
@@ -38,6 +39,7 @@ clang_target_link_libraries(clangTidy
3839
clangSerialization
3940
clangTooling
4041
clangToolingCore
42+
clangQuery
4143
)
4244

4345
if(CLANG_TIDY_ENABLE_STATIC_ANALYZER)
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
//===--- ClangQueryCheck.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 "ClangQueryCheck.h"
10+
#include "clang/ASTMatchers/ASTMatchFinder.h"
11+
12+
using namespace clang::ast_matchers;
13+
14+
namespace clang::tidy::misc {
15+
16+
void ClangQueryCheck::registerMatchers(MatchFinder *Finder) {
17+
for (const auto &Matcher : Matchers) {
18+
bool Ok = Finder->addDynamicMatcher(Matcher, this);
19+
assert(Ok && "Expected to get top level matcher from query parser");
20+
}
21+
}
22+
23+
void ClangQueryCheck::check(const MatchFinder::MatchResult &Result) {
24+
auto Map = Result.Nodes.getMap();
25+
for (const auto &[k, v] : Map) {
26+
diag(v.getSourceRange().getBegin(), k) << v.getSourceRange();
27+
}
28+
}
29+
30+
} // namespace clang::tidy::misc
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
//===--- ClangQueryCheck.h - clang-tidy --------------------*- C++ -*-===//
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+
#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CLANGQUERYCHECK_H
10+
#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CLANGQUERYCHECK_H
11+
12+
#include "ClangTidyCheck.h"
13+
#include "clang/ASTMatchers/Dynamic/VariantValue.h"
14+
#include <vector>
15+
16+
namespace clang::query {
17+
class QuerySession;
18+
} // namespace clang::query
19+
20+
namespace clang::tidy::misc {
21+
22+
/// A check that matches a given matchers printing their binds as warnings
23+
class ClangQueryCheck : public ClangTidyCheck {
24+
using MatcherVec = std::vector<ast_matchers::dynamic::DynTypedMatcher>;
25+
26+
public:
27+
ClangQueryCheck(StringRef Name, ClangTidyContext *Context,
28+
MatcherVec Matchers)
29+
: ClangTidyCheck(Name, Context), Matchers(std::move(Matchers)) {}
30+
31+
void registerMatchers(ast_matchers::MatchFinder *Finder) override;
32+
void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
33+
bool isLanguageVersionSupported(const LangOptions &LangOpts) const override {
34+
return LangOpts.CPlusPlus;
35+
}
36+
37+
private:
38+
MatcherVec Matchers;
39+
};
40+
41+
} // namespace clang::tidy::misc
42+
43+
#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CLANGQUERYCHECK_H

clang-tools-extra/clang-tidy/ClangTidy.cpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
//===----------------------------------------------------------------------===//
1616

1717
#include "ClangTidy.h"
18+
#include "ClangQueryCheck.h"
1819
#include "ClangTidyCheck.h"
1920
#include "ClangTidyDiagnosticConsumer.h"
2021
#include "ClangTidyModuleRegistry.h"
@@ -350,6 +351,13 @@ ClangTidyASTConsumerFactory::ClangTidyASTConsumerFactory(
350351
std::unique_ptr<ClangTidyModule> Module = E.instantiate();
351352
Module->addCheckFactories(*CheckFactories);
352353
}
354+
355+
for (const auto &[k, v] : Context.getOptions().ClangQueryChecks) {
356+
CheckFactories->registerCheckFactory(k, [v](StringRef Name,
357+
ClangTidyContext *Context) {
358+
return std::make_unique<misc::ClangQueryCheck>(Name, Context, v.Matchers);
359+
});
360+
}
353361
}
354362

355363
#if CLANG_TIDY_ENABLE_STATIC_ANALYZER

clang-tools-extra/clang-tidy/ClangTidyOptions.cpp

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@
77
//===----------------------------------------------------------------------===//
88

99
#include "ClangTidyOptions.h"
10+
#include "../clang-query/Query.h"
11+
#include "../clang-query/QueryParser.h"
1012
#include "ClangTidyModuleRegistry.h"
1113
#include "clang/Basic/LLVM.h"
1214
#include "llvm/ADT/SmallString.h"
@@ -126,6 +128,83 @@ void yamlize(IO &IO, ClangTidyOptions::OptionMap &Val, bool,
126128
}
127129
}
128130

131+
std::vector<clang::ast_matchers::dynamic::DynTypedMatcher>
132+
processQuerySource(IO &IO, StringRef SourceRef,
133+
clang::query::QuerySession &QS) {
134+
namespace query = clang::query;
135+
std::vector<clang::ast_matchers::dynamic::DynTypedMatcher> Matchers;
136+
137+
while (!SourceRef.empty()) {
138+
query::QueryRef Q = query::QueryParser::parse(SourceRef, QS);
139+
switch (Q->Kind) {
140+
case query::QK_Match: {
141+
const auto &MatchQuerry = llvm::cast<query::MatchQuery>(*Q);
142+
Matchers.push_back(MatchQuerry.Matcher);
143+
break;
144+
}
145+
case query::QK_Let: {
146+
const auto &LetQuerry = llvm::cast<query::LetQuery>(*Q);
147+
LetQuerry.run(llvm::errs(), QS);
148+
break;
149+
}
150+
case query::QK_Invalid: {
151+
const auto &InvalidQuerry = llvm::cast<query::InvalidQuery>(*Q);
152+
for (const auto &Line : llvm::split(InvalidQuerry.ErrStr, "\n")) {
153+
IO.setError(Line);
154+
}
155+
break;
156+
}
157+
// FIXME FileQuerry should also be supported, but what to do with relative
158+
// paths?
159+
case query::QK_File:
160+
case query::QK_DisableOutputKind:
161+
case query::QK_EnableOutputKind:
162+
case query::QK_SetOutputKind:
163+
case query::QK_SetTraversalKind:
164+
case query::QK_Help:
165+
case query::QK_NoOp:
166+
case query::QK_Quit:
167+
case query::QK_SetBool: {
168+
IO.setError("unsupported querry kind");
169+
}
170+
}
171+
SourceRef = Q->RemainingContent;
172+
}
173+
174+
return Matchers;
175+
}
176+
177+
template <>
178+
void yamlize(IO &IO, ClangTidyOptions::QueryCheckMap &Val, bool,
179+
EmptyContext &Ctx) {
180+
IO.beginMapping();
181+
if (IO.outputting()) {
182+
for (auto &[k, v] : Val) {
183+
IO.mapRequired(k.data(), v);
184+
}
185+
} else {
186+
for (StringRef Key : IO.keys()) {
187+
IO.mapRequired(Key.data(), Val[Key]);
188+
}
189+
}
190+
IO.endMapping();
191+
}
192+
193+
template <>
194+
void yamlize(IO &IO, ClangTidyOptions::QueryCheckValue &Val, bool,
195+
EmptyContext &Ctx) {
196+
if (IO.outputting()) {
197+
StringRef SourceRef = Val.Source;
198+
IO.blockScalarString(SourceRef);
199+
} else {
200+
StringRef SourceRef;
201+
IO.blockScalarString(SourceRef);
202+
Val.Source = SourceRef;
203+
clang::query::QuerySession QS({});
204+
Val.Matchers = processQuerySource(IO, SourceRef, QS);
205+
}
206+
}
207+
129208
struct ChecksVariant {
130209
std::optional<std::string> AsString;
131210
std::optional<std::vector<std::string>> AsVector;
@@ -181,6 +260,7 @@ template <> struct MappingTraits<ClangTidyOptions> {
181260
IO.mapOptional("InheritParentConfig", Options.InheritParentConfig);
182261
IO.mapOptional("UseColor", Options.UseColor);
183262
IO.mapOptional("SystemHeaders", Options.SystemHeaders);
263+
IO.mapOptional("ClangQueryChecks", Options.ClangQueryChecks);
184264
}
185265
};
186266

@@ -249,6 +329,10 @@ ClangTidyOptions &ClangTidyOptions::mergeWith(const ClangTidyOptions &Other,
249329
ClangTidyValue(KeyValue.getValue().Value,
250330
KeyValue.getValue().Priority + Order));
251331
}
332+
333+
for (const auto &KeyValue : Other.ClangQueryChecks) {
334+
ClangQueryChecks.insert_or_assign(KeyValue.getKey(), KeyValue.getValue());
335+
}
252336
return *this;
253337
}
254338

clang-tools-extra/clang-tidy/ClangTidyOptions.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CLANGTIDYOPTIONS_H
1010
#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CLANGTIDYOPTIONS_H
1111

12+
#include "clang/ASTMatchers/Dynamic/VariantValue.h"
1213
#include "llvm/ADT/IntrusiveRefCntPtr.h"
1314
#include "llvm/ADT/SmallString.h"
1415
#include "llvm/ADT/StringMap.h"
@@ -126,8 +127,15 @@ struct ClangTidyOptions {
126127
using StringPair = std::pair<std::string, std::string>;
127128
using OptionMap = llvm::StringMap<ClangTidyValue>;
128129

130+
struct QueryCheckValue {
131+
std::string Source;
132+
std::vector<ast_matchers::dynamic::DynTypedMatcher> Matchers;
133+
};
134+
using QueryCheckMap = llvm::StringMap<QueryCheckValue>;
135+
129136
/// Key-value mapping used to store check-specific options.
130137
OptionMap CheckOptions;
138+
QueryCheckMap ClangQueryChecks;
131139

132140
using ArgList = std::vector<std::string>;
133141

clang-tools-extra/clang-tidy/tool/ClangTidyMain.cpp

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,18 @@ Configuration files:
6060
Checks - Same as '--checks'. Additionally, the list of
6161
globs can be specified as a list instead of a
6262
string.
63+
ClangQueryChecks - List of key-value pairs. Key specifies a name
64+
of the new check and value specifies a list
65+
of matchers in the form of clang-query
66+
syntax. Example:
67+
ClangQueryChecks:
68+
custom-check: |
69+
let matcher varDecl(
70+
hasTypeLoc(
71+
typeLoc().bind("Custom message")
72+
)
73+
)
74+
match matcher
6375
ExcludeHeaderFilterRegex - Same as '--exclude-header-filter'.
6476
ExtraArgs - Same as '--extra-arg'.
6577
ExtraArgsBefore - Same as '--extra-arg-before'.
@@ -483,7 +495,8 @@ static StringRef closest(StringRef Value, const StringSet<> &Allowed) {
483495
return Closest;
484496
}
485497

486-
static constexpr StringLiteral VerifyConfigWarningEnd = " [-verify-config]\n";
498+
static constexpr llvm::StringLiteral VerifyConfigWarningEnd =
499+
" [-verify-config]\n";
487500

488501
static bool verifyChecks(const StringSet<> &AllChecks, StringRef CheckGlob,
489502
StringRef Source) {

clang-tools-extra/docs/clang-tidy/index.rst

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -292,6 +292,18 @@ An overview of all the command-line options:
292292
Checks - Same as '--checks'. Additionally, the list of
293293
globs can be specified as a list instead of a
294294
string.
295+
ClangQueryChecks - List of key-value pairs. Key specifies a name
296+
of the new check and value specifies a list
297+
of matchers in the form of clang-query
298+
syntax. Example:
299+
ClangQueryChecks:
300+
custom-check: |
301+
let matcher varDecl(
302+
hasTypeLoc(
303+
typeLoc().bind("Custom message")
304+
)
305+
)
306+
match matcher
295307
ExcludeHeaderFilterRegex - Same as '--exclude-header-filter'.
296308
ExtraArgs - Same as '--extra-arg'.
297309
ExtraArgsBefore - Same as '--extra-arg-before'.
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
// DEFINE: %{custom-call-yaml} = custom-call: 'm callExpr().bind(\"Custom message\")'
2+
//
3+
// DEFINE: %{custom-let-call-yaml} = custom-let-call: \" \
4+
// DEFINE: let expr varDecl( \
5+
// DEFINE: hasType(asString(\\\"long long\\\")), \
6+
// DEFINE: hasTypeLoc(typeLoc().bind(\\\"Let message\\\")) \
7+
// DEFINE: ) \n \
8+
// DEFINE: match expr\"
9+
//
10+
// DEFINE: %{full-config} = "{ClangQueryChecks: {%{custom-call-yaml},%{custom-let-call-yaml}}}"
11+
12+
//Check single match expression
13+
// RUN: clang-tidy %s -checks='-*, custom-*' \
14+
// RUN: -config="{ClangQueryChecks: {%{custom-call-yaml}}}" \
15+
// RUN: -- | FileCheck %s -check-prefix=CHECK-CUSTOM-CALL
16+
17+
void a() {
18+
}
19+
20+
// CHECK-CUSTOM-CALL: warning: Custom message [custom-call]
21+
// CHECK-CUSTOM-CALL-NEXT: a();{{$}}
22+
void b() {
23+
a();
24+
}
25+
26+
//Check let with match expression
27+
// RUN: clang-tidy %s -checks='-*, custom-*' \
28+
// RUN: -config="{ClangQueryChecks: {%{custom-let-call-yaml}}}" \
29+
// RUN: -- | FileCheck %s -check-prefix=CHECK-CUSTOM-LET
30+
void c() {
31+
// CHECK-CUSTOM-LET: warning: Let message [custom-let-call]
32+
// CHECK-CUSTOM-LET-NEXT: long long test_long_long = 0;{{$}}
33+
long long test_long_long_nolint = 0; //NOLINT(custom-let-call)
34+
long long test_long_long = 0;
35+
}
36+
37+
//Check multiple checks in one config
38+
// RUN: clang-tidy %s -checks='-*, custom-*' \
39+
// RUN: -config=%{full-config} \
40+
// RUN: -- | FileCheck %s -check-prefixes=CHECK-CUSTOM-CALL,CHECK-CUSTOM-LET
41+
42+
//Check multiple checks in one config but only one enabled
43+
// RUN: clang-tidy %s -checks='-*, custom-call' \
44+
// RUN: -config=%{full-config} \
45+
// RUN: -- | FileCheck %s -check-prefixes=CHECK-CUSTOM-CALL --implicit-check-not warning:
46+
47+
//Check config dump
48+
// RUN: clang-tidy -dump-config -checks='-*, custom-*' \
49+
// RUN: -config=%{full-config} \
50+
// RUN: -- | FileCheck %s -check-prefix=CHECK-CONFIG
51+
// CHECK-CONFIG: ClangQueryChecks:
52+
// CHECK-CONFIG-DAG: custom-let-call:
53+
// CHECK-CONFIG-DAG: custom-call: |{{$[[:space:]]}} m callExpr().bind("Custom message")

0 commit comments

Comments
 (0)