Skip to content

Commit b43991f

Browse files
committed
Merge remote-tracking branch 'origin/users/ccc/clang-tidy/query-check' into users/ccc/clang-tidy/query-check
2 parents bc0c4db + 5bbf53d commit b43991f

25 files changed

+534
-6
lines changed

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ add_subdirectory(bugprone)
5858
add_subdirectory(cert)
5959
add_subdirectory(concurrency)
6060
add_subdirectory(cppcoreguidelines)
61+
add_subdirectory(custom)
6162
add_subdirectory(darwin)
6263
add_subdirectory(fuchsia)
6364
add_subdirectory(google)
@@ -85,6 +86,7 @@ set(ALL_CLANG_TIDY_CHECKS
8586
clangTidyCERTModule
8687
clangTidyConcurrencyModule
8788
clangTidyCppCoreGuidelinesModule
89+
clangTidyCustomModule
8890
clangTidyDarwinModule
8991
clangTidyFuchsiaModule
9092
clangTidyGoogleModule

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

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,11 @@ LLVM_INSTANTIATE_REGISTRY(clang::tidy::ClangTidyModuleRegistry)
5757

5858
namespace clang::tidy {
5959

60+
namespace custom {
61+
extern void registerCustomChecks(ClangTidyOptions const &O,
62+
ClangTidyCheckFactories &Factories);
63+
} // namespace custom
64+
6065
namespace {
6166
#if CLANG_TIDY_ENABLE_STATIC_ANALYZER
6267
static const char *AnalyzerCheckNamePrefix = "clang-analyzer-";
@@ -345,6 +350,7 @@ ClangTidyASTConsumerFactory::ClangTidyASTConsumerFactory(
345350
IntrusiveRefCntPtr<llvm::vfs::OverlayFileSystem> OverlayFS)
346351
: Context(Context), OverlayFS(std::move(OverlayFS)),
347352
CheckFactories(new ClangTidyCheckFactories) {
353+
custom::registerCustomChecks(Context.getOptions(), *CheckFactories);
348354
for (ClangTidyModuleRegistry::entry E : ClangTidyModuleRegistry::entries()) {
349355
std::unique_ptr<ClangTidyModule> Module = E.instantiate();
350356
Module->addCheckFactories(*CheckFactories);
@@ -415,7 +421,7 @@ ClangTidyASTConsumerFactory::createASTConsumer(
415421
.getCurrentWorkingDirectory();
416422
if (WorkingDir)
417423
Context.setCurrentBuildDirectory(WorkingDir.get());
418-
424+
custom::registerCustomChecks(Context.getOptions(), *CheckFactories);
419425
std::vector<std::unique_ptr<ClangTidyCheck>> Checks =
420426
CheckFactories->createChecksForLanguage(&Context);
421427

@@ -655,6 +661,7 @@ getAllChecksAndOptions(bool AllowEnablingAnalyzerAlphaCheckers) {
655661
std::make_unique<DefaultOptionsProvider>(ClangTidyGlobalOptions(), Opts),
656662
AllowEnablingAnalyzerAlphaCheckers);
657663
ClangTidyCheckFactories Factories;
664+
custom::registerCustomChecks(Context.getOptions(), Factories);
658665
for (const ClangTidyModuleRegistry::entry &Module :
659666
ClangTidyModuleRegistry::entries()) {
660667
Module.instantiate()->addCheckFactories(Factories);

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

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,11 @@ extern volatile int CppCoreGuidelinesModuleAnchorSource;
5454
static int LLVM_ATTRIBUTE_UNUSED CppCoreGuidelinesModuleAnchorDestination =
5555
CppCoreGuidelinesModuleAnchorSource;
5656

57+
// This anchor is used to force the linker to link the CustomModule.
58+
extern volatile int CustomModuleAnchorSource;
59+
static int LLVM_ATTRIBUTE_UNUSED CustomModuleAnchorDestination =
60+
CustomModuleAnchorSource;
61+
5762
// This anchor is used to force the linker to link the DarwinModule.
5863
extern volatile int DarwinModuleAnchorSource;
5964
static int LLVM_ATTRIBUTE_UNUSED DarwinModuleAnchorDestination =

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,8 @@ class ClangTidyCheckFactories {
6262
});
6363
}
6464

65+
void erase(llvm::StringRef CheckName) { Factories.erase(CheckName); }
66+
6567
/// Create instances of checks that are enabled.
6668
std::vector<std::unique_ptr<ClangTidyCheck>>
6769
createChecks(ClangTidyContext *Context) const;

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

Lines changed: 53 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,12 @@
88

99
#include "ClangTidyOptions.h"
1010
#include "ClangTidyModuleRegistry.h"
11+
#include "clang/Basic/DiagnosticIDs.h"
1112
#include "clang/Basic/LLVM.h"
1213
#include "llvm/ADT/SmallString.h"
14+
#include "llvm/ADT/StringExtras.h"
1315
#include "llvm/Support/Debug.h"
14-
#include "llvm/Support/Errc.h"
1516
#include "llvm/Support/ErrorOr.h"
16-
#include "llvm/Support/FileSystem.h"
1717
#include "llvm/Support/MemoryBufferRef.h"
1818
#include "llvm/Support/Path.h"
1919
#include "llvm/Support/YAMLTraits.h"
@@ -72,7 +72,8 @@ struct NOptionMap {
7272
NOptionMap(IO &, const ClangTidyOptions::OptionMap &OptionMap) {
7373
Options.reserve(OptionMap.size());
7474
for (const auto &KeyValue : OptionMap)
75-
Options.emplace_back(std::string(KeyValue.getKey()), KeyValue.getValue().Value);
75+
Options.emplace_back(std::string(KeyValue.getKey()),
76+
KeyValue.getValue().Value);
7677
}
7778
ClangTidyOptions::OptionMap denormalize(IO &) {
7879
ClangTidyOptions::OptionMap Map;
@@ -126,6 +127,52 @@ void yamlize(IO &IO, ClangTidyOptions::OptionMap &Val, bool,
126127
}
127128
}
128129

130+
namespace {
131+
struct MultiLineString {
132+
std::string &S;
133+
};
134+
} // namespace
135+
136+
template <> struct BlockScalarTraits<MultiLineString> {
137+
static void output(const MultiLineString &S, void *Ctxt, raw_ostream &OS) {
138+
OS << S.S;
139+
}
140+
static StringRef input(StringRef Str, void *Ctxt, MultiLineString &S) {
141+
S.S = Str;
142+
return "";
143+
}
144+
};
145+
146+
template <> struct ScalarEnumerationTraits<clang::DiagnosticIDs::Level> {
147+
static void enumeration(IO &IO, clang::DiagnosticIDs::Level &Level) {
148+
IO.enumCase(Level, "Error", clang::DiagnosticIDs::Level::Error);
149+
IO.enumCase(Level, "Warning", clang::DiagnosticIDs::Level::Warning);
150+
IO.enumCase(Level, "Note", clang::DiagnosticIDs::Level::Note);
151+
}
152+
};
153+
template <> struct SequenceElementTraits<ClangTidyOptions::CustomCheckDiag> {
154+
static const bool flow = false;
155+
};
156+
template <> struct MappingTraits<ClangTidyOptions::CustomCheckDiag> {
157+
static void mapping(IO &IO, ClangTidyOptions::CustomCheckDiag &D) {
158+
IO.mapRequired("BindName", D.BindName);
159+
MultiLineString MLS{D.Message};
160+
IO.mapRequired("Message", MLS);
161+
IO.mapOptional("Level", D.Level);
162+
}
163+
};
164+
template <> struct SequenceElementTraits<ClangTidyOptions::CustomCheckValue> {
165+
static const bool flow = false;
166+
};
167+
template <> struct MappingTraits<ClangTidyOptions::CustomCheckValue> {
168+
static void mapping(IO &IO, ClangTidyOptions::CustomCheckValue &V) {
169+
IO.mapRequired("Name", V.Name);
170+
MultiLineString MLS{V.Query};
171+
IO.mapRequired("Query", MLS);
172+
IO.mapRequired("Diagnostic", V.Diags);
173+
}
174+
};
175+
129176
struct ChecksVariant {
130177
std::optional<std::string> AsString;
131178
std::optional<std::vector<std::string>> AsVector;
@@ -181,6 +228,7 @@ template <> struct MappingTraits<ClangTidyOptions> {
181228
IO.mapOptional("InheritParentConfig", Options.InheritParentConfig);
182229
IO.mapOptional("UseColor", Options.UseColor);
183230
IO.mapOptional("SystemHeaders", Options.SystemHeaders);
231+
IO.mapOptional("CustomChecks", Options.CustomChecks);
184232
}
185233
};
186234

@@ -242,7 +290,8 @@ ClangTidyOptions &ClangTidyOptions::mergeWith(const ClangTidyOptions &Other,
242290
overrideValue(UseColor, Other.UseColor);
243291
mergeVectors(ExtraArgs, Other.ExtraArgs);
244292
mergeVectors(ExtraArgsBefore, Other.ExtraArgsBefore);
245-
293+
// FIXME: how to handle duplicate names check?
294+
mergeVectors(CustomChecks, Other.CustomChecks);
246295
for (const auto &KeyValue : Other.CheckOptions) {
247296
CheckOptions.insert_or_assign(
248297
KeyValue.getKey(),

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

Lines changed: 15 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/Basic/DiagnosticIDs.h"
1213
#include "llvm/ADT/IntrusiveRefCntPtr.h"
1314
#include "llvm/ADT/SmallString.h"
1415
#include "llvm/ADT/StringMap.h"
@@ -17,6 +18,7 @@
1718
#include "llvm/Support/MemoryBufferRef.h"
1819
#include "llvm/Support/VirtualFileSystem.h"
1920
#include <functional>
21+
#include <map>
2022
#include <optional>
2123
#include <string>
2224
#include <system_error>
@@ -129,6 +131,19 @@ struct ClangTidyOptions {
129131
/// Key-value mapping used to store check-specific options.
130132
OptionMap CheckOptions;
131133

134+
struct CustomCheckDiag {
135+
std::string BindName;
136+
std::string Message;
137+
std::optional<DiagnosticIDs::Level> Level;
138+
};
139+
struct CustomCheckValue {
140+
std::string Name;
141+
std::string Query;
142+
llvm::SmallVector<CustomCheckDiag> Diags;
143+
};
144+
using CustomCheckValueList = llvm::SmallVector<CustomCheckValue>;
145+
std::optional<CustomCheckValueList> CustomChecks;
146+
132147
using ArgList = std::vector<std::string>;
133148

134149
/// Add extra compilation arguments to the end of the list.
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
set(LLVM_LINK_COMPONENTS
2+
support
3+
)
4+
5+
add_clang_library(clangTidyCustomModule STATIC
6+
CustomTidyModule.cpp
7+
QueryCheck.cpp
8+
9+
LINK_LIBS
10+
clangTidy
11+
clangTidyUtils
12+
13+
DEPENDS
14+
ClangDriverOptions
15+
)
16+
17+
clang_target_link_libraries(clangTidyCustomModule
18+
PRIVATE
19+
clangQuery
20+
)
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
#include "../ClangTidy.h"
2+
#include "../ClangTidyModule.h"
3+
#include "../ClangTidyModuleRegistry.h"
4+
#include "../ClangTidyOptions.h"
5+
#include "QueryCheck.h"
6+
#include "llvm/ADT/SmallSet.h"
7+
#include "llvm/ADT/SmallString.h"
8+
#include "llvm/ADT/StringRef.h"
9+
#include <memory>
10+
11+
namespace clang::tidy {
12+
namespace custom {
13+
14+
class CustomModule : public ClangTidyModule {
15+
public:
16+
void addCheckFactories(ClangTidyCheckFactories &CheckFactories) override {}
17+
};
18+
19+
// We need to register the checks more flexibly than builtin modules. The checks
20+
// will changed dynamically when switching to different source file.
21+
extern void registerCustomChecks(ClangTidyOptions const &Options,
22+
ClangTidyCheckFactories &Factories) {
23+
static llvm::SmallSet<llvm::SmallString<32>, 8> CustomCheckNames{};
24+
if (!Options.CustomChecks.has_value() || Options.CustomChecks->empty())
25+
return;
26+
for (llvm::SmallString<32> const &Name : CustomCheckNames)
27+
Factories.erase(Name);
28+
for (const ClangTidyOptions::CustomCheckValue &V :
29+
Options.CustomChecks.value()) {
30+
llvm::SmallString<32> Name = llvm::StringRef{"custom-" + V.Name};
31+
Factories.registerCheckFactory(
32+
// add custom- prefix to avoid conflicts with builtin checks
33+
Name, [&V](llvm::StringRef Name, ClangTidyContext *Context) {
34+
return std::make_unique<custom::QueryCheck>(Name, V, Context);
35+
});
36+
CustomCheckNames.insert(std::move(Name));
37+
}
38+
}
39+
40+
} // namespace custom
41+
42+
// Register the CustomTidyModule using this statically initialized variable.
43+
static ClangTidyModuleRegistry::Add<custom::CustomModule>
44+
X("custom-module", "Adds custom query lint checks.");
45+
46+
// This anchor is used to force the linker to link in the generated object file
47+
// and thus register the AlteraModule.
48+
volatile int CustomModuleAnchorSource = 0;
49+
50+
} // namespace clang::tidy
Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
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"
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+
19+
using namespace clang::ast_matchers;
20+
21+
namespace clang::tidy::custom {
22+
23+
QueryCheck::QueryCheck(llvm::StringRef Name,
24+
const ClangTidyOptions::CustomCheckValue &V,
25+
ClangTidyContext *Context)
26+
: ClangTidyCheck(Name, Context) {
27+
for (const ClangTidyOptions::CustomCheckDiag &D : V.Diags) {
28+
auto DiagnosticIdIt =
29+
Diags
30+
.try_emplace(D.Level.value_or(DiagnosticIDs::Warning),
31+
llvm::StringMap<llvm::SmallVector<std::string>>{})
32+
.first;
33+
auto DiagMessageIt =
34+
DiagnosticIdIt->getSecond()
35+
.try_emplace(D.BindName, llvm::SmallVector<std::string>{})
36+
.first;
37+
DiagMessageIt->second.emplace_back(D.Message);
38+
}
39+
40+
clang::query::QuerySession QS({});
41+
llvm::StringRef QueryStringRef{V.Query};
42+
while (!QueryStringRef.empty()) {
43+
query::QueryRef Q = query::QueryParser::parse(QueryStringRef, QS);
44+
switch (Q->Kind) {
45+
case query::QK_Match: {
46+
const auto &MatchQuerry = llvm::cast<query::MatchQuery>(*Q);
47+
Matchers.push_back(MatchQuerry.Matcher);
48+
break;
49+
}
50+
case query::QK_Let: {
51+
const auto &LetQuerry = llvm::cast<query::LetQuery>(*Q);
52+
LetQuerry.run(llvm::errs(), QS);
53+
break;
54+
}
55+
case query::QK_Invalid: {
56+
const auto &InvalidQuerry = llvm::cast<query::InvalidQuery>(*Q);
57+
Context->configurationDiag(InvalidQuerry.ErrStr);
58+
break;
59+
}
60+
// FIXME: TODO
61+
case query::QK_File:
62+
case query::QK_DisableOutputKind:
63+
case query::QK_EnableOutputKind:
64+
case query::QK_SetOutputKind:
65+
case query::QK_SetTraversalKind:
66+
case query::QK_Help:
67+
case query::QK_NoOp:
68+
case query::QK_Quit:
69+
case query::QK_SetBool: {
70+
Context->configurationDiag("unsupported querry kind");
71+
}
72+
}
73+
QueryStringRef = Q->RemainingContent;
74+
}
75+
}
76+
77+
void QueryCheck::registerMatchers(MatchFinder *Finder) {
78+
for (const ast_matchers::dynamic::DynTypedMatcher &M : Matchers)
79+
Finder->addDynamicMatcher(M, this);
80+
}
81+
82+
void QueryCheck::check(const MatchFinder::MatchResult &Result) {
83+
auto Emit = [this](DiagMaps const &DiagMaps, std::string const &BindName,
84+
DynTypedNode const &Node, DiagnosticIDs::Level Level) {
85+
if (!DiagMaps.contains(Level))
86+
return;
87+
auto &DiagMap = DiagMaps.at(Level);
88+
if (!DiagMap.contains(BindName))
89+
return;
90+
for (const std::string &Message : DiagMap.at(BindName)) {
91+
diag(Node.getSourceRange().getBegin(), Message, Level);
92+
}
93+
};
94+
for (const auto &[Name, Node] : Result.Nodes.getMap())
95+
Emit(Diags, Name, Node, DiagnosticIDs::Error);
96+
for (const auto &[Name, Node] : Result.Nodes.getMap())
97+
Emit(Diags, Name, Node, DiagnosticIDs::Warning);
98+
// place Note last, otherwise it will not be emitted
99+
for (const auto &[Name, Node] : Result.Nodes.getMap())
100+
Emit(Diags, Name, Node, DiagnosticIDs::Note);
101+
}
102+
} // namespace clang::tidy::custom

0 commit comments

Comments
 (0)