Mercurial > hg > CbC > CbC_llvm
comparison clang-tools-extra/clangd/TidyProvider.cpp @ 221:79ff65ed7e25
LLVM12 Original
author | Shinji KONO <kono@ie.u-ryukyu.ac.jp> |
---|---|
date | Tue, 15 Jun 2021 19:15:29 +0900 |
parents | |
children | c4bab56944e8 |
comparison
equal
deleted
inserted
replaced
220:42394fc6a535 | 221:79ff65ed7e25 |
---|---|
1 //===--- TidyProvider.cpp - create options for running 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 "TidyProvider.h" | |
10 #include "../clang-tidy/ClangTidyModuleRegistry.h" | |
11 #include "Config.h" | |
12 #include "support/FileCache.h" | |
13 #include "support/Logger.h" | |
14 #include "support/Path.h" | |
15 #include "support/ThreadsafeFS.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/ErrorHandling.h" | |
22 #include "llvm/Support/Process.h" | |
23 #include "llvm/Support/SourceMgr.h" | |
24 #include "llvm/Support/VirtualFileSystem.h" | |
25 #include <memory> | |
26 | |
27 namespace clang { | |
28 namespace clangd { | |
29 namespace { | |
30 | |
31 // Access to config from a .clang-tidy file, caching IO and parsing. | |
32 class DotClangTidyCache : private FileCache { | |
33 // We cache and expose shared_ptr to avoid copying the value on every lookup | |
34 // when we're ultimately just going to pass it to mergeWith. | |
35 mutable std::shared_ptr<const tidy::ClangTidyOptions> Value; | |
36 | |
37 public: | |
38 DotClangTidyCache(PathRef Path) : FileCache(Path) {} | |
39 | |
40 std::shared_ptr<const tidy::ClangTidyOptions> | |
41 get(const ThreadsafeFS &TFS, | |
42 std::chrono::steady_clock::time_point FreshTime) const { | |
43 std::shared_ptr<const tidy::ClangTidyOptions> Result; | |
44 read( | |
45 TFS, FreshTime, | |
46 [this](llvm::Optional<llvm::StringRef> Data) { | |
47 Value.reset(); | |
48 if (Data && !Data->empty()) { | |
49 tidy::DiagCallback Diagnostics = [](const llvm::SMDiagnostic &D) { | |
50 switch (D.getKind()) { | |
51 case llvm::SourceMgr::DK_Error: | |
52 elog("tidy-config error at {0}:{1}:{2}: {3}", D.getFilename(), | |
53 D.getLineNo(), D.getColumnNo(), D.getMessage()); | |
54 break; | |
55 case llvm::SourceMgr::DK_Warning: | |
56 log("tidy-config warning at {0}:{1}:{2}: {3}", D.getFilename(), | |
57 D.getLineNo(), D.getColumnNo(), D.getMessage()); | |
58 break; | |
59 case llvm::SourceMgr::DK_Note: | |
60 case llvm::SourceMgr::DK_Remark: | |
61 vlog("tidy-config note at {0}:{1}:{2}: {3}", D.getFilename(), | |
62 D.getLineNo(), D.getColumnNo(), D.getMessage()); | |
63 break; | |
64 } | |
65 }; | |
66 if (auto Parsed = tidy::parseConfigurationWithDiags( | |
67 llvm::MemoryBufferRef(*Data, path()), Diagnostics)) | |
68 Value = std::make_shared<const tidy::ClangTidyOptions>( | |
69 std::move(*Parsed)); | |
70 else | |
71 elog("Error parsing clang-tidy configuration in {0}: {1}", path(), | |
72 Parsed.getError().message()); | |
73 } | |
74 }, | |
75 [&]() { Result = Value; }); | |
76 return Result; | |
77 } | |
78 }; | |
79 | |
80 // Access to combined config from .clang-tidy files governing a source file. | |
81 // Each config file is cached and the caches are shared for affected sources. | |
82 // | |
83 // FIXME: largely duplicates config::Provider::fromAncestorRelativeYAMLFiles. | |
84 // Potentially useful for compile_commands.json too. Extract? | |
85 class DotClangTidyTree { | |
86 const ThreadsafeFS &FS; | |
87 std::string RelPath; | |
88 std::chrono::steady_clock::duration MaxStaleness; | |
89 | |
90 mutable std::mutex Mu; | |
91 // Keys are the ancestor directory, not the actual config path within it. | |
92 // We only insert into this map, so pointers to values are stable forever. | |
93 // Mutex guards the map itself, not the values (which are threadsafe). | |
94 mutable llvm::StringMap<DotClangTidyCache> Cache; | |
95 | |
96 public: | |
97 DotClangTidyTree(const ThreadsafeFS &FS) | |
98 : FS(FS), RelPath(".clang-tidy"), MaxStaleness(std::chrono::seconds(5)) {} | |
99 | |
100 void apply(tidy::ClangTidyOptions &Result, PathRef AbsPath) { | |
101 namespace path = llvm::sys::path; | |
102 assert(path::is_absolute(AbsPath)); | |
103 | |
104 // Compute absolute paths to all ancestors (substrings of P.Path). | |
105 // Ensure cache entries for each ancestor exist in the map. | |
106 llvm::SmallVector<DotClangTidyCache *> Caches; | |
107 { | |
108 std::lock_guard<std::mutex> Lock(Mu); | |
109 for (auto Ancestor = absoluteParent(AbsPath); !Ancestor.empty(); | |
110 Ancestor = absoluteParent(Ancestor)) { | |
111 auto It = Cache.find(Ancestor); | |
112 // Assemble the actual config file path only if needed. | |
113 if (It == Cache.end()) { | |
114 llvm::SmallString<256> ConfigPath = Ancestor; | |
115 path::append(ConfigPath, RelPath); | |
116 It = Cache.try_emplace(Ancestor, ConfigPath.str()).first; | |
117 } | |
118 Caches.push_back(&It->second); | |
119 } | |
120 } | |
121 // Finally query each individual file. | |
122 // This will take a (per-file) lock for each file that actually exists. | |
123 std::chrono::steady_clock::time_point FreshTime = | |
124 std::chrono::steady_clock::now() - MaxStaleness; | |
125 llvm::SmallVector<std::shared_ptr<const tidy::ClangTidyOptions>> | |
126 OptionStack; | |
127 for (const DotClangTidyCache *Cache : Caches) | |
128 if (auto Config = Cache->get(FS, FreshTime)) { | |
129 OptionStack.push_back(std::move(Config)); | |
130 if (!OptionStack.back()->InheritParentConfig.getValueOr(false)) | |
131 break; | |
132 } | |
133 unsigned Order = 1u; | |
134 for (auto &Option : llvm::reverse(OptionStack)) | |
135 Result.mergeWith(*Option, Order++); | |
136 } | |
137 }; | |
138 | |
139 } // namespace | |
140 | |
141 static void mergeCheckList(llvm::Optional<std::string> &Checks, | |
142 llvm::StringRef List) { | |
143 if (List.empty()) | |
144 return; | |
145 if (!Checks || Checks->empty()) { | |
146 Checks.emplace(List); | |
147 return; | |
148 } | |
149 *Checks = llvm::join_items(",", *Checks, List); | |
150 } | |
151 | |
152 TidyProviderRef provideEnvironment() { | |
153 static const llvm::Optional<std::string> User = [] { | |
154 llvm::Optional<std::string> Ret = llvm::sys::Process::GetEnv("USER"); | |
155 #ifdef _WIN32 | |
156 if (!Ret) | |
157 return llvm::sys::Process::GetEnv("USERNAME"); | |
158 #endif | |
159 return Ret; | |
160 }(); | |
161 | |
162 if (User) | |
163 return | |
164 [](tidy::ClangTidyOptions &Opts, llvm::StringRef) { Opts.User = User; }; | |
165 // FIXME: Once function_ref and unique_function operator= operators handle | |
166 // null values, this can return null. | |
167 return [](tidy::ClangTidyOptions &, llvm::StringRef) {}; | |
168 } | |
169 | |
170 TidyProviderRef provideDefaultChecks() { | |
171 // These default checks are chosen for: | |
172 // - low false-positive rate | |
173 // - providing a lot of value | |
174 // - being reasonably efficient | |
175 static const std::string DefaultChecks = llvm::join_items( | |
176 ",", "readability-misleading-indentation", "readability-deleted-default", | |
177 "bugprone-integer-division", "bugprone-sizeof-expression", | |
178 "bugprone-suspicious-missing-comma", "bugprone-unused-raii", | |
179 "bugprone-unused-return-value", "misc-unused-using-decls", | |
180 "misc-unused-alias-decls", "misc-definitions-in-headers"); | |
181 return [](tidy::ClangTidyOptions &Opts, llvm::StringRef) { | |
182 if (!Opts.Checks || Opts.Checks->empty()) | |
183 Opts.Checks = DefaultChecks; | |
184 }; | |
185 } | |
186 | |
187 TidyProvider addTidyChecks(llvm::StringRef Checks, | |
188 llvm::StringRef WarningsAsErrors) { | |
189 return [Checks = std::string(Checks), | |
190 WarningsAsErrors = std::string(WarningsAsErrors)]( | |
191 tidy::ClangTidyOptions &Opts, llvm::StringRef) { | |
192 mergeCheckList(Opts.Checks, Checks); | |
193 mergeCheckList(Opts.WarningsAsErrors, WarningsAsErrors); | |
194 }; | |
195 } | |
196 | |
197 TidyProvider disableUnusableChecks(llvm::ArrayRef<std::string> ExtraBadChecks) { | |
198 constexpr llvm::StringLiteral Seperator(","); | |
199 static const std::string BadChecks = | |
200 llvm::join_items(Seperator, | |
201 // We want this list to start with a seperator to | |
202 // simplify appending in the lambda. So including an | |
203 // empty string here will force that. | |
204 "", | |
205 // ----- False Positives ----- | |
206 | |
207 // Check relies on seeing ifndef/define/endif directives, | |
208 // clangd doesn't replay those when using a preamble. | |
209 "-llvm-header-guard", | |
210 | |
211 // ----- Crashing Checks ----- | |
212 | |
213 // Check can choke on invalid (intermediate) c++ | |
214 // code, which is often the case when clangd | |
215 // tries to build an AST. | |
216 "-bugprone-use-after-move"); | |
217 | |
218 size_t Size = BadChecks.size(); | |
219 for (const std::string &Str : ExtraBadChecks) { | |
220 if (Str.empty()) | |
221 continue; | |
222 Size += Seperator.size(); | |
223 if (LLVM_LIKELY(Str.front() != '-')) | |
224 ++Size; | |
225 Size += Str.size(); | |
226 } | |
227 std::string DisableGlob; | |
228 DisableGlob.reserve(Size); | |
229 DisableGlob += BadChecks; | |
230 for (const std::string &Str : ExtraBadChecks) { | |
231 if (Str.empty()) | |
232 continue; | |
233 DisableGlob += Seperator; | |
234 if (LLVM_LIKELY(Str.front() != '-')) | |
235 DisableGlob.push_back('-'); | |
236 DisableGlob += Str; | |
237 } | |
238 | |
239 return [DisableList(std::move(DisableGlob))](tidy::ClangTidyOptions &Opts, | |
240 llvm::StringRef) { | |
241 if (Opts.Checks && !Opts.Checks->empty()) | |
242 Opts.Checks->append(DisableList); | |
243 }; | |
244 } | |
245 | |
246 TidyProviderRef provideClangdConfig() { | |
247 return [](tidy::ClangTidyOptions &Opts, llvm::StringRef) { | |
248 const auto &CurTidyConfig = Config::current().Diagnostics.ClangTidy; | |
249 if (!CurTidyConfig.Checks.empty()) | |
250 mergeCheckList(Opts.Checks, CurTidyConfig.Checks); | |
251 | |
252 for (const auto &CheckOption : CurTidyConfig.CheckOptions) | |
253 Opts.CheckOptions.insert_or_assign(CheckOption.getKey(), | |
254 tidy::ClangTidyOptions::ClangTidyValue( | |
255 CheckOption.getValue(), 10000U)); | |
256 }; | |
257 } | |
258 | |
259 TidyProvider provideClangTidyFiles(ThreadsafeFS &TFS) { | |
260 return [Tree = std::make_unique<DotClangTidyTree>(TFS)]( | |
261 tidy::ClangTidyOptions &Opts, llvm::StringRef Filename) { | |
262 Tree->apply(Opts, Filename); | |
263 }; | |
264 } | |
265 | |
266 TidyProvider combine(std::vector<TidyProvider> Providers) { | |
267 // FIXME: Once function_ref and unique_function operator= operators handle | |
268 // null values, we should filter out any Providers that are null. Right now we | |
269 // have to ensure we dont pass any providers that are null. | |
270 return [Providers(std::move(Providers))](tidy::ClangTidyOptions &Opts, | |
271 llvm::StringRef Filename) { | |
272 for (const auto &Provider : Providers) | |
273 Provider(Opts, Filename); | |
274 }; | |
275 } | |
276 | |
277 tidy::ClangTidyOptions getTidyOptionsForFile(TidyProviderRef Provider, | |
278 llvm::StringRef Filename) { | |
279 tidy::ClangTidyOptions Opts = tidy::ClangTidyOptions::getDefaults(); | |
280 Opts.Checks->clear(); | |
281 if (Provider) | |
282 Provider(Opts, Filename); | |
283 return Opts; | |
284 } | |
285 | |
286 bool isRegisteredTidyCheck(llvm::StringRef Check) { | |
287 assert(!Check.empty()); | |
288 assert(!Check.contains('*') && !Check.contains(',') && | |
289 "isRegisteredCheck doesn't support globs"); | |
290 assert(Check.ltrim().front() != '-'); | |
291 | |
292 static const llvm::StringSet<llvm::BumpPtrAllocator> AllChecks = [] { | |
293 llvm::StringSet<llvm::BumpPtrAllocator> Result; | |
294 tidy::ClangTidyCheckFactories Factories; | |
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()); | |
300 return Result; | |
301 }(); | |
302 | |
303 return AllChecks.contains(Check); | |
304 } | |
305 } // namespace clangd | |
306 } // namespace clang |