Bug Summary

File:build/source/clang-tools-extra/clangd/tool/Check.cpp
Warning:line 293, column 7
Address of stack memory associated with local variable 'CTProvider' is still referred to by the stack variable 'C' upon returning to the caller. This will be a dangling reference

Annotated Source Code

Press '?' to see keyboard shortcuts

clang -cc1 -cc1 -triple x86_64-pc-linux-gnu -analyze -disable-free -clear-ast-before-backend -disable-llvm-verifier -discard-value-names -main-file-name Check.cpp -analyzer-checker=core -analyzer-checker=apiModeling -analyzer-checker=unix -analyzer-checker=deadcode -analyzer-checker=cplusplus -analyzer-checker=security.insecureAPI.UncheckedReturn -analyzer-checker=security.insecureAPI.getpw -analyzer-checker=security.insecureAPI.gets -analyzer-checker=security.insecureAPI.mktemp -analyzer-checker=security.insecureAPI.mkstemp -analyzer-checker=security.insecureAPI.vfork -analyzer-checker=nullability.NullPassedToNonnull -analyzer-checker=nullability.NullReturnedFromNonnull -analyzer-output plist -w -setup-static-analyzer -analyzer-config-compatibility-mode=true -mrelocation-model pic -pic-level 2 -mframe-pointer=none -relaxed-aliasing -fmath-errno -ffp-contract=on -fno-rounding-math -mconstructor-aliases -funwind-tables=2 -target-cpu x86-64 -tune-cpu generic -debugger-tuning=gdb -ffunction-sections -fdata-sections -fcoverage-compilation-dir=/build/source/build-llvm/tools/clang/stage2-bins -resource-dir /usr/lib/llvm-17/lib/clang/17 -D CLANG_REPOSITORY_STRING="++20230510111145+7df43bdb42ae-1~exp1~20230510111303.1288" -D _DEBUG -D _GLIBCXX_ASSERTIONS -D _GNU_SOURCE -D _LIBCPP_ENABLE_ASSERTIONS -D __STDC_CONSTANT_MACROS -D __STDC_FORMAT_MACROS -D __STDC_LIMIT_MACROS -I tools/clang/tools/extra/clangd/tool -I /build/source/clang-tools-extra/clangd/tool -I /build/source/clang-tools-extra/clangd/../include-cleaner/include -I tools/clang/tools/extra/clangd/../clang-tidy -I /build/source/clang/include -I tools/clang/include -I include -I /build/source/llvm/include -I /build/source/clang-tools-extra/clangd -I tools/clang/tools/extra/clangd -D _FORTIFY_SOURCE=2 -D NDEBUG -U NDEBUG -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/10/../../../../include/c++/10 -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/10/../../../../include/x86_64-linux-gnu/c++/10 -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/10/../../../../include/c++/10/backward -internal-isystem /usr/lib/llvm-17/lib/clang/17/include -internal-isystem /usr/local/include -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/10/../../../../x86_64-linux-gnu/include -internal-externc-isystem /usr/include/x86_64-linux-gnu -internal-externc-isystem /include -internal-externc-isystem /usr/include -fmacro-prefix-map=/build/source/build-llvm/tools/clang/stage2-bins=build-llvm/tools/clang/stage2-bins -fmacro-prefix-map=/build/source/= -fcoverage-prefix-map=/build/source/build-llvm/tools/clang/stage2-bins=build-llvm/tools/clang/stage2-bins -fcoverage-prefix-map=/build/source/= -source-date-epoch 1683717183 -O2 -Wno-unused-command-line-argument -Wno-unused-parameter -Wwrite-strings -Wno-missing-field-initializers -Wno-long-long -Wno-maybe-uninitialized -Wno-class-memaccess -Wno-redundant-move -Wno-pessimizing-move -Wno-noexcept-type -Wno-comment -Wno-misleading-indentation -std=c++17 -fdeprecated-macro -fdebug-compilation-dir=/build/source/build-llvm/tools/clang/stage2-bins -fdebug-prefix-map=/build/source/build-llvm/tools/clang/stage2-bins=build-llvm/tools/clang/stage2-bins -fdebug-prefix-map=/build/source/= -ferror-limit 19 -fvisibility-inlines-hidden -stack-protector 2 -fgnuc-version=4.2.1 -fcolor-diagnostics -vectorize-loops -vectorize-slp -analyzer-output=html -analyzer-config stable-report-filename=true -faddrsig -D__GCC_HAVE_DWARF2_CFI_ASM=1 -o /tmp/scan-build-2023-05-10-133810-16478-1 -x c++ /build/source/clang-tools-extra/clangd/tool/Check.cpp
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/ClangTidyModuleRegistry.h"
28#include "../clang-tidy/GlobList.h"
29#include "ClangdLSPServer.h"
30#include "CodeComplete.h"
31#include "CompileCommands.h"
32#include "Config.h"
33#include "Feature.h"
34#include "GlobalCompilationDatabase.h"
35#include "Hover.h"
36#include "InlayHints.h"
37#include "ParsedAST.h"
38#include "Preamble.h"
39#include "Protocol.h"
40#include "SemanticHighlighting.h"
41#include "SourceCode.h"
42#include "XRefs.h"
43#include "index/CanonicalIncludes.h"
44#include "index/FileIndex.h"
45#include "refactor/Tweak.h"
46#include "support/ThreadsafeFS.h"
47#include "support/Trace.h"
48#include "clang/AST/ASTContext.h"
49#include "clang/Basic/Diagnostic.h"
50#include "clang/Format/Format.h"
51#include "clang/Frontend/CompilerInvocation.h"
52#include "clang/Tooling/CompilationDatabase.h"
53#include "llvm/ADT/ArrayRef.h"
54#include "llvm/Support/Path.h"
55#include "llvm/Support/Process.h"
56#include <optional>
57
58namespace clang {
59namespace clangd {
60namespace {
61
62// These will never be shown in --help, ClangdMain doesn't list the category.
63llvm::cl::opt<std::string> CheckTidyTime{
64 "check-tidy-time",
65 llvm::cl::desc("Print the overhead of checks matching this glob"),
66 llvm::cl::init("")};
67llvm::cl::opt<std::string> CheckFileLines{
68 "check-lines",
69 llvm::cl::desc(
70 "Limits the range of tokens in -check file on which "
71 "various features are tested. Example --check-lines=3-7 restricts "
72 "testing to lines 3 to 7 (inclusive) or --check-lines=5 to restrict "
73 "to one line. Default is testing entire file."),
74 llvm::cl::init("")};
75llvm::cl::opt<bool> CheckLocations{
76 "check-locations",
77 llvm::cl::desc(
78 "Runs certain features (e.g. hover) at each point in the file. "
79 "Somewhat slow."),
80 llvm::cl::init(true)};
81llvm::cl::opt<bool> CheckCompletion{
82 "check-completion",
83 llvm::cl::desc("Run code-completion at each point (slow)"),
84 llvm::cl::init(false)};
85
86// Print (and count) the error-level diagnostics (warnings are ignored).
87unsigned showErrors(llvm::ArrayRef<Diag> Diags) {
88 unsigned ErrCount = 0;
89 for (const auto &D : Diags) {
90 if (D.Severity >= DiagnosticsEngine::Error) {
91 elog("[{0}] Line {1}: {2}", D.Name, D.Range.start.line + 1, D.Message);
92 ++ErrCount;
93 }
94 }
95 return ErrCount;
96}
97
98std::vector<std::string> listTidyChecks(llvm::StringRef Glob) {
99 tidy::GlobList G(Glob);
100 tidy::ClangTidyCheckFactories CTFactories;
101 for (const auto &E : tidy::ClangTidyModuleRegistry::entries())
102 E.instantiate()->addCheckFactories(CTFactories);
103 std::vector<std::string> Result;
104 for (const auto &E : CTFactories)
105 if (G.contains(E.getKey()))
106 Result.push_back(E.getKey().str());
107 llvm::sort(Result);
108 return Result;
109}
110
111// This class is just a linear pipeline whose functions get called in sequence.
112// Each exercises part of clangd's logic on our test file and logs results.
113// Later steps depend on state built in earlier ones (such as the AST).
114// Many steps can fatally fail (return false), then subsequent ones cannot run.
115// Nonfatal failures are logged and tracked in ErrCount.
116class Checker {
117 // from constructor
118 std::string File;
119 ClangdLSPServer::Options Opts;
120 // from buildCommand
121 tooling::CompileCommand Cmd;
122 // from buildInvocation
123 ParseInputs Inputs;
124 std::unique_ptr<CompilerInvocation> Invocation;
125 format::FormatStyle Style;
126 // from buildAST
127 std::shared_ptr<const PreambleData> Preamble;
128 std::optional<ParsedAST> AST;
129 FileIndex Index;
130
131public:
132 // Number of non-fatal errors seen.
133 unsigned ErrCount = 0;
134
135 Checker(llvm::StringRef File, const ClangdLSPServer::Options &Opts)
136 : File(File), Opts(Opts) {}
137
138 // Read compilation database and choose a compile command for the file.
139 bool buildCommand(const ThreadsafeFS &TFS) {
140 log("Loading compilation database...");
141 DirectoryBasedGlobalCompilationDatabase::Options CDBOpts(TFS);
142 CDBOpts.CompileCommandsDir =
143 Config::current().CompileFlags.CDBSearch.FixedCDBPath;
144 std::unique_ptr<GlobalCompilationDatabase> BaseCDB =
145 std::make_unique<DirectoryBasedGlobalCompilationDatabase>(CDBOpts);
146 auto Mangler = CommandMangler::detect();
147 Mangler.SystemIncludeExtractor =
148 getSystemIncludeExtractor(llvm::ArrayRef(Opts.QueryDriverGlobs));
149 if (Opts.ResourceDir)
150 Mangler.ResourceDir = *Opts.ResourceDir;
151 auto CDB = std::make_unique<OverlayCDB>(
152 BaseCDB.get(), std::vector<std::string>{}, std::move(Mangler));
153
154 if (auto TrueCmd = CDB->getCompileCommand(File)) {
155 Cmd = std::move(*TrueCmd);
156 log("Compile command {0} is: {1}",
157 Cmd.Heuristic.empty() ? "from CDB" : Cmd.Heuristic,
158 printArgv(Cmd.CommandLine));
159 } else {
160 Cmd = CDB->getFallbackCommand(File);
161 log("Generic fallback command is: {0}", printArgv(Cmd.CommandLine));
162 }
163
164 return true;
165 }
166
167 // Prepare inputs and build CompilerInvocation (parsed compile command).
168 bool buildInvocation(const ThreadsafeFS &TFS,
169 std::optional<std::string> Contents) {
170 StoreDiags CaptureInvocationDiags;
171 std::vector<std::string> CC1Args;
172 Inputs.CompileCommand = Cmd;
173 Inputs.TFS = &TFS;
174 Inputs.ClangTidyProvider = Opts.ClangTidyProvider;
175 Inputs.Opts.PreambleParseForwardingFunctions =
176 Opts.PreambleParseForwardingFunctions;
177 if (Contents) {
178 Inputs.Contents = *Contents;
179 log("Imaginary source file contents:\n{0}", Inputs.Contents);
180 } else {
181 if (auto Contents = TFS.view(std::nullopt)->getBufferForFile(File)) {
182 Inputs.Contents = Contents->get()->getBuffer().str();
183 } else {
184 elog("Couldn't read {0}: {1}", File, Contents.getError().message());
185 return false;
186 }
187 }
188 log("Parsing command...");
189 Invocation =
190 buildCompilerInvocation(Inputs, CaptureInvocationDiags, &CC1Args);
191 auto InvocationDiags = CaptureInvocationDiags.take();
192 ErrCount += showErrors(InvocationDiags);
193 log("internal (cc1) args are: {0}", printArgv(CC1Args));
194 if (!Invocation) {
195 elog("Failed to parse command line");
196 return false;
197 }
198
199 // FIXME: Check that resource-dir/built-in-headers exist?
200
201 Style = getFormatStyleForFile(File, Inputs.Contents, TFS);
202
203 return true;
204 }
205
206 // Build preamble and AST, and index them.
207 bool buildAST() {
208 log("Building preamble...");
209 Preamble = buildPreamble(File, *Invocation, Inputs, /*StoreInMemory=*/true,
210 [&](ASTContext &Ctx, Preprocessor &PP,
211 const CanonicalIncludes &Includes) {
212 if (!Opts.BuildDynamicSymbolIndex)
213 return;
214 log("Indexing headers...");
215 Index.updatePreamble(File, /*Version=*/"null",
216 Ctx, PP, Includes);
217 });
218 if (!Preamble) {
7
Taking false branch
219 elog("Failed to build preamble");
220 return false;
221 }
222 ErrCount += showErrors(Preamble->Diags);
223
224 log("Building AST...");
225 AST = ParsedAST::build(File, Inputs, std::move(Invocation),
226 /*InvocationDiags=*/std::vector<Diag>{}, Preamble);
227 if (!AST) {
8
Taking false branch
228 elog("Failed to build AST");
229 return false;
230 }
231 ErrCount += showErrors(llvm::ArrayRef(*AST->getDiagnostics())
232 .drop_front(Preamble->Diags.size()));
233
234 if (Opts.BuildDynamicSymbolIndex) {
9
Assuming field 'BuildDynamicSymbolIndex' is false
10
Taking false branch
235 log("Indexing AST...");
236 Index.updateMain(File, *AST);
237 }
238
239 if (!CheckTidyTime.empty()) {
11
Assuming the condition is true
12
Taking true branch
240 if (!CLANGD_TIDY_CHECKS1) {
13
Taking false branch
241 elog("-{0} requires -DCLANGD_TIDY_CHECKS!", CheckTidyTime.ArgStr);
242 return false;
243 }
244 #ifndef NDEBUG
245 elog("Timing clang-tidy checks in asserts-mode is not representative!");
246 #endif
247 checkTidyTimes();
14
Calling 'Checker::checkTidyTimes'
248 }
249
250 return true;
251 }
252
253 // For each check foo, we want to build with checks=-* and checks=-*,foo.
254 // (We do a full build rather than just AST matchers to meausre PPCallbacks).
255 //
256 // However, performance has both random noise and systematic changes, such as
257 // step-function slowdowns due to CPU scaling.
258 // We take the median of 5 measurements, and after every check discard the
259 // measurement if the baseline changed by >3%.
260 void checkTidyTimes() {
261 double Stability = 0.03;
262 log("Timing AST build with individual clang-tidy checks (target accuracy "
263 "{0:P0})",
264 Stability);
265
266 using Duration = std::chrono::nanoseconds;
267 // Measure time elapsed by a block of code. Currently: user CPU time.
268 auto Time = [&](auto &&Run) -> Duration {
269 llvm::sys::TimePoint<> Elapsed;
270 std::chrono::nanoseconds UserBegin, UserEnd, System;
271 llvm::sys::Process::GetTimeUsage(Elapsed, UserBegin, System);
272 Run();
273 llvm::sys::Process::GetTimeUsage(Elapsed, UserEnd, System);
274 return UserEnd - UserBegin;
275 };
276 auto Change = [&](Duration Exp, Duration Base) -> double {
277 return (double)(Exp.count() - Base.count()) / Base.count();
278 };
279 // Build ParsedAST with a fixed check glob, and return the time taken.
280 auto Build = [&](llvm::StringRef Checks) -> Duration {
281 TidyProvider CTProvider = [&](tidy::ClangTidyOptions &Opts,
282 llvm::StringRef) {
283 Opts.Checks = Checks.str();
284 };
285 Inputs.ClangTidyProvider = CTProvider;
286 // Sigh, can't reuse the CompilerInvocation.
287 IgnoringDiagConsumer IgnoreDiags;
288 auto Invocation = buildCompilerInvocation(Inputs, IgnoreDiags);
289 Duration Val = Time([&] {
290 ParsedAST::build(File, Inputs, std::move(Invocation), {}, Preamble);
291 });
292 vlog(" Measured {0} ==> {1}", Checks, Val);
293 return Val;
18
Address of stack memory associated with local variable 'CTProvider' is still referred to by the stack variable 'C' upon returning to the caller. This will be a dangling reference
294 };
295 // Measure several times, return the median.
296 auto MedianTime = [&](llvm::StringRef Checks) -> Duration {
297 std::array<Duration, 5> Measurements;
298 for (auto &M : Measurements)
16
Assuming '__begin4' is not equal to '__end4'
299 M = Build(Checks);
17
Calling 'operator()'
300 llvm::sort(Measurements);
301 return Measurements[Measurements.size() / 2];
302 };
303 Duration Baseline = MedianTime("-*");
15
Calling 'operator()'
304 log(" Baseline = {0}", Baseline);
305 // Attempt to time a check, may update Baseline if it is unstable.
306 auto Measure = [&](llvm::StringRef Check) -> double {
307 for (;;) {
308 Duration Median = MedianTime(("-*," + Check).str());
309 Duration NewBase = MedianTime("-*");
310
311 // Value only usable if baseline is fairly consistent before/after.
312 double DeltaFraction = Change(NewBase, Baseline);
313 Baseline = NewBase;
314 vlog(" Baseline = {0}", Baseline);
315 if (DeltaFraction < -Stability || DeltaFraction > Stability) {
316 elog(" Speed unstable, discarding measurement.");
317 continue;
318 }
319 return Change(Median, Baseline);
320 }
321 };
322
323 for (const auto& Check : listTidyChecks(CheckTidyTime)) {
324 // vlog the check name in case we crash!
325 vlog(" Timing {0}", Check);
326 double Fraction = Measure(Check);
327 log(" {0} = {1:P0}", Check, Fraction);
328 }
329 log("Finished individual clang-tidy checks");
330
331 // Restore old options.
332 Inputs.ClangTidyProvider = Opts.ClangTidyProvider;
333 }
334
335 // Build Inlay Hints for the entire AST or the specified range
336 void buildInlayHints(std::optional<Range> LineRange) {
337 log("Building inlay hints");
338 auto Hints = inlayHints(*AST, LineRange);
339
340 for (const auto &Hint : Hints) {
341 vlog(" {0} {1} {2}", Hint.kind, Hint.position, Hint.label);
342 }
343 }
344
345 void buildSemanticHighlighting(std::optional<Range> LineRange) {
346 log("Building semantic highlighting");
347 auto Highlights =
348 getSemanticHighlightings(*AST, /*IncludeInactiveRegionTokens=*/true);
349 for (const auto HL : Highlights)
350 if (!LineRange || LineRange->contains(HL.R))
351 vlog(" {0} {1} {2}", HL.R, HL.Kind, HL.Modifiers);
352 }
353
354 // Run AST-based features at each token in the file.
355 void testLocationFeatures(std::optional<Range> LineRange) {
356 trace::Span Trace("testLocationFeatures");
357 log("Testing features at each token (may be slow in large files)");
358 auto &SM = AST->getSourceManager();
359 auto SpelledTokens = AST->getTokens().spelledTokens(SM.getMainFileID());
360
361 CodeCompleteOptions CCOpts = Opts.CodeComplete;
362 CCOpts.Index = &Index;
363
364 for (const auto &Tok : SpelledTokens) {
365 unsigned Start = AST->getSourceManager().getFileOffset(Tok.location());
366 unsigned End = Start + Tok.length();
367 Position Pos = offsetToPosition(Inputs.Contents, Start);
368
369 if (LineRange && !LineRange->contains(Pos))
370 continue;
371
372 trace::Span Trace("Token");
373 SPAN_ATTACH(Trace, "pos", Pos)do { if (auto *Args = (Trace).Args) (*Args)["pos"] = Pos; } while
(0)
;
374 SPAN_ATTACH(Trace, "text", Tok.text(AST->getSourceManager()))do { if (auto *Args = (Trace).Args) (*Args)["text"] = Tok.text
(AST->getSourceManager()); } while (0)
;
375
376 // FIXME: dumping the tokens may leak sensitive code into bug reports.
377 // Add an option to turn this off, once we decide how options work.
378 vlog(" {0} {1}", Pos, Tok.text(AST->getSourceManager()));
379 auto Tree = SelectionTree::createRight(AST->getASTContext(),
380 AST->getTokens(), Start, End);
381 Tweak::Selection Selection(&Index, *AST, Start, End, std::move(Tree),
382 nullptr);
383 // FS is only populated when applying a tweak, not during prepare as
384 // prepare should not do any I/O to be fast.
385 auto Tweaks =
386 prepareTweaks(Selection, Opts.TweakFilter, Opts.FeatureModules);
387 Selection.FS =
388 &AST->getSourceManager().getFileManager().getVirtualFileSystem();
389 for (const auto &T : Tweaks) {
390 auto Result = T->apply(Selection);
391 if (!Result) {
392 elog(" tweak: {0} ==> FAIL: {1}", T->id(), Result.takeError());
393 ++ErrCount;
394 } else {
395 vlog(" tweak: {0}", T->id());
396 }
397 }
398 unsigned Definitions = locateSymbolAt(*AST, Pos, &Index).size();
399 vlog(" definition: {0}", Definitions);
400
401 auto Hover = getHover(*AST, Pos, Style, &Index);
402 vlog(" hover: {0}", Hover.has_value());
403
404 unsigned DocHighlights = findDocumentHighlights(*AST, Pos).size();
405 vlog(" documentHighlight: {0}", DocHighlights);
406
407 if (CheckCompletion) {
408 Position EndPos = offsetToPosition(Inputs.Contents, End);
409 auto CC = codeComplete(File, EndPos, Preamble.get(), Inputs, CCOpts);
410 vlog(" code completion: {0}",
411 CC.Completions.empty() ? "<empty>" : CC.Completions[0].Name);
412 }
413 }
414 }
415};
416
417} // namespace
418
419bool check(llvm::StringRef File, const ThreadsafeFS &TFS,
420 const ClangdLSPServer::Options &Opts) {
421 std::optional<Range> LineRange;
422 if (!CheckFileLines.empty()) {
1
Assuming the condition is false
2
Taking false branch
423 uint32_t Begin = 0, End = std::numeric_limits<uint32_t>::max();
424 StringRef RangeStr(CheckFileLines);
425 bool ParseError = RangeStr.consumeInteger(0, Begin);
426 if (RangeStr.empty()) {
427 End = Begin;
428 } else {
429 ParseError |= !RangeStr.consume_front("-");
430 ParseError |= RangeStr.consumeInteger(0, End);
431 }
432 if (ParseError || !RangeStr.empty() || Begin <= 0 || End < Begin) {
433 elog("Invalid --check-lines specified. Use Begin-End format, e.g. 3-17");
434 return false;
435 }
436 LineRange = Range{Position{static_cast<int>(Begin - 1), 0},
437 Position{static_cast<int>(End), 0}};
438 }
439
440 llvm::SmallString<0> FakeFile;
441 std::optional<std::string> Contents;
442 if (File.empty()) {
3
Assuming the condition is false
4
Taking false branch
443 llvm::sys::path::system_temp_directory(false, FakeFile);
444 llvm::sys::path::append(FakeFile, "test.cc");
445 File = FakeFile;
446 Contents = R"cpp(
447 #include <stddef.h>
448 #include <string>
449
450 size_t N = 50;
451 auto xxx = std::string(N, 'x');
452 )cpp";
453 }
454 log("Testing on source file {0}", File);
455
456 auto ContextProvider = ClangdServer::createConfiguredContextProvider(
457 Opts.ConfigProvider, nullptr);
458 WithContext Ctx(ContextProvider(
459 FakeFile.empty()
5
'?' condition is false
460 ? File
461 : /*Don't turn on local configs for an arbitrary temp path.*/ ""));
462 Checker C(File, Opts);
463 if (!C.buildCommand(TFS) || !C.buildInvocation(TFS, Contents) ||
464 !C.buildAST())
6
Calling 'Checker::buildAST'
465 return false;
466 C.buildInlayHints(LineRange);
467 C.buildSemanticHighlighting(LineRange);
468 if (CheckLocations)
469 C.testLocationFeatures(LineRange);
470
471 log("All checks completed, {0} errors", C.ErrCount);
472 return C.ErrCount == 0;
473}
474
475} // namespace clangd
476} // namespace clang