clang-tools 18.0.0git
Check.cpp
Go to the documentation of this file.
1//===--- Check.cpp - clangd self-diagnostics ------------------------------===//
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// Many basic problems can occur processing a file in clangd, e.g.:
10// - system includes are not found
11// - crash when indexing its AST
12// clangd --check provides a simplified, isolated way to reproduce these,
13// with no editor, LSP, threads, background indexing etc to contend with.
14//
15// One important use case is gathering information for bug reports.
16// Another is reproducing crashes, and checking which setting prevent them.
17//
18// It simulates opening a file (determining compile command, parsing, indexing)
19// and then running features at many locations.
20//
21// Currently it adds some basic logging of progress and results.
22// We should consider extending it to also recognize common symptoms and
23// recommend solutions (e.g. standard library installation issues).
24//
25//===----------------------------------------------------------------------===//
26
27#include "../clang-tidy/ClangTidyModule.h"
28#include "../clang-tidy/ClangTidyModuleRegistry.h"
29#include "../clang-tidy/ClangTidyOptions.h"
30#include "../clang-tidy/GlobList.h"
31#include "ClangdLSPServer.h"
32#include "ClangdServer.h"
33#include "CodeComplete.h"
34#include "CompileCommands.h"
35#include "Compiler.h"
36#include "Config.h"
37#include "Diagnostics.h"
38#include "Feature.h"
40#include "Hover.h"
41#include "InlayHints.h"
42#include "ParsedAST.h"
43#include "Preamble.h"
44#include "Protocol.h"
45#include "Selection.h"
47#include "SourceCode.h"
48#include "TidyProvider.h"
49#include "XRefs.h"
50#include "clang-include-cleaner/Record.h"
51#include "index/FileIndex.h"
52#include "refactor/Tweak.h"
53#include "support/Context.h"
54#include "support/Logger.h"
56#include "support/Trace.h"
57#include "clang/AST/ASTContext.h"
58#include "clang/Basic/Diagnostic.h"
59#include "clang/Basic/LLVM.h"
60#include "clang/Format/Format.h"
61#include "clang/Frontend/CompilerInvocation.h"
62#include "clang/Tooling/CompilationDatabase.h"
63#include "llvm/ADT/ArrayRef.h"
64#include "llvm/ADT/STLExtras.h"
65#include "llvm/ADT/SmallString.h"
66#include "llvm/Support/Chrono.h"
67#include "llvm/Support/CommandLine.h"
68#include "llvm/Support/Path.h"
69#include "llvm/Support/Process.h"
70#include <array>
71#include <chrono>
72#include <cstdint>
73#include <limits>
74#include <memory>
75#include <optional>
76#include <utility>
77#include <vector>
78
79namespace clang {
80namespace clangd {
81namespace {
82
83// These will never be shown in --help, ClangdMain doesn't list the category.
84llvm::cl::opt<std::string> CheckTidyTime{
85 "check-tidy-time",
86 llvm::cl::desc("Print the overhead of checks matching this glob"),
87 llvm::cl::init("")};
88llvm::cl::opt<std::string> CheckFileLines{
89 "check-lines",
90 llvm::cl::desc(
91 "Limits the range of tokens in -check file on which "
92 "various features are tested. Example --check-lines=3-7 restricts "
93 "testing to lines 3 to 7 (inclusive) or --check-lines=5 to restrict "
94 "to one line. Default is testing entire file."),
95 llvm::cl::init("")};
96llvm::cl::opt<bool> CheckLocations{
97 "check-locations",
98 llvm::cl::desc(
99 "Runs certain features (e.g. hover) at each point in the file. "
100 "Somewhat slow."),
101 llvm::cl::init(true)};
102llvm::cl::opt<bool> CheckCompletion{
103 "check-completion",
104 llvm::cl::desc("Run code-completion at each point (slow)"),
105 llvm::cl::init(false)};
106
107// Print (and count) the error-level diagnostics (warnings are ignored).
108unsigned showErrors(llvm::ArrayRef<Diag> Diags) {
109 unsigned ErrCount = 0;
110 for (const auto &D : Diags) {
111 if (D.Severity >= DiagnosticsEngine::Error) {
112 elog("[{0}] Line {1}: {2}", D.Name, D.Range.start.line + 1, D.Message);
113 ++ErrCount;
114 }
115 }
116 return ErrCount;
117}
118
119std::vector<std::string> listTidyChecks(llvm::StringRef Glob) {
120 tidy::GlobList G(Glob);
121 tidy::ClangTidyCheckFactories CTFactories;
122 for (const auto &E : tidy::ClangTidyModuleRegistry::entries())
123 E.instantiate()->addCheckFactories(CTFactories);
124 std::vector<std::string> Result;
125 for (const auto &E : CTFactories)
126 if (G.contains(E.getKey()))
127 Result.push_back(E.getKey().str());
128 llvm::sort(Result);
129 return Result;
130}
131
132// This class is just a linear pipeline whose functions get called in sequence.
133// Each exercises part of clangd's logic on our test file and logs results.
134// Later steps depend on state built in earlier ones (such as the AST).
135// Many steps can fatally fail (return false), then subsequent ones cannot run.
136// Nonfatal failures are logged and tracked in ErrCount.
137class Checker {
138 // from constructor
139 std::string File;
140 ClangdLSPServer::Options Opts;
141 // from buildCommand
142 tooling::CompileCommand Cmd;
143 // from buildInvocation
144 ParseInputs Inputs;
145 std::unique_ptr<CompilerInvocation> Invocation;
146 format::FormatStyle Style;
147 // from buildAST
148 std::shared_ptr<const PreambleData> Preamble;
149 std::optional<ParsedAST> AST;
150 FileIndex Index;
151
152public:
153 // Number of non-fatal errors seen.
154 unsigned ErrCount = 0;
155
156 Checker(llvm::StringRef File, const ClangdLSPServer::Options &Opts)
157 : File(File), Opts(Opts) {}
158
159 // Read compilation database and choose a compile command for the file.
160 bool buildCommand(const ThreadsafeFS &TFS) {
161 log("Loading compilation database...");
162 DirectoryBasedGlobalCompilationDatabase::Options CDBOpts(TFS);
163 CDBOpts.CompileCommandsDir =
165 std::unique_ptr<GlobalCompilationDatabase> BaseCDB =
166 std::make_unique<DirectoryBasedGlobalCompilationDatabase>(CDBOpts);
167 auto Mangler = CommandMangler::detect();
168 Mangler.SystemIncludeExtractor =
169 getSystemIncludeExtractor(llvm::ArrayRef(Opts.QueryDriverGlobs));
170 if (Opts.ResourceDir)
171 Mangler.ResourceDir = *Opts.ResourceDir;
172 auto CDB = std::make_unique<OverlayCDB>(
173 BaseCDB.get(), std::vector<std::string>{}, std::move(Mangler));
174
175 if (auto TrueCmd = CDB->getCompileCommand(File)) {
176 Cmd = std::move(*TrueCmd);
177 log("Compile command {0} is: [{1}] {2}",
178 Cmd.Heuristic.empty() ? "from CDB" : Cmd.Heuristic, Cmd.Directory,
179 printArgv(Cmd.CommandLine));
180 } else {
181 Cmd = CDB->getFallbackCommand(File);
182 log("Generic fallback command is: [{0}] {1}", Cmd.Directory,
183 printArgv(Cmd.CommandLine));
184 }
185
186 return true;
187 }
188
189 // Prepare inputs and build CompilerInvocation (parsed compile command).
190 bool buildInvocation(const ThreadsafeFS &TFS,
191 std::optional<std::string> Contents) {
192 StoreDiags CaptureInvocationDiags;
193 std::vector<std::string> CC1Args;
194 Inputs.CompileCommand = Cmd;
195 Inputs.TFS = &TFS;
196 Inputs.ClangTidyProvider = Opts.ClangTidyProvider;
197 Inputs.Opts.PreambleParseForwardingFunctions =
198 Opts.PreambleParseForwardingFunctions;
199 if (Contents) {
200 Inputs.Contents = *Contents;
201 log("Imaginary source file contents:\n{0}", Inputs.Contents);
202 } else {
203 if (auto Contents = TFS.view(std::nullopt)->getBufferForFile(File)) {
204 Inputs.Contents = Contents->get()->getBuffer().str();
205 } else {
206 elog("Couldn't read {0}: {1}", File, Contents.getError().message());
207 return false;
208 }
209 }
210 log("Parsing command...");
211 Invocation =
212 buildCompilerInvocation(Inputs, CaptureInvocationDiags, &CC1Args);
213 auto InvocationDiags = CaptureInvocationDiags.take();
214 ErrCount += showErrors(InvocationDiags);
215 log("internal (cc1) args are: {0}", printArgv(CC1Args));
216 if (!Invocation) {
217 elog("Failed to parse command line");
218 return false;
219 }
220
221 // FIXME: Check that resource-dir/built-in-headers exist?
222
223 Style = getFormatStyleForFile(File, Inputs.Contents, TFS);
224
225 return true;
226 }
227
228 // Build preamble and AST, and index them.
229 bool buildAST() {
230 log("Building preamble...");
232 File, *Invocation, Inputs, /*StoreInMemory=*/true,
233 [&](CapturedASTCtx Ctx,
234 std::shared_ptr<const include_cleaner::PragmaIncludes> PI) {
235 if (!Opts.BuildDynamicSymbolIndex)
236 return;
237 log("Indexing headers...");
238 Index.updatePreamble(File, /*Version=*/"null", Ctx.getASTContext(),
239 Ctx.getPreprocessor(), *PI);
240 });
241 if (!Preamble) {
242 elog("Failed to build preamble");
243 return false;
244 }
245 ErrCount += showErrors(Preamble->Diags);
246
247 log("Building AST...");
248 AST = ParsedAST::build(File, Inputs, std::move(Invocation),
249 /*InvocationDiags=*/std::vector<Diag>{}, Preamble);
250 if (!AST) {
251 elog("Failed to build AST");
252 return false;
253 }
254 ErrCount +=
255 showErrors(AST->getDiagnostics().drop_front(Preamble->Diags.size()));
256
257 if (Opts.BuildDynamicSymbolIndex) {
258 log("Indexing AST...");
259 Index.updateMain(File, *AST);
260 }
261
262 if (!CheckTidyTime.empty()) {
263 if (!CLANGD_TIDY_CHECKS) {
264 elog("-{0} requires -DCLANGD_TIDY_CHECKS!", CheckTidyTime.ArgStr);
265 return false;
266 }
267 #ifndef NDEBUG
268 elog("Timing clang-tidy checks in asserts-mode is not representative!");
269 #endif
270 checkTidyTimes();
271 }
272
273 return true;
274 }
275
276 // For each check foo, we want to build with checks=-* and checks=-*,foo.
277 // (We do a full build rather than just AST matchers to meausre PPCallbacks).
278 //
279 // However, performance has both random noise and systematic changes, such as
280 // step-function slowdowns due to CPU scaling.
281 // We take the median of 5 measurements, and after every check discard the
282 // measurement if the baseline changed by >3%.
283 void checkTidyTimes() {
284 double Stability = 0.03;
285 log("Timing AST build with individual clang-tidy checks (target accuracy "
286 "{0:P0})",
287 Stability);
288
289 using Duration = std::chrono::nanoseconds;
290 // Measure time elapsed by a block of code. Currently: user CPU time.
291 auto Time = [&](auto &&Run) -> Duration {
292 llvm::sys::TimePoint<> Elapsed;
293 std::chrono::nanoseconds UserBegin, UserEnd, System;
294 llvm::sys::Process::GetTimeUsage(Elapsed, UserBegin, System);
295 Run();
296 llvm::sys::Process::GetTimeUsage(Elapsed, UserEnd, System);
297 return UserEnd - UserBegin;
298 };
299 auto Change = [&](Duration Exp, Duration Base) -> double {
300 return (double)(Exp.count() - Base.count()) / Base.count();
301 };
302 // Build ParsedAST with a fixed check glob, and return the time taken.
303 auto Build = [&](llvm::StringRef Checks) -> Duration {
304 TidyProvider CTProvider = [&](tidy::ClangTidyOptions &Opts,
305 llvm::StringRef) {
306 Opts.Checks = Checks.str();
307 };
308 Inputs.ClangTidyProvider = CTProvider;
309 // Sigh, can't reuse the CompilerInvocation.
310 IgnoringDiagConsumer IgnoreDiags;
311 auto Invocation = buildCompilerInvocation(Inputs, IgnoreDiags);
312 Duration Val = Time([&] {
313 ParsedAST::build(File, Inputs, std::move(Invocation), {}, Preamble);
314 });
315 vlog(" Measured {0} ==> {1}", Checks, Val);
316 return Val;
317 };
318 // Measure several times, return the median.
319 auto MedianTime = [&](llvm::StringRef Checks) -> Duration {
320 std::array<Duration, 5> Measurements;
321 for (auto &M : Measurements)
322 M = Build(Checks);
323 llvm::sort(Measurements);
324 return Measurements[Measurements.size() / 2];
325 };
326 Duration Baseline = MedianTime("-*");
327 log(" Baseline = {0}", Baseline);
328 // Attempt to time a check, may update Baseline if it is unstable.
329 auto Measure = [&](llvm::StringRef Check) -> double {
330 for (;;) {
331 Duration Median = MedianTime(("-*," + Check).str());
332 Duration NewBase = MedianTime("-*");
333
334 // Value only usable if baseline is fairly consistent before/after.
335 double DeltaFraction = Change(NewBase, Baseline);
336 Baseline = NewBase;
337 vlog(" Baseline = {0}", Baseline);
338 if (DeltaFraction < -Stability || DeltaFraction > Stability) {
339 elog(" Speed unstable, discarding measurement.");
340 continue;
341 }
342 return Change(Median, Baseline);
343 }
344 };
345
346 for (const auto& Check : listTidyChecks(CheckTidyTime)) {
347 // vlog the check name in case we crash!
348 vlog(" Timing {0}", Check);
349 double Fraction = Measure(Check);
350 log(" {0} = {1:P0}", Check, Fraction);
351 }
352 log("Finished individual clang-tidy checks");
353
354 // Restore old options.
355 Inputs.ClangTidyProvider = Opts.ClangTidyProvider;
356 }
357
358 // Build Inlay Hints for the entire AST or the specified range
359 void buildInlayHints(std::optional<Range> LineRange) {
360 log("Building inlay hints");
361 auto Hints = inlayHints(*AST, LineRange);
362
363 for (const auto &Hint : Hints) {
364 vlog(" {0} {1} {2}", Hint.kind, Hint.position, Hint.label);
365 }
366 }
367
368 void buildSemanticHighlighting(std::optional<Range> LineRange) {
369 log("Building semantic highlighting");
370 auto Highlights =
371 getSemanticHighlightings(*AST, /*IncludeInactiveRegionTokens=*/true);
372 for (const auto HL : Highlights)
373 if (!LineRange || LineRange->contains(HL.R))
374 vlog(" {0} {1} {2}", HL.R, HL.Kind, HL.Modifiers);
375 }
376
377 // Run AST-based features at each token in the file.
378 void testLocationFeatures(std::optional<Range> LineRange) {
379 trace::Span Trace("testLocationFeatures");
380 log("Testing features at each token (may be slow in large files)");
381 auto &SM = AST->getSourceManager();
382 auto SpelledTokens = AST->getTokens().spelledTokens(SM.getMainFileID());
383
384 CodeCompleteOptions CCOpts = Opts.CodeComplete;
385 CCOpts.Index = &Index;
386
387 for (const auto &Tok : SpelledTokens) {
388 unsigned Start = AST->getSourceManager().getFileOffset(Tok.location());
389 unsigned End = Start + Tok.length();
390 Position Pos = offsetToPosition(Inputs.Contents, Start);
391
392 if (LineRange && !LineRange->contains(Pos))
393 continue;
394
395 trace::Span Trace("Token");
396 SPAN_ATTACH(Trace, "pos", Pos);
397 SPAN_ATTACH(Trace, "text", Tok.text(AST->getSourceManager()));
398
399 // FIXME: dumping the tokens may leak sensitive code into bug reports.
400 // Add an option to turn this off, once we decide how options work.
401 vlog(" {0} {1}", Pos, Tok.text(AST->getSourceManager()));
402 auto Tree = SelectionTree::createRight(AST->getASTContext(),
403 AST->getTokens(), Start, End);
404 Tweak::Selection Selection(&Index, *AST, Start, End, std::move(Tree),
405 nullptr);
406 // FS is only populated when applying a tweak, not during prepare as
407 // prepare should not do any I/O to be fast.
408 auto Tweaks =
409 prepareTweaks(Selection, Opts.TweakFilter, Opts.FeatureModules);
410 Selection.FS =
411 &AST->getSourceManager().getFileManager().getVirtualFileSystem();
412 for (const auto &T : Tweaks) {
413 auto Result = T->apply(Selection);
414 if (!Result) {
415 elog(" tweak: {0} ==> FAIL: {1}", T->id(), Result.takeError());
416 ++ErrCount;
417 } else {
418 vlog(" tweak: {0}", T->id());
419 }
420 }
421 unsigned Definitions = locateSymbolAt(*AST, Pos, &Index).size();
422 vlog(" definition: {0}", Definitions);
423
424 auto Hover = getHover(*AST, Pos, Style, &Index);
425 vlog(" hover: {0}", Hover.has_value());
426
427 unsigned DocHighlights = findDocumentHighlights(*AST, Pos).size();
428 vlog(" documentHighlight: {0}", DocHighlights);
429
430 if (CheckCompletion) {
431 Position EndPos = offsetToPosition(Inputs.Contents, End);
432 auto CC = codeComplete(File, EndPos, Preamble.get(), Inputs, CCOpts);
433 vlog(" code completion: {0}",
434 CC.Completions.empty() ? "<empty>" : CC.Completions[0].Name);
435 }
436 }
437 }
438};
439
440} // namespace
441
442bool check(llvm::StringRef File, const ThreadsafeFS &TFS,
443 const ClangdLSPServer::Options &Opts) {
444 std::optional<Range> LineRange;
445 if (!CheckFileLines.empty()) {
446 uint32_t Begin = 0, End = std::numeric_limits<uint32_t>::max();
447 StringRef RangeStr(CheckFileLines);
448 bool ParseError = RangeStr.consumeInteger(0, Begin);
449 if (RangeStr.empty()) {
450 End = Begin;
451 } else {
452 ParseError |= !RangeStr.consume_front("-");
453 ParseError |= RangeStr.consumeInteger(0, End);
454 }
455 if (ParseError || !RangeStr.empty() || Begin <= 0 || End < Begin) {
456 elog("Invalid --check-lines specified. Use Begin-End format, e.g. 3-17");
457 return false;
458 }
459 LineRange = Range{Position{static_cast<int>(Begin - 1), 0},
460 Position{static_cast<int>(End), 0}};
461 }
462
463 llvm::SmallString<0> FakeFile;
464 std::optional<std::string> Contents;
465 if (File.empty()) {
466 llvm::sys::path::system_temp_directory(false, FakeFile);
467 llvm::sys::path::append(FakeFile, "test.cc");
468 File = FakeFile;
469 Contents = R"cpp(
470 #include <stddef.h>
471 #include <string>
472
473 size_t N = 50;
474 auto xxx = std::string(N, 'x');
475 )cpp";
476 }
477 log("Testing on source file {0}", File);
478
480 Opts.ConfigProvider, nullptr);
481 WithContext Ctx(ContextProvider(
482 FakeFile.empty()
483 ? File
484 : /*Don't turn on local configs for an arbitrary temp path.*/ ""));
485 Checker C(File, Opts);
486 if (!C.buildCommand(TFS) || !C.buildInvocation(TFS, Contents) ||
487 !C.buildAST())
488 return false;
489 C.buildInlayHints(LineRange);
490 C.buildSemanticHighlighting(LineRange);
491 if (CheckLocations)
492 C.testLocationFeatures(LineRange);
493
494 log("All checks completed, {0} errors", C.ErrCount);
495 return C.ErrCount == 0;
496}
497
498} // namespace clangd
499} // namespace clang
const Expr * E
unsigned ErrCount
Definition: Check.cpp:154
const PreambleData & Preamble
const Criteria C
CognitiveComplexity CC
IgnoringDiagConsumer IgnoreDiags
size_t Pos
std::vector< FixItHint > Hints
const google::protobuf::Message & M
Definition: Server.cpp:309
#define SPAN_ATTACH(S, Name, Expr)
Attach a key-value pair to a Span event.
Definition: Trace.h:164
static std::function< Context(PathRef)> createConfiguredContextProvider(const config::Provider *Provider, ClangdServer::Callbacks *)
Creates a context provider that loads and installs config.
static std::optional< ParsedAST > build(llvm::StringRef Filename, const ParseInputs &Inputs, std::unique_ptr< clang::CompilerInvocation > CI, llvm::ArrayRef< Diag > CompilerInvocationDiags, std::shared_ptr< const PreambleData > Preamble)
Attempts to run Clang and store the parsed AST.
Definition: ParsedAST.cpp:387
static SelectionTree createRight(ASTContext &AST, const syntax::TokenBuffer &Tokens, unsigned Begin, unsigned End)
Definition: Selection.cpp:1067
Wrapper for vfs::FileSystem for use in multithreaded programs like clangd.
Definition: ThreadsafeFS.h:26
WithContext replaces Context::current() with a provided scope.
Definition: Context.h:185
std::vector< HighlightingToken > getSemanticHighlightings(ParsedAST &AST, bool IncludeInactiveRegionTokens)
SystemIncludeExtractorFn getSystemIncludeExtractor(llvm::ArrayRef< std::string > QueryDriverGlobs)
Position offsetToPosition(llvm::StringRef Code, size_t Offset)
Turn an offset in Code into a [line, column] pair.
Definition: SourceCode.cpp:202
std::vector< DocumentHighlight > findDocumentHighlights(ParsedAST &AST, Position Pos)
Returns highlights for all usages of a symbol at Pos.
Definition: XRefs.cpp:1231
std::unique_ptr< CompilerInvocation > buildCompilerInvocation(const ParseInputs &Inputs, clang::DiagnosticConsumer &D, std::vector< std::string > *CC1Args)
Builds compiler invocation that could be used to build AST or preamble.
Definition: Compiler.cpp:95
format::FormatStyle getFormatStyleForFile(llvm::StringRef File, llvm::StringRef Content, const ThreadsafeFS &TFS)
Choose the clang-format style we should apply to a certain file.
Definition: SourceCode.cpp:579
void vlog(const char *Fmt, Ts &&... Vals)
Definition: Logger.h:72
std::optional< HoverInfo > getHover(ParsedAST &AST, Position Pos, const format::FormatStyle &Style, const SymbolIndex *Index)
Get the hover information when hovering at Pos.
Definition: Hover.cpp:1273
std::vector< LocatedSymbol > locateSymbolAt(ParsedAST &AST, Position Pos, const SymbolIndex *Index)
Get definition of symbol at a specified Pos.
Definition: XRefs.cpp:760
std::shared_ptr< const PreambleData > buildPreamble(PathRef FileName, CompilerInvocation CI, const ParseInputs &Inputs, bool StoreInMemory, PreambleParsedCallback PreambleCallback, PreambleBuildStats *Stats)
Build a preamble for the new inputs unless an old one can be reused.
Definition: Preamble.cpp:591
void log(const char *Fmt, Ts &&... Vals)
Definition: Logger.h:67
std::string printArgv(llvm::ArrayRef< llvm::StringRef > Args)
std::vector< std::unique_ptr< Tweak > > prepareTweaks(const Tweak::Selection &S, llvm::function_ref< bool(const Tweak &)> Filter, const FeatureModuleSet *Modules)
Calls prepare() on all tweaks that satisfy the filter, returning those that can run on the selection.
Definition: Tweak.cpp:72
std::vector< InlayHint > inlayHints(ParsedAST &AST, std::optional< Range > RestrictRange)
Compute and return inlay hints for a file.
llvm::unique_function< void(tidy::ClangTidyOptions &, llvm::StringRef) const > TidyProvider
A factory to modify a tidy::ClangTidyOptions.
Definition: TidyProvider.h:23
CodeCompleteResult codeComplete(PathRef FileName, Position Pos, const PreambleData *Preamble, const ParseInputs &ParseInput, CodeCompleteOptions Opts, SpeculativeFuzzyFind *SpecFuzzyFind)
Gets code completions at a specified Pos in FileName.
bool check(llvm::StringRef File, const ThreadsafeFS &TFS, const ClangdLSPServer::Options &Opts)
Definition: Check.cpp:442
void elog(const char *Fmt, Ts &&... Vals)
Definition: Logger.h:61
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//
static CommandMangler detect()
std::optional< std::string > FixedCDBPath
Definition: Config.h:59
static const Config & current()
Returns the Config of the current Context, or an empty configuration.
Definition: Config.cpp:17
CDBSearchSpec CDBSearch
Where to search for compilation databases for this file's flags.
Definition: Config.h:68
struct clang::clangd::Config::@2 CompileFlags
Controls how the compile command for the current file is determined.