10 #include "../clang-tidy/ClangTidyModuleRegistry.h"
16 #include "llvm/ADT/STLExtras.h"
17 #include "llvm/ADT/SmallString.h"
18 #include "llvm/ADT/StringExtras.h"
19 #include "llvm/ADT/StringSet.h"
20 #include "llvm/Support/Allocator.h"
21 #include "llvm/Support/Process.h"
22 #include "llvm/Support/SourceMgr.h"
30 class DotClangTidyCache :
private FileCache {
33 mutable std::shared_ptr<const tidy::ClangTidyOptions> Value;
38 std::shared_ptr<const tidy::ClangTidyOptions>
39 get(
const ThreadsafeFS &TFS,
40 std::chrono::steady_clock::time_point FreshTime)
const {
41 std::shared_ptr<const tidy::ClangTidyOptions> Result;
44 [
this](llvm::Optional<llvm::StringRef> Data) {
46 if (Data && !Data->empty()) {
47 tidy::DiagCallback Diagnostics = [](const llvm::SMDiagnostic &D) {
48 switch (D.getKind()) {
49 case llvm::SourceMgr::DK_Error:
50 elog(
"tidy-config error at {0}:{1}:{2}: {3}", D.getFilename(),
51 D.getLineNo(), D.getColumnNo(), D.getMessage());
53 case llvm::SourceMgr::DK_Warning:
54 log(
"tidy-config warning at {0}:{1}:{2}: {3}", D.getFilename(),
55 D.getLineNo(), D.getColumnNo(), D.getMessage());
57 case llvm::SourceMgr::DK_Note:
58 case llvm::SourceMgr::DK_Remark:
59 vlog(
"tidy-config note at {0}:{1}:{2}: {3}", D.getFilename(),
60 D.getLineNo(), D.getColumnNo(), D.getMessage());
66 Value = std::make_shared<const tidy::ClangTidyOptions>(
69 elog(
"Error parsing clang-tidy configuration in {0}: {1}", path(),
70 Parsed.getError().message());
73 [&]() { Result = Value; });
83 class DotClangTidyTree {
84 const ThreadsafeFS &FS;
86 std::chrono::steady_clock::duration MaxStaleness;
88 mutable std::mutex Mu;
92 mutable llvm::StringMap<DotClangTidyCache> Cache;
95 DotClangTidyTree(
const ThreadsafeFS &FS)
96 : FS(FS), RelPath(
".clang-tidy"), MaxStaleness(std::chrono::seconds(5)) {}
98 void apply(tidy::ClangTidyOptions &Result,
PathRef AbsPath) {
99 namespace path = llvm::sys::path;
100 assert(path::is_absolute(AbsPath));
104 llvm::SmallVector<DotClangTidyCache *> Caches;
106 std::lock_guard<std::mutex> Lock(Mu);
109 auto It = Cache.find(Ancestor);
111 if (It == Cache.end()) {
112 llvm::SmallString<256> ConfigPath = Ancestor;
113 path::append(ConfigPath, RelPath);
114 It = Cache.try_emplace(Ancestor, ConfigPath.str()).first;
116 Caches.push_back(&It->second);
121 std::chrono::steady_clock::time_point FreshTime =
122 std::chrono::steady_clock::now() - MaxStaleness;
123 llvm::SmallVector<std::shared_ptr<const tidy::ClangTidyOptions>>
125 for (
const DotClangTidyCache *Cache : Caches)
126 if (
auto Config = Cache->get(FS, FreshTime)) {
127 OptionStack.push_back(std::move(
Config));
128 if (!OptionStack.back()->InheritParentConfig.value_or(
false))
132 for (
auto &Option : llvm::reverse(OptionStack))
133 Result.mergeWith(*Option, Order++);
140 llvm::StringRef List) {
151 static const llvm::Optional<std::string> User = [] {
152 llvm::Optional<std::string> Ret = llvm::sys::Process::GetEnv(
"USER");
155 return llvm::sys::Process::GetEnv(
"USERNAME");
174 ",",
"readability-misleading-indentation",
"readability-deleted-default",
175 "bugprone-integer-division",
"bugprone-sizeof-expression",
176 "bugprone-suspicious-missing-comma",
"bugprone-unused-raii",
177 "bugprone-unused-return-value",
"misc-unused-using-decls",
178 "misc-unused-alias-decls",
"misc-definitions-in-headers");
180 if (!Opts.Checks || Opts.Checks->empty())
196 constexpr llvm::StringLiteral Seperator(
",");
197 static const std::string BadChecks =
198 llvm::join_items(Seperator,
207 "-llvm-header-guard",
214 "-bugprone-use-after-move",
216 "-hicpp-invalid-access-moved");
218 size_t Size = BadChecks.size();
219 for (
const std::string &Str : ExtraBadChecks) {
222 Size += Seperator.size();
223 if (LLVM_LIKELY(Str.front() !=
'-'))
227 std::string DisableGlob;
228 DisableGlob.reserve(Size);
229 DisableGlob += BadChecks;
230 for (
const std::string &Str : ExtraBadChecks) {
233 DisableGlob += Seperator;
234 if (LLVM_LIKELY(Str.front() !=
'-'))
235 DisableGlob.push_back(
'-');
241 if (Opts.Checks && !Opts.Checks->empty())
242 Opts.Checks->append(DisableList);
248 const auto &CurTidyConfig = Config::current().Diagnostics.ClangTidy;
249 if (!CurTidyConfig.Checks.empty())
252 for (
const auto &CheckOption : CurTidyConfig.CheckOptions)
253 Opts.CheckOptions.insert_or_assign(CheckOption.getKey(),
255 CheckOption.getValue(), 10000U));
260 return [Tree = std::make_unique<DotClangTidyTree>(TFS)](
272 for (
const auto &Provider : Providers)
280 Opts.Checks->clear();
287 assert(!Check.empty());
288 assert(!Check.contains(
'*') && !Check.contains(
',') &&
289 "isRegisteredCheck doesn't support globs");
290 assert(Check.ltrim().front() !=
'-');
292 static const llvm::StringSet<llvm::BumpPtrAllocator> AllChecks = [] {
293 llvm::StringSet<llvm::BumpPtrAllocator> Result;
295 for (tidy::ClangTidyModuleRegistry::entry
E :
296 tidy::ClangTidyModuleRegistry::entries())
297 E.instantiate()->addCheckFactories(Factories);
298 for (
const auto &Factory : Factories)
299 Result.insert(Factory.getKey());
303 return AllChecks.contains(Check);