Mercurial > hg > CbC > CbC_llvm
comparison clang-tools-extra/clangd/ConfigCompile.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 | 5f17cb93ff66 |
comparison
equal
deleted
inserted
replaced
220:42394fc6a535 | 221:79ff65ed7e25 |
---|---|
1 //===--- ConfigCompile.cpp - Translating Fragments into Config ------------===// | |
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 // Fragments are applied to Configs in two steps: | |
10 // | |
11 // 1. (When the fragment is first loaded) | |
12 // FragmentCompiler::compile() traverses the Fragment and creates | |
13 // function objects that know how to apply the configuration. | |
14 // 2. (Every time a config is required) | |
15 // CompiledFragment() executes these functions to populate the Config. | |
16 // | |
17 // Work could be split between these steps in different ways. We try to | |
18 // do as much work as possible in the first step. For example, regexes are | |
19 // compiled in stage 1 and captured by the apply function. This is because: | |
20 // | |
21 // - it's more efficient, as the work done in stage 1 must only be done once | |
22 // - problems can be reported in stage 1, in stage 2 we must silently recover | |
23 // | |
24 //===----------------------------------------------------------------------===// | |
25 | |
26 #include "CompileCommands.h" | |
27 #include "Config.h" | |
28 #include "ConfigFragment.h" | |
29 #include "ConfigProvider.h" | |
30 #include "Diagnostics.h" | |
31 #include "Features.inc" | |
32 #include "TidyProvider.h" | |
33 #include "support/Logger.h" | |
34 #include "support/Path.h" | |
35 #include "support/Trace.h" | |
36 #include "llvm/ADT/None.h" | |
37 #include "llvm/ADT/Optional.h" | |
38 #include "llvm/ADT/STLExtras.h" | |
39 #include "llvm/ADT/SmallString.h" | |
40 #include "llvm/ADT/StringRef.h" | |
41 #include "llvm/ADT/StringSwitch.h" | |
42 #include "llvm/Support/Error.h" | |
43 #include "llvm/Support/FileSystem.h" | |
44 #include "llvm/Support/Format.h" | |
45 #include "llvm/Support/FormatVariadic.h" | |
46 #include "llvm/Support/Path.h" | |
47 #include "llvm/Support/Regex.h" | |
48 #include "llvm/Support/SMLoc.h" | |
49 #include "llvm/Support/SourceMgr.h" | |
50 #include <string> | |
51 | |
52 namespace clang { | |
53 namespace clangd { | |
54 namespace config { | |
55 namespace { | |
56 | |
57 // Returns an empty stringref if Path is not under FragmentDir. Returns Path | |
58 // as-is when FragmentDir is empty. | |
59 llvm::StringRef configRelative(llvm::StringRef Path, | |
60 llvm::StringRef FragmentDir) { | |
61 if (FragmentDir.empty()) | |
62 return Path; | |
63 if (!Path.consume_front(FragmentDir)) | |
64 return llvm::StringRef(); | |
65 return Path.empty() ? "." : Path; | |
66 } | |
67 | |
68 struct CompiledFragmentImpl { | |
69 // The independent conditions to check before using settings from this config. | |
70 // The following fragment has *two* conditions: | |
71 // If: { Platform: [mac, linux], PathMatch: foo/.* } | |
72 // All of them must be satisfied: the platform and path conditions are ANDed. | |
73 // The OR logic for the platform condition is implemented inside the function. | |
74 std::vector<llvm::unique_function<bool(const Params &) const>> Conditions; | |
75 // Mutations that this fragment will apply to the configuration. | |
76 // These are invoked only if the conditions are satisfied. | |
77 std::vector<llvm::unique_function<void(const Params &, Config &) const>> | |
78 Apply; | |
79 | |
80 bool operator()(const Params &P, Config &C) const { | |
81 for (const auto &C : Conditions) { | |
82 if (!C(P)) { | |
83 dlog("Config fragment {0}: condition not met", this); | |
84 return false; | |
85 } | |
86 } | |
87 dlog("Config fragment {0}: applying {1} rules", this, Apply.size()); | |
88 for (const auto &A : Apply) | |
89 A(P, C); | |
90 return true; | |
91 } | |
92 }; | |
93 | |
94 // Wrapper around condition compile() functions to reduce arg-passing. | |
95 struct FragmentCompiler { | |
96 FragmentCompiler(CompiledFragmentImpl &Out, DiagnosticCallback D, | |
97 llvm::SourceMgr *SM) | |
98 : Out(Out), Diagnostic(D), SourceMgr(SM) {} | |
99 CompiledFragmentImpl &Out; | |
100 DiagnosticCallback Diagnostic; | |
101 llvm::SourceMgr *SourceMgr; | |
102 // Normalized Fragment::SourceInfo::Directory. | |
103 std::string FragmentDirectory; | |
104 bool Trusted = false; | |
105 | |
106 llvm::Optional<llvm::Regex> | |
107 compileRegex(const Located<std::string> &Text, | |
108 llvm::Regex::RegexFlags Flags = llvm::Regex::NoFlags) { | |
109 std::string Anchored = "^(" + *Text + ")$"; | |
110 llvm::Regex Result(Anchored, Flags); | |
111 std::string RegexError; | |
112 if (!Result.isValid(RegexError)) { | |
113 diag(Error, "Invalid regex " + Anchored + ": " + RegexError, Text.Range); | |
114 return llvm::None; | |
115 } | |
116 return Result; | |
117 } | |
118 | |
119 llvm::Optional<std::string> makeAbsolute(Located<std::string> Path, | |
120 llvm::StringLiteral Description, | |
121 llvm::sys::path::Style Style) { | |
122 if (llvm::sys::path::is_absolute(*Path)) | |
123 return *Path; | |
124 if (FragmentDirectory.empty()) { | |
125 diag(Error, | |
126 llvm::formatv( | |
127 "{0} must be an absolute path, because this fragment is not " | |
128 "associated with any directory.", | |
129 Description) | |
130 .str(), | |
131 Path.Range); | |
132 return llvm::None; | |
133 } | |
134 llvm::SmallString<256> AbsPath = llvm::StringRef(*Path); | |
135 llvm::sys::fs::make_absolute(FragmentDirectory, AbsPath); | |
136 llvm::sys::path::native(AbsPath, Style); | |
137 return AbsPath.str().str(); | |
138 } | |
139 | |
140 // Helper with similar API to StringSwitch, for parsing enum values. | |
141 template <typename T> class EnumSwitch { | |
142 FragmentCompiler &Outer; | |
143 llvm::StringRef EnumName; | |
144 const Located<std::string> &Input; | |
145 llvm::Optional<T> Result; | |
146 llvm::SmallVector<llvm::StringLiteral> ValidValues; | |
147 | |
148 public: | |
149 EnumSwitch(llvm::StringRef EnumName, const Located<std::string> &In, | |
150 FragmentCompiler &Outer) | |
151 : Outer(Outer), EnumName(EnumName), Input(In) {} | |
152 | |
153 EnumSwitch &map(llvm::StringLiteral Name, T Value) { | |
154 assert(!llvm::is_contained(ValidValues, Name) && "Duplicate value!"); | |
155 ValidValues.push_back(Name); | |
156 if (!Result && *Input == Name) | |
157 Result = Value; | |
158 return *this; | |
159 } | |
160 | |
161 llvm::Optional<T> value() { | |
162 if (!Result) | |
163 Outer.diag( | |
164 Warning, | |
165 llvm::formatv("Invalid {0} value '{1}'. Valid values are {2}.", | |
166 EnumName, *Input, llvm::join(ValidValues, ", ")) | |
167 .str(), | |
168 Input.Range); | |
169 return Result; | |
170 }; | |
171 }; | |
172 | |
173 // Attempt to parse a specified string into an enum. | |
174 // Yields llvm::None and produces a diagnostic on failure. | |
175 // | |
176 // Optional<T> Value = compileEnum<En>("Foo", Frag.Foo) | |
177 // .map("Foo", Enum::Foo) | |
178 // .map("Bar", Enum::Bar) | |
179 // .value(); | |
180 template <typename T> | |
181 EnumSwitch<T> compileEnum(llvm::StringRef EnumName, | |
182 const Located<std::string> &In) { | |
183 return EnumSwitch<T>(EnumName, In, *this); | |
184 } | |
185 | |
186 void compile(Fragment &&F) { | |
187 Trusted = F.Source.Trusted; | |
188 if (!F.Source.Directory.empty()) { | |
189 FragmentDirectory = llvm::sys::path::convert_to_slash(F.Source.Directory); | |
190 if (FragmentDirectory.back() != '/') | |
191 FragmentDirectory += '/'; | |
192 } | |
193 compile(std::move(F.If)); | |
194 compile(std::move(F.CompileFlags)); | |
195 compile(std::move(F.Index)); | |
196 compile(std::move(F.Diagnostics)); | |
197 compile(std::move(F.Completion)); | |
198 } | |
199 | |
200 void compile(Fragment::IfBlock &&F) { | |
201 if (F.HasUnrecognizedCondition) | |
202 Out.Conditions.push_back([&](const Params &) { return false; }); | |
203 | |
204 #ifdef CLANGD_PATH_CASE_INSENSITIVE | |
205 llvm::Regex::RegexFlags Flags = llvm::Regex::IgnoreCase; | |
206 #else | |
207 llvm::Regex::RegexFlags Flags = llvm::Regex::NoFlags; | |
208 #endif | |
209 | |
210 auto PathMatch = std::make_unique<std::vector<llvm::Regex>>(); | |
211 for (auto &Entry : F.PathMatch) { | |
212 if (auto RE = compileRegex(Entry, Flags)) | |
213 PathMatch->push_back(std::move(*RE)); | |
214 } | |
215 if (!PathMatch->empty()) { | |
216 Out.Conditions.push_back( | |
217 [PathMatch(std::move(PathMatch)), | |
218 FragmentDir(FragmentDirectory)](const Params &P) { | |
219 if (P.Path.empty()) | |
220 return false; | |
221 llvm::StringRef Path = configRelative(P.Path, FragmentDir); | |
222 // Ignore the file if it is not nested under Fragment. | |
223 if (Path.empty()) | |
224 return false; | |
225 return llvm::any_of(*PathMatch, [&](const llvm::Regex &RE) { | |
226 return RE.match(Path); | |
227 }); | |
228 }); | |
229 } | |
230 | |
231 auto PathExclude = std::make_unique<std::vector<llvm::Regex>>(); | |
232 for (auto &Entry : F.PathExclude) { | |
233 if (auto RE = compileRegex(Entry, Flags)) | |
234 PathExclude->push_back(std::move(*RE)); | |
235 } | |
236 if (!PathExclude->empty()) { | |
237 Out.Conditions.push_back( | |
238 [PathExclude(std::move(PathExclude)), | |
239 FragmentDir(FragmentDirectory)](const Params &P) { | |
240 if (P.Path.empty()) | |
241 return false; | |
242 llvm::StringRef Path = configRelative(P.Path, FragmentDir); | |
243 // Ignore the file if it is not nested under Fragment. | |
244 if (Path.empty()) | |
245 return true; | |
246 return llvm::none_of(*PathExclude, [&](const llvm::Regex &RE) { | |
247 return RE.match(Path); | |
248 }); | |
249 }); | |
250 } | |
251 } | |
252 | |
253 void compile(Fragment::CompileFlagsBlock &&F) { | |
254 if (!F.Remove.empty()) { | |
255 auto Remove = std::make_shared<ArgStripper>(); | |
256 for (auto &A : F.Remove) | |
257 Remove->strip(*A); | |
258 Out.Apply.push_back([Remove(std::shared_ptr<const ArgStripper>( | |
259 std::move(Remove)))](const Params &, Config &C) { | |
260 C.CompileFlags.Edits.push_back( | |
261 [Remove](std::vector<std::string> &Args) { | |
262 Remove->process(Args); | |
263 }); | |
264 }); | |
265 } | |
266 | |
267 if (!F.Add.empty()) { | |
268 std::vector<std::string> Add; | |
269 for (auto &A : F.Add) | |
270 Add.push_back(std::move(*A)); | |
271 Out.Apply.push_back([Add(std::move(Add))](const Params &, Config &C) { | |
272 C.CompileFlags.Edits.push_back([Add](std::vector<std::string> &Args) { | |
273 Args.insert(Args.end(), Add.begin(), Add.end()); | |
274 }); | |
275 }); | |
276 } | |
277 | |
278 if (F.CompilationDatabase) { | |
279 llvm::Optional<Config::CDBSearchSpec> Spec; | |
280 if (**F.CompilationDatabase == "Ancestors") { | |
281 Spec.emplace(); | |
282 Spec->Policy = Config::CDBSearchSpec::Ancestors; | |
283 } else if (**F.CompilationDatabase == "None") { | |
284 Spec.emplace(); | |
285 Spec->Policy = Config::CDBSearchSpec::NoCDBSearch; | |
286 } else { | |
287 if (auto Path = | |
288 makeAbsolute(*F.CompilationDatabase, "CompilationDatabase", | |
289 llvm::sys::path::Style::native)) { | |
290 // Drop trailing slash to put the path in canonical form. | |
291 // Should makeAbsolute do this? | |
292 llvm::StringRef Rel = llvm::sys::path::relative_path(*Path); | |
293 if (!Rel.empty() && llvm::sys::path::is_separator(Rel.back())) | |
294 Path->pop_back(); | |
295 | |
296 Spec.emplace(); | |
297 Spec->Policy = Config::CDBSearchSpec::FixedDir; | |
298 Spec->FixedCDBPath = std::move(Path); | |
299 } | |
300 } | |
301 if (Spec) | |
302 Out.Apply.push_back( | |
303 [Spec(std::move(*Spec))](const Params &, Config &C) { | |
304 C.CompileFlags.CDBSearch = Spec; | |
305 }); | |
306 } | |
307 } | |
308 | |
309 void compile(Fragment::IndexBlock &&F) { | |
310 if (F.Background) { | |
311 if (auto Val = compileEnum<Config::BackgroundPolicy>("Background", | |
312 **F.Background) | |
313 .map("Build", Config::BackgroundPolicy::Build) | |
314 .map("Skip", Config::BackgroundPolicy::Skip) | |
315 .value()) | |
316 Out.Apply.push_back( | |
317 [Val](const Params &, Config &C) { C.Index.Background = *Val; }); | |
318 } | |
319 if (F.External) | |
320 compile(std::move(**F.External), F.External->Range); | |
321 } | |
322 | |
323 void compile(Fragment::IndexBlock::ExternalBlock &&External, | |
324 llvm::SMRange BlockRange) { | |
325 if (External.Server && !Trusted) { | |
326 diag(Error, | |
327 "Remote index may not be specified by untrusted configuration. " | |
328 "Copy this into user config to use it.", | |
329 External.Server->Range); | |
330 return; | |
331 } | |
332 #ifndef CLANGD_ENABLE_REMOTE | |
333 if (External.Server) { | |
334 elog("Clangd isn't compiled with remote index support, ignoring Server: " | |
335 "{0}", | |
336 *External.Server); | |
337 External.Server.reset(); | |
338 } | |
339 #endif | |
340 // Make sure exactly one of the Sources is set. | |
341 unsigned SourceCount = External.File.hasValue() + | |
342 External.Server.hasValue() + *External.IsNone; | |
343 if (SourceCount != 1) { | |
344 diag(Error, "Exactly one of File, Server or None must be set.", | |
345 BlockRange); | |
346 return; | |
347 } | |
348 Config::ExternalIndexSpec Spec; | |
349 if (External.Server) { | |
350 Spec.Kind = Config::ExternalIndexSpec::Server; | |
351 Spec.Location = std::move(**External.Server); | |
352 } else if (External.File) { | |
353 Spec.Kind = Config::ExternalIndexSpec::File; | |
354 auto AbsPath = makeAbsolute(std::move(*External.File), "File", | |
355 llvm::sys::path::Style::native); | |
356 if (!AbsPath) | |
357 return; | |
358 Spec.Location = std::move(*AbsPath); | |
359 } else { | |
360 assert(*External.IsNone); | |
361 Spec.Kind = Config::ExternalIndexSpec::None; | |
362 } | |
363 if (Spec.Kind != Config::ExternalIndexSpec::None) { | |
364 // Make sure MountPoint is an absolute path with forward slashes. | |
365 if (!External.MountPoint) | |
366 External.MountPoint.emplace(FragmentDirectory); | |
367 if ((**External.MountPoint).empty()) { | |
368 diag(Error, "A mountpoint is required.", BlockRange); | |
369 return; | |
370 } | |
371 auto AbsPath = makeAbsolute(std::move(*External.MountPoint), "MountPoint", | |
372 llvm::sys::path::Style::posix); | |
373 if (!AbsPath) | |
374 return; | |
375 Spec.MountPoint = std::move(*AbsPath); | |
376 } | |
377 Out.Apply.push_back([Spec(std::move(Spec))](const Params &P, Config &C) { | |
378 if (Spec.Kind == Config::ExternalIndexSpec::None) { | |
379 C.Index.External = Spec; | |
380 return; | |
381 } | |
382 if (P.Path.empty() || !pathStartsWith(Spec.MountPoint, P.Path, | |
383 llvm::sys::path::Style::posix)) | |
384 return; | |
385 C.Index.External = Spec; | |
386 // Disable background indexing for the files under the mountpoint. | |
387 // Note that this will overwrite statements in any previous fragments | |
388 // (including the current one). | |
389 C.Index.Background = Config::BackgroundPolicy::Skip; | |
390 }); | |
391 } | |
392 | |
393 void compile(Fragment::DiagnosticsBlock &&F) { | |
394 std::vector<std::string> Normalized; | |
395 for (const auto &Suppressed : F.Suppress) { | |
396 if (*Suppressed == "*") { | |
397 Out.Apply.push_back([&](const Params &, Config &C) { | |
398 C.Diagnostics.SuppressAll = true; | |
399 C.Diagnostics.Suppress.clear(); | |
400 }); | |
401 return; | |
402 } | |
403 Normalized.push_back(normalizeSuppressedCode(*Suppressed).str()); | |
404 } | |
405 if (!Normalized.empty()) | |
406 Out.Apply.push_back( | |
407 [Normalized(std::move(Normalized))](const Params &, Config &C) { | |
408 if (C.Diagnostics.SuppressAll) | |
409 return; | |
410 for (llvm::StringRef N : Normalized) | |
411 C.Diagnostics.Suppress.insert(N); | |
412 }); | |
413 | |
414 compile(std::move(F.ClangTidy)); | |
415 } | |
416 | |
417 void compile(Fragment::StyleBlock &&F) { | |
418 if (!F.FullyQualifiedNamespaces.empty()) { | |
419 std::vector<std::string> FullyQualifiedNamespaces; | |
420 for (auto &N : F.FullyQualifiedNamespaces) { | |
421 // Normalize the data by dropping both leading and trailing :: | |
422 StringRef Namespace(*N); | |
423 Namespace.consume_front("::"); | |
424 Namespace.consume_back("::"); | |
425 FullyQualifiedNamespaces.push_back(Namespace.str()); | |
426 } | |
427 Out.Apply.push_back([FullyQualifiedNamespaces( | |
428 std::move(FullyQualifiedNamespaces))]( | |
429 const Params &, Config &C) { | |
430 C.Style.FullyQualifiedNamespaces.insert( | |
431 C.Style.FullyQualifiedNamespaces.begin(), | |
432 FullyQualifiedNamespaces.begin(), FullyQualifiedNamespaces.end()); | |
433 }); | |
434 } | |
435 } | |
436 | |
437 void appendTidyCheckSpec(std::string &CurSpec, | |
438 const Located<std::string> &Arg, bool IsPositive) { | |
439 StringRef Str = StringRef(*Arg).trim(); | |
440 // Don't support negating here, its handled if the item is in the Add or | |
441 // Remove list. | |
442 if (Str.startswith("-") || Str.contains(',')) { | |
443 diag(Error, "Invalid clang-tidy check name", Arg.Range); | |
444 return; | |
445 } | |
446 if (!Str.contains('*') && !isRegisteredTidyCheck(Str)) { | |
447 diag(Warning, | |
448 llvm::formatv("clang-tidy check '{0}' was not found", Str).str(), | |
449 Arg.Range); | |
450 return; | |
451 } | |
452 CurSpec += ','; | |
453 if (!IsPositive) | |
454 CurSpec += '-'; | |
455 CurSpec += Str; | |
456 } | |
457 | |
458 void compile(Fragment::DiagnosticsBlock::ClangTidyBlock &&F) { | |
459 std::string Checks; | |
460 for (auto &CheckGlob : F.Add) | |
461 appendTidyCheckSpec(Checks, CheckGlob, true); | |
462 | |
463 for (auto &CheckGlob : F.Remove) | |
464 appendTidyCheckSpec(Checks, CheckGlob, false); | |
465 | |
466 if (!Checks.empty()) | |
467 Out.Apply.push_back( | |
468 [Checks = std::move(Checks)](const Params &, Config &C) { | |
469 C.Diagnostics.ClangTidy.Checks.append( | |
470 Checks, | |
471 C.Diagnostics.ClangTidy.Checks.empty() ? /*skip comma*/ 1 : 0, | |
472 std::string::npos); | |
473 }); | |
474 if (!F.CheckOptions.empty()) { | |
475 std::vector<std::pair<std::string, std::string>> CheckOptions; | |
476 for (auto &Opt : F.CheckOptions) | |
477 CheckOptions.emplace_back(std::move(*Opt.first), | |
478 std::move(*Opt.second)); | |
479 Out.Apply.push_back( | |
480 [CheckOptions = std::move(CheckOptions)](const Params &, Config &C) { | |
481 for (auto &StringPair : CheckOptions) | |
482 C.Diagnostics.ClangTidy.CheckOptions.insert_or_assign( | |
483 StringPair.first, StringPair.second); | |
484 }); | |
485 } | |
486 } | |
487 | |
488 void compile(Fragment::CompletionBlock &&F) { | |
489 if (F.AllScopes) { | |
490 Out.Apply.push_back( | |
491 [AllScopes(**F.AllScopes)](const Params &, Config &C) { | |
492 C.Completion.AllScopes = AllScopes; | |
493 }); | |
494 } | |
495 } | |
496 | |
497 constexpr static llvm::SourceMgr::DiagKind Error = llvm::SourceMgr::DK_Error; | |
498 constexpr static llvm::SourceMgr::DiagKind Warning = | |
499 llvm::SourceMgr::DK_Warning; | |
500 void diag(llvm::SourceMgr::DiagKind Kind, llvm::StringRef Message, | |
501 llvm::SMRange Range) { | |
502 if (Range.isValid() && SourceMgr != nullptr) | |
503 Diagnostic(SourceMgr->GetMessage(Range.Start, Kind, Message, Range)); | |
504 else | |
505 Diagnostic(llvm::SMDiagnostic("", Kind, Message)); | |
506 } | |
507 }; | |
508 | |
509 } // namespace | |
510 | |
511 CompiledFragment Fragment::compile(DiagnosticCallback D) && { | |
512 llvm::StringRef ConfigFile = "<unknown>"; | |
513 std::pair<unsigned, unsigned> LineCol = {0, 0}; | |
514 if (auto *SM = Source.Manager.get()) { | |
515 unsigned BufID = SM->getMainFileID(); | |
516 LineCol = SM->getLineAndColumn(Source.Location, BufID); | |
517 ConfigFile = SM->getBufferInfo(BufID).Buffer->getBufferIdentifier(); | |
518 } | |
519 trace::Span Tracer("ConfigCompile"); | |
520 SPAN_ATTACH(Tracer, "ConfigFile", ConfigFile); | |
521 auto Result = std::make_shared<CompiledFragmentImpl>(); | |
522 vlog("Config fragment: compiling {0}:{1} -> {2} (trusted={3})", ConfigFile, | |
523 LineCol.first, Result.get(), Source.Trusted); | |
524 | |
525 FragmentCompiler{*Result, D, Source.Manager.get()}.compile(std::move(*this)); | |
526 // Return as cheaply-copyable wrapper. | |
527 return [Result(std::move(Result))](const Params &P, Config &C) { | |
528 return (*Result)(P, C); | |
529 }; | |
530 } | |
531 | |
532 } // namespace config | |
533 } // namespace clangd | |
534 } // namespace clang |