1//===- Indexing.cpp - Higher level API functions --------------------------===//
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 "CIndexDiagnostic.h"
10#include "CIndexer.h"
11#include "CLog.h"
12#include "CXCursor.h"
13#include "CXIndexDataConsumer.h"
14#include "CXSourceLocation.h"
15#include "CXString.h"
16#include "CXTranslationUnit.h"
17#include "clang/AST/ASTConsumer.h"
18#include "clang/Driver/CreateInvocationFromArgs.h"
19#include "clang/Frontend/ASTUnit.h"
20#include "clang/Frontend/CompilerInstance.h"
21#include "clang/Frontend/CompilerInvocation.h"
22#include "clang/Frontend/FrontendAction.h"
23#include "clang/Frontend/MultiplexConsumer.h"
24#include "clang/Frontend/Utils.h"
25#include "clang/Index/IndexingAction.h"
26#include "clang/Lex/HeaderSearch.h"
27#include "clang/Lex/PPCallbacks.h"
28#include "clang/Lex/PPConditionalDirectiveRecord.h"
29#include "clang/Lex/Preprocessor.h"
30#include "clang/Lex/PreprocessorOptions.h"
31#include "llvm/Support/CrashRecoveryContext.h"
32#include "llvm/Support/MemoryBuffer.h"
33#include "llvm/Support/VirtualFileSystem.h"
34#include <cstdio>
35#include <mutex>
36#include <utility>
37
38using namespace clang;
39using namespace clang::index;
40using namespace cxtu;
41using namespace cxindex;
42
43namespace {
44
45//===----------------------------------------------------------------------===//
46// Skip Parsed Bodies
47//===----------------------------------------------------------------------===//
48
49/// A "region" in source code identified by the file/offset of the
50/// preprocessor conditional directive that it belongs to.
51/// Multiple, non-consecutive ranges can be parts of the same region.
52///
53/// As an example of different regions separated by preprocessor directives:
54///
55/// \code
56/// #1
57/// #ifdef BLAH
58/// #2
59/// #ifdef CAKE
60/// #3
61/// #endif
62/// #2
63/// #endif
64/// #1
65/// \endcode
66///
67/// There are 3 regions, with non-consecutive parts:
68/// #1 is identified as the beginning of the file
69/// #2 is identified as the location of "#ifdef BLAH"
70/// #3 is identified as the location of "#ifdef CAKE"
71///
72class PPRegion {
73 llvm::sys::fs::UniqueID UniqueID;
74 time_t ModTime;
75 unsigned Offset;
76public:
77 PPRegion() : UniqueID(0, 0), ModTime(), Offset() {}
78 PPRegion(llvm::sys::fs::UniqueID UniqueID, unsigned offset, time_t modTime)
79 : UniqueID(UniqueID), ModTime(modTime), Offset(offset) {}
80
81 const llvm::sys::fs::UniqueID &getUniqueID() const { return UniqueID; }
82 unsigned getOffset() const { return Offset; }
83 time_t getModTime() const { return ModTime; }
84
85 bool isInvalid() const { return *this == PPRegion(); }
86
87 friend bool operator==(const PPRegion &lhs, const PPRegion &rhs) {
88 return lhs.UniqueID == rhs.UniqueID && lhs.Offset == rhs.Offset &&
89 lhs.ModTime == rhs.ModTime;
90 }
91};
92
93} // end anonymous namespace
94
95namespace llvm {
96
97 template <>
98 struct DenseMapInfo<PPRegion> {
99 static unsigned getHashValue(const PPRegion &S) {
100 llvm::FoldingSetNodeID ID;
101 const llvm::sys::fs::UniqueID &UniqueID = S.getUniqueID();
102 ID.AddInteger(I: UniqueID.getFile());
103 ID.AddInteger(I: UniqueID.getDevice());
104 ID.AddInteger(I: S.getOffset());
105 ID.AddInteger(I: S.getModTime());
106 return ID.ComputeHash();
107 }
108
109 static bool isEqual(const PPRegion &LHS, const PPRegion &RHS) {
110 return LHS == RHS;
111 }
112 };
113}
114
115namespace {
116
117/// Keeps track of function bodies that have already been parsed.
118///
119/// Is thread-safe.
120class ThreadSafeParsedRegions {
121 mutable std::mutex Mutex;
122 llvm::DenseSet<PPRegion> ParsedRegions;
123
124public:
125 ~ThreadSafeParsedRegions() = default;
126
127 llvm::DenseSet<PPRegion> getParsedRegions() const {
128 std::lock_guard<std::mutex> MG(Mutex);
129 return ParsedRegions;
130 }
131
132 void addParsedRegions(ArrayRef<PPRegion> Regions) {
133 std::lock_guard<std::mutex> MG(Mutex);
134 ParsedRegions.insert_range(R&: Regions);
135 }
136};
137
138/// Provides information whether source locations have already been parsed in
139/// another FrontendAction.
140///
141/// Is NOT thread-safe.
142class ParsedSrcLocationsTracker {
143 ThreadSafeParsedRegions &ParsedRegionsStorage;
144 PPConditionalDirectiveRecord &PPRec;
145 Preprocessor &PP;
146
147 /// Snapshot of the shared state at the point when this instance was
148 /// constructed.
149 llvm::DenseSet<PPRegion> ParsedRegionsSnapshot;
150 /// Regions that were queried during this instance lifetime.
151 SmallVector<PPRegion, 32> NewParsedRegions;
152
153 /// Caching the last queried region.
154 PPRegion LastRegion;
155 bool LastIsParsed;
156
157public:
158 /// Creates snapshot of \p ParsedRegionsStorage.
159 ParsedSrcLocationsTracker(ThreadSafeParsedRegions &ParsedRegionsStorage,
160 PPConditionalDirectiveRecord &ppRec,
161 Preprocessor &pp)
162 : ParsedRegionsStorage(ParsedRegionsStorage), PPRec(ppRec), PP(pp),
163 ParsedRegionsSnapshot(ParsedRegionsStorage.getParsedRegions()) {}
164
165 /// \returns true iff \p Loc has already been parsed.
166 ///
167 /// Can provide false-negative in case the location was parsed after this
168 /// instance had been constructed.
169 bool hasAlredyBeenParsed(SourceLocation Loc, FileID FID, FileEntryRef FE) {
170 PPRegion region = getRegion(Loc, FID, FE);
171 if (region.isInvalid())
172 return false;
173
174 // Check common case, consecutive functions in the same region.
175 if (LastRegion == region)
176 return LastIsParsed;
177
178 LastRegion = region;
179 // Source locations can't be revisited during single TU parsing.
180 // That means if we hit the same region again, it's a different location in
181 // the same region and so the "is parsed" value from the snapshot is still
182 // correct.
183 LastIsParsed = ParsedRegionsSnapshot.count(V: region);
184 if (!LastIsParsed)
185 NewParsedRegions.emplace_back(Args: std::move(region));
186 return LastIsParsed;
187 }
188
189 /// Updates ParsedRegionsStorage with newly parsed regions.
190 void syncWithStorage() {
191 ParsedRegionsStorage.addParsedRegions(Regions: NewParsedRegions);
192 }
193
194private:
195 PPRegion getRegion(SourceLocation Loc, FileID FID, FileEntryRef FE) {
196 auto Bail = [this, FE]() {
197 if (isParsedOnceInclude(FE)) {
198 const llvm::sys::fs::UniqueID &ID = FE.getUniqueID();
199 return PPRegion(ID, 0, FE.getModificationTime());
200 }
201 return PPRegion();
202 };
203
204 SourceLocation RegionLoc = PPRec.findConditionalDirectiveRegionLoc(Loc);
205 assert(RegionLoc.isFileID());
206 if (RegionLoc.isInvalid())
207 return Bail();
208
209 FileID RegionFID;
210 unsigned RegionOffset;
211 std::tie(args&: RegionFID, args&: RegionOffset) =
212 PPRec.getSourceManager().getDecomposedLoc(Loc: RegionLoc);
213
214 if (RegionFID != FID)
215 return Bail();
216
217 const llvm::sys::fs::UniqueID &ID = FE.getUniqueID();
218 return PPRegion(ID, RegionOffset, FE.getModificationTime());
219 }
220
221 bool isParsedOnceInclude(FileEntryRef FE) {
222 return PP.getHeaderSearchInfo().isFileMultipleIncludeGuarded(File: FE) ||
223 PP.getHeaderSearchInfo().hasFileBeenImported(File: FE);
224 }
225};
226
227//===----------------------------------------------------------------------===//
228// IndexPPCallbacks
229//===----------------------------------------------------------------------===//
230
231class IndexPPCallbacks : public PPCallbacks {
232 Preprocessor &PP;
233 CXIndexDataConsumer &DataConsumer;
234 bool IsMainFileEntered;
235
236public:
237 IndexPPCallbacks(Preprocessor &PP, CXIndexDataConsumer &dataConsumer)
238 : PP(PP), DataConsumer(dataConsumer), IsMainFileEntered(false) { }
239
240 void FileChanged(SourceLocation Loc, FileChangeReason Reason,
241 SrcMgr::CharacteristicKind FileType, FileID PrevFID) override {
242 if (IsMainFileEntered)
243 return;
244
245 SourceManager &SM = PP.getSourceManager();
246 SourceLocation MainFileLoc = SM.getLocForStartOfFile(FID: SM.getMainFileID());
247
248 if (Loc == MainFileLoc && Reason == PPCallbacks::EnterFile) {
249 IsMainFileEntered = true;
250 DataConsumer.enteredMainFile(
251 File: *SM.getFileEntryRefForID(FID: SM.getMainFileID()));
252 }
253 }
254
255 void InclusionDirective(SourceLocation HashLoc, const Token &IncludeTok,
256 StringRef FileName, bool IsAngled,
257 CharSourceRange FilenameRange,
258 OptionalFileEntryRef File, StringRef SearchPath,
259 StringRef RelativePath, const Module *SuggestedModule,
260 bool ModuleImported,
261 SrcMgr::CharacteristicKind FileType) override {
262 bool isImport = (IncludeTok.is(K: tok::identifier) &&
263 IncludeTok.getIdentifierInfo()->getPPKeywordID() == tok::pp_import);
264 DataConsumer.ppIncludedFile(hashLoc: HashLoc, filename: FileName, File, isImport, isAngled: IsAngled,
265 isModuleImport: ModuleImported);
266 }
267
268 /// MacroDefined - This hook is called whenever a macro definition is seen.
269 void MacroDefined(const Token &Id, const MacroDirective *MD) override {}
270
271 /// MacroUndefined - This hook is called whenever a macro #undef is seen.
272 /// MI is released immediately following this callback.
273 void MacroUndefined(const Token &MacroNameTok,
274 const MacroDefinition &MD,
275 const MacroDirective *UD) override {}
276
277 /// MacroExpands - This is called by when a macro invocation is found.
278 void MacroExpands(const Token &MacroNameTok, const MacroDefinition &MD,
279 SourceRange Range, const MacroArgs *Args) override {}
280
281 /// SourceRangeSkipped - This hook is called when a source range is skipped.
282 /// \param Range The SourceRange that was skipped. The range begins at the
283 /// #if/#else directive and ends after the #endif/#else directive.
284 void SourceRangeSkipped(SourceRange Range, SourceLocation EndifLoc) override {
285 }
286};
287
288//===----------------------------------------------------------------------===//
289// IndexingConsumer
290//===----------------------------------------------------------------------===//
291
292class IndexingConsumer : public ASTConsumer {
293 CXIndexDataConsumer &DataConsumer;
294
295public:
296 IndexingConsumer(CXIndexDataConsumer &dataConsumer,
297 ParsedSrcLocationsTracker *parsedLocsTracker)
298 : DataConsumer(dataConsumer) {}
299
300 void Initialize(ASTContext &Context) override {
301 // TODO: accept Context as IntrusiveRefCntPtr?
302 DataConsumer.setASTContext(&Context);
303 DataConsumer.startedTranslationUnit();
304 }
305
306 bool HandleTopLevelDecl(DeclGroupRef DG) override {
307 return !DataConsumer.shouldAbort();
308 }
309};
310
311//===----------------------------------------------------------------------===//
312// CaptureDiagnosticConsumer
313//===----------------------------------------------------------------------===//
314
315class CaptureDiagnosticConsumer : public DiagnosticConsumer {
316 SmallVector<StoredDiagnostic, 4> Errors;
317public:
318
319 void HandleDiagnostic(DiagnosticsEngine::Level level,
320 const Diagnostic &Info) override {
321 if (level >= DiagnosticsEngine::Error)
322 Errors.push_back(Elt: StoredDiagnostic(level, Info));
323 }
324};
325
326//===----------------------------------------------------------------------===//
327// IndexingFrontendAction
328//===----------------------------------------------------------------------===//
329
330class IndexingFrontendAction : public ASTFrontendAction {
331 std::shared_ptr<CXIndexDataConsumer> DataConsumer;
332 IndexingOptions Opts;
333
334 ThreadSafeParsedRegions *SKData;
335 std::unique_ptr<ParsedSrcLocationsTracker> ParsedLocsTracker;
336
337public:
338 IndexingFrontendAction(std::shared_ptr<CXIndexDataConsumer> dataConsumer,
339 const IndexingOptions &Opts,
340 ThreadSafeParsedRegions *skData)
341 : DataConsumer(std::move(dataConsumer)), Opts(Opts), SKData(skData) {}
342
343 std::unique_ptr<ASTConsumer> CreateASTConsumer(CompilerInstance &CI,
344 StringRef InFile) override {
345 PreprocessorOptions &PPOpts = CI.getPreprocessorOpts();
346
347 if (!PPOpts.ImplicitPCHInclude.empty())
348 DataConsumer->importedPCH(FileName: PPOpts.ImplicitPCHInclude);
349
350 DataConsumer->setASTContext(CI.getASTContextPtr());
351 Preprocessor &PP = CI.getPreprocessor();
352 PP.addPPCallbacks(C: std::make_unique<IndexPPCallbacks>(args&: PP, args&: *DataConsumer));
353 DataConsumer->setPreprocessor(CI.getPreprocessorPtr());
354
355 if (SKData) {
356 auto *PPRec = new PPConditionalDirectiveRecord(PP.getSourceManager());
357 PP.addPPCallbacks(C: std::unique_ptr<PPCallbacks>(PPRec));
358 ParsedLocsTracker =
359 std::make_unique<ParsedSrcLocationsTracker>(args&: *SKData, args&: *PPRec, args&: PP);
360 }
361
362 std::vector<std::unique_ptr<ASTConsumer>> Consumers;
363 Consumers.push_back(x: std::make_unique<IndexingConsumer>(
364 args&: *DataConsumer, args: ParsedLocsTracker.get()));
365 Consumers.push_back(x: createIndexingASTConsumer(
366 DataConsumer, Opts, PP: CI.getPreprocessorPtr(),
367 ShouldSkipFunctionBody: [this](const Decl *D) { return this->shouldSkipFunctionBody(D); }));
368 return std::make_unique<MultiplexConsumer>(args: std::move(Consumers));
369 }
370
371 bool shouldSkipFunctionBody(const Decl *D) {
372 if (!ParsedLocsTracker) {
373 // Always skip bodies.
374 return true;
375 }
376
377 const SourceManager &SM = D->getASTContext().getSourceManager();
378 SourceLocation Loc = D->getLocation();
379 if (Loc.isMacroID())
380 return false;
381 if (SM.isInSystemHeader(Loc))
382 return true; // always skip bodies from system headers.
383
384 auto [FID, Offset] = SM.getDecomposedLoc(Loc);
385 // Don't skip bodies from main files; this may be revisited.
386 if (SM.getMainFileID() == FID)
387 return false;
388 OptionalFileEntryRef FE = SM.getFileEntryRefForID(FID);
389 if (!FE)
390 return false;
391
392 return ParsedLocsTracker->hasAlredyBeenParsed(Loc, FID, FE: *FE);
393 }
394
395 TranslationUnitKind getTranslationUnitKind() override {
396 if (DataConsumer->shouldIndexImplicitTemplateInsts())
397 return TU_Complete;
398 else
399 return TU_Prefix;
400 }
401 bool hasCodeCompletionSupport() const override { return false; }
402
403 void EndSourceFileAction() override {
404 if (ParsedLocsTracker)
405 ParsedLocsTracker->syncWithStorage();
406 }
407};
408
409//===----------------------------------------------------------------------===//
410// clang_indexSourceFileUnit Implementation
411//===----------------------------------------------------------------------===//
412
413static IndexingOptions getIndexingOptionsFromCXOptions(unsigned index_options) {
414 IndexingOptions IdxOpts;
415 if (index_options & CXIndexOpt_IndexFunctionLocalSymbols)
416 IdxOpts.IndexFunctionLocals = true;
417 if (index_options & CXIndexOpt_IndexImplicitTemplateInstantiations)
418 IdxOpts.IndexImplicitInstantiation = true;
419 return IdxOpts;
420}
421
422struct IndexSessionData {
423 CXIndex CIdx;
424 std::unique_ptr<ThreadSafeParsedRegions> SkipBodyData =
425 std::make_unique<ThreadSafeParsedRegions>();
426
427 explicit IndexSessionData(CXIndex cIdx) : CIdx(cIdx) {}
428};
429
430} // anonymous namespace
431
432static CXErrorCode clang_indexSourceFile_Impl(
433 CXIndexAction cxIdxAction, CXClientData client_data,
434 IndexerCallbacks *client_index_callbacks, unsigned index_callbacks_size,
435 unsigned index_options, const char *source_filename,
436 const char *const *command_line_args, int num_command_line_args,
437 ArrayRef<CXUnsavedFile> unsaved_files, CXTranslationUnit *out_TU,
438 unsigned TU_options) {
439 if (out_TU)
440 *out_TU = nullptr;
441 bool requestedToGetTU = (out_TU != nullptr);
442
443 if (!cxIdxAction) {
444 return CXError_InvalidArguments;
445 }
446 if (!client_index_callbacks || index_callbacks_size == 0) {
447 return CXError_InvalidArguments;
448 }
449
450 IndexerCallbacks CB;
451 memset(s: &CB, c: 0, n: sizeof(CB));
452 unsigned ClientCBSize = index_callbacks_size < sizeof(CB)
453 ? index_callbacks_size : sizeof(CB);
454 memcpy(dest: &CB, src: client_index_callbacks, n: ClientCBSize);
455
456 IndexSessionData *IdxSession = static_cast<IndexSessionData *>(cxIdxAction);
457 CIndexer *CXXIdx = static_cast<CIndexer *>(IdxSession->CIdx);
458
459 if (CXXIdx->isOptEnabled(opt: CXGlobalOpt_ThreadBackgroundPriorityForIndexing))
460 setThreadBackgroundPriority();
461
462 CaptureDiagsKind CaptureDiagnostics = CaptureDiagsKind::All;
463 if (TU_options & CXTranslationUnit_IgnoreNonErrorsFromIncludedFiles)
464 CaptureDiagnostics = CaptureDiagsKind::AllWithoutNonErrorsFromIncludes;
465 if (Logger::isLoggingEnabled())
466 CaptureDiagnostics = CaptureDiagsKind::None;
467
468 CaptureDiagnosticConsumer *CaptureDiag = nullptr;
469 if (CaptureDiagnostics != CaptureDiagsKind::None)
470 CaptureDiag = new CaptureDiagnosticConsumer();
471
472 // Configure the diagnostics.
473 auto DiagOpts = std::make_shared<DiagnosticOptions>();
474 IntrusiveRefCntPtr<DiagnosticsEngine> Diags(
475 CompilerInstance::createDiagnostics(VFS&: *llvm::vfs::getRealFileSystem(),
476 Opts&: *DiagOpts, Client: CaptureDiag,
477 /*ShouldOwnClient=*/true));
478
479 // Recover resources if we crash before exiting this function.
480 llvm::CrashRecoveryContextCleanupRegistrar<DiagnosticsEngine,
481 llvm::CrashRecoveryContextReleaseRefCleanup<DiagnosticsEngine> >
482 DiagCleanup(Diags.get());
483
484 std::unique_ptr<std::vector<const char *>> Args(
485 new std::vector<const char *>());
486
487 // Recover resources if we crash before exiting this method.
488 llvm::CrashRecoveryContextCleanupRegistrar<std::vector<const char*> >
489 ArgsCleanup(Args.get());
490
491 Args->insert(position: Args->end(), first: command_line_args,
492 last: command_line_args + num_command_line_args);
493
494 // The 'source_filename' argument is optional. If the caller does not
495 // specify it then it is assumed that the source file is specified
496 // in the actual argument list.
497 // Put the source file after command_line_args otherwise if '-x' flag is
498 // present it will be unused.
499 if (source_filename)
500 Args->push_back(x: source_filename);
501
502 CreateInvocationOptions CIOpts;
503 CIOpts.Diags = Diags;
504 CIOpts.ProbePrecompiled = true; // FIXME: historical default. Needed?
505 std::shared_ptr<CompilerInvocation> CInvok =
506 createInvocation(Args: *Args, Opts: std::move(CIOpts));
507
508 if (!CInvok)
509 return CXError_Failure;
510
511 // Recover resources if we crash before exiting this function.
512 llvm::CrashRecoveryContextCleanupRegistrar<
513 std::shared_ptr<CompilerInvocation>,
514 llvm::CrashRecoveryContextDestructorCleanup<
515 std::shared_ptr<CompilerInvocation>>>
516 CInvokCleanup(&CInvok);
517
518 if (CInvok->getFrontendOpts().Inputs.empty())
519 return CXError_Failure;
520
521 typedef SmallVector<std::unique_ptr<llvm::MemoryBuffer>, 8> MemBufferOwner;
522 std::unique_ptr<MemBufferOwner> BufOwner(new MemBufferOwner);
523
524 // Recover resources if we crash before exiting this method.
525 llvm::CrashRecoveryContextCleanupRegistrar<MemBufferOwner> BufOwnerCleanup(
526 BufOwner.get());
527
528 for (auto &UF : unsaved_files) {
529 std::unique_ptr<llvm::MemoryBuffer> MB =
530 llvm::MemoryBuffer::getMemBufferCopy(InputData: getContents(UF), BufferName: UF.Filename);
531 CInvok->getPreprocessorOpts().addRemappedFile(From: UF.Filename, To: MB.get());
532 BufOwner->push_back(Elt: std::move(MB));
533 }
534
535 // Since libclang is primarily used by batch tools dealing with
536 // (often very broken) source code, where spell-checking can have a
537 // significant negative impact on performance (particularly when
538 // precompiled headers are involved), we disable it.
539 CInvok->getLangOpts().SpellChecking = false;
540
541 if (index_options & CXIndexOpt_SuppressWarnings)
542 CInvok->getDiagnosticOpts().IgnoreWarnings = true;
543
544 // Make sure to use the raw module format.
545 CInvok->getHeaderSearchOpts().ModuleFormat = std::string(
546 CXXIdx->getPCHContainerOperations()->getRawReader().getFormats().front());
547
548 auto Unit = ASTUnit::create(CI: CInvok, DiagOpts, Diags, CaptureDiagnostics,
549 /*UserFilesAreVolatile=*/true);
550 if (!Unit)
551 return CXError_InvalidArguments;
552
553 auto *UPtr = Unit.get();
554 std::unique_ptr<CXTUOwner> CXTU(
555 new CXTUOwner(MakeCXTranslationUnit(CIdx: CXXIdx, AU: std::move(Unit))));
556
557 // Recover resources if we crash before exiting this method.
558 llvm::CrashRecoveryContextCleanupRegistrar<CXTUOwner>
559 CXTUCleanup(CXTU.get());
560
561 // Enable the skip-parsed-bodies optimization only for C++; this may be
562 // revisited.
563 bool SkipBodies = (index_options & CXIndexOpt_SkipParsedBodiesInSession) &&
564 CInvok->getLangOpts().CPlusPlus;
565 if (SkipBodies)
566 CInvok->getFrontendOpts().SkipFunctionBodies = true;
567
568 auto DataConsumer =
569 std::make_shared<CXIndexDataConsumer>(args&: client_data, args&: CB, args&: index_options,
570 args: CXTU->getTU());
571 auto IndexAction = std::make_unique<IndexingFrontendAction>(
572 args&: DataConsumer, args: getIndexingOptionsFromCXOptions(index_options),
573 args: SkipBodies ? IdxSession->SkipBodyData.get() : nullptr);
574
575 // Recover resources if we crash before exiting this method.
576 llvm::CrashRecoveryContextCleanupRegistrar<FrontendAction>
577 IndexActionCleanup(IndexAction.get());
578
579 bool Persistent = requestedToGetTU;
580 bool OnlyLocalDecls = false;
581 bool PrecompilePreamble = false;
582 bool CreatePreambleOnFirstParse = false;
583 bool CacheCodeCompletionResults = false;
584 PreprocessorOptions &PPOpts = CInvok->getPreprocessorOpts();
585 PPOpts.AllowPCHWithCompilerErrors = true;
586
587 if (requestedToGetTU) {
588 OnlyLocalDecls = CXXIdx->getOnlyLocalDecls();
589 PrecompilePreamble = TU_options & CXTranslationUnit_PrecompiledPreamble;
590 CreatePreambleOnFirstParse =
591 TU_options & CXTranslationUnit_CreatePreambleOnFirstParse;
592 // FIXME: Add a flag for modules.
593 CacheCodeCompletionResults
594 = TU_options & CXTranslationUnit_CacheCompletionResults;
595 }
596
597 if (TU_options & CXTranslationUnit_DetailedPreprocessingRecord) {
598 PPOpts.DetailedRecord = true;
599 }
600
601 if (!requestedToGetTU && !CInvok->getLangOpts().Modules)
602 PPOpts.DetailedRecord = false;
603
604 // Unless the user specified that they want the preamble on the first parse
605 // set it up to be created on the first reparse. This makes the first parse
606 // faster, trading for a slower (first) reparse.
607 unsigned PrecompilePreambleAfterNParses =
608 !PrecompilePreamble ? 0 : 2 - CreatePreambleOnFirstParse;
609 DiagnosticErrorTrap DiagTrap(*Diags);
610 bool Success = ASTUnit::LoadFromCompilerInvocationAction(
611 CI: std::move(CInvok), PCHContainerOps: CXXIdx->getPCHContainerOperations(), DiagOpts, Diags,
612 Action: IndexAction.get(), Unit: UPtr, Persistent, ResourceFilesPath: CXXIdx->getClangResourcesPath(),
613 OnlyLocalDecls, CaptureDiagnostics, PrecompilePreambleAfterNParses,
614 CacheCodeCompletionResults, /*UserFilesAreVolatile=*/true);
615 if (DiagTrap.hasErrorOccurred() && CXXIdx->getDisplayDiagnostics())
616 printDiagsToStderr(Unit: UPtr);
617
618 if (isASTReadError(AU: UPtr))
619 return CXError_ASTReadError;
620
621 if (!Success)
622 return CXError_Failure;
623
624 if (out_TU)
625 *out_TU = CXTU->takeTU();
626
627 return CXError_Success;
628}
629
630//===----------------------------------------------------------------------===//
631// clang_indexTranslationUnit Implementation
632//===----------------------------------------------------------------------===//
633
634static void indexPreprocessingRecord(ASTUnit &Unit, CXIndexDataConsumer &IdxCtx) {
635 Preprocessor &PP = Unit.getPreprocessor();
636 if (!PP.getPreprocessingRecord())
637 return;
638
639 // FIXME: Only deserialize inclusion directives.
640
641 bool isModuleFile = Unit.isModuleFile();
642 for (PreprocessedEntity *PPE : Unit.getLocalPreprocessingEntities()) {
643 if (InclusionDirective *ID = dyn_cast<InclusionDirective>(Val: PPE)) {
644 SourceLocation Loc = ID->getSourceRange().getBegin();
645 // Modules have synthetic main files as input, give an invalid location
646 // if the location points to such a file.
647 if (isModuleFile && Unit.isInMainFileID(Loc))
648 Loc = SourceLocation();
649 IdxCtx.ppIncludedFile(hashLoc: Loc, filename: ID->getFileName(),
650 File: ID->getFile(),
651 isImport: ID->getKind() == InclusionDirective::Import,
652 isAngled: !ID->wasInQuotes(), isModuleImport: ID->importedModule());
653 }
654 }
655}
656
657static CXErrorCode clang_indexTranslationUnit_Impl(
658 CXIndexAction idxAction, CXClientData client_data,
659 IndexerCallbacks *client_index_callbacks, unsigned index_callbacks_size,
660 unsigned index_options, CXTranslationUnit TU) {
661 // Check arguments.
662 if (isNotUsableTU(TU)) {
663 LOG_BAD_TU(TU);
664 return CXError_InvalidArguments;
665 }
666 if (!client_index_callbacks || index_callbacks_size == 0) {
667 return CXError_InvalidArguments;
668 }
669
670 CIndexer *CXXIdx = TU->CIdx;
671 if (CXXIdx->isOptEnabled(opt: CXGlobalOpt_ThreadBackgroundPriorityForIndexing))
672 setThreadBackgroundPriority();
673
674 IndexerCallbacks CB;
675 memset(s: &CB, c: 0, n: sizeof(CB));
676 unsigned ClientCBSize = index_callbacks_size < sizeof(CB)
677 ? index_callbacks_size : sizeof(CB);
678 memcpy(dest: &CB, src: client_index_callbacks, n: ClientCBSize);
679
680 CXIndexDataConsumer DataConsumer(client_data, CB, index_options, TU);
681
682 ASTUnit *Unit = cxtu::getASTUnit(TU);
683 if (!Unit)
684 return CXError_Failure;
685
686 ASTUnit::ConcurrencyCheck Check(*Unit);
687
688 if (std::optional<StringRef> PCHFile = Unit->getPCHFile())
689 DataConsumer.importedPCH(FileName: *PCHFile);
690
691 FileManager &FileMgr = Unit->getFileManager();
692
693 if (Unit->getOriginalSourceFileName().empty())
694 DataConsumer.enteredMainFile(File: std::nullopt);
695 else if (auto MainFile =
696 FileMgr.getFileRef(Filename: Unit->getOriginalSourceFileName()))
697 DataConsumer.enteredMainFile(File: *MainFile);
698 else
699 DataConsumer.enteredMainFile(File: std::nullopt);
700
701 DataConsumer.setASTContext(Unit->getASTContextPtr());
702 DataConsumer.startedTranslationUnit();
703
704 indexPreprocessingRecord(Unit&: *Unit, IdxCtx&: DataConsumer);
705 indexASTUnit(Unit&: *Unit, DataConsumer, Opts: getIndexingOptionsFromCXOptions(index_options));
706 DataConsumer.indexDiagnostics();
707
708 return CXError_Success;
709}
710
711//===----------------------------------------------------------------------===//
712// libclang public APIs.
713//===----------------------------------------------------------------------===//
714
715int clang_index_isEntityObjCContainerKind(CXIdxEntityKind K) {
716 return CXIdxEntity_ObjCClass <= K && K <= CXIdxEntity_ObjCCategory;
717}
718
719const CXIdxObjCContainerDeclInfo *
720clang_index_getObjCContainerDeclInfo(const CXIdxDeclInfo *DInfo) {
721 if (!DInfo)
722 return nullptr;
723
724 const DeclInfo *DI = static_cast<const DeclInfo *>(DInfo);
725 if (const ObjCContainerDeclInfo *
726 ContInfo = dyn_cast<ObjCContainerDeclInfo>(Val: DI))
727 return &ContInfo->ObjCContDeclInfo;
728
729 return nullptr;
730}
731
732const CXIdxObjCInterfaceDeclInfo *
733clang_index_getObjCInterfaceDeclInfo(const CXIdxDeclInfo *DInfo) {
734 if (!DInfo)
735 return nullptr;
736
737 const DeclInfo *DI = static_cast<const DeclInfo *>(DInfo);
738 if (const ObjCInterfaceDeclInfo *
739 InterInfo = dyn_cast<ObjCInterfaceDeclInfo>(Val: DI))
740 return &InterInfo->ObjCInterDeclInfo;
741
742 return nullptr;
743}
744
745const CXIdxObjCCategoryDeclInfo *
746clang_index_getObjCCategoryDeclInfo(const CXIdxDeclInfo *DInfo){
747 if (!DInfo)
748 return nullptr;
749
750 const DeclInfo *DI = static_cast<const DeclInfo *>(DInfo);
751 if (const ObjCCategoryDeclInfo *
752 CatInfo = dyn_cast<ObjCCategoryDeclInfo>(Val: DI))
753 return &CatInfo->ObjCCatDeclInfo;
754
755 return nullptr;
756}
757
758const CXIdxObjCProtocolRefListInfo *
759clang_index_getObjCProtocolRefListInfo(const CXIdxDeclInfo *DInfo) {
760 if (!DInfo)
761 return nullptr;
762
763 const DeclInfo *DI = static_cast<const DeclInfo *>(DInfo);
764
765 if (const ObjCInterfaceDeclInfo *
766 InterInfo = dyn_cast<ObjCInterfaceDeclInfo>(Val: DI))
767 return InterInfo->ObjCInterDeclInfo.protocols;
768
769 if (const ObjCProtocolDeclInfo *
770 ProtInfo = dyn_cast<ObjCProtocolDeclInfo>(Val: DI))
771 return &ProtInfo->ObjCProtoRefListInfo;
772
773 if (const ObjCCategoryDeclInfo *CatInfo = dyn_cast<ObjCCategoryDeclInfo>(Val: DI))
774 return CatInfo->ObjCCatDeclInfo.protocols;
775
776 return nullptr;
777}
778
779const CXIdxObjCPropertyDeclInfo *
780clang_index_getObjCPropertyDeclInfo(const CXIdxDeclInfo *DInfo) {
781 if (!DInfo)
782 return nullptr;
783
784 const DeclInfo *DI = static_cast<const DeclInfo *>(DInfo);
785 if (const ObjCPropertyDeclInfo *PropInfo = dyn_cast<ObjCPropertyDeclInfo>(Val: DI))
786 return &PropInfo->ObjCPropDeclInfo;
787
788 return nullptr;
789}
790
791const CXIdxIBOutletCollectionAttrInfo *
792clang_index_getIBOutletCollectionAttrInfo(const CXIdxAttrInfo *AInfo) {
793 if (!AInfo)
794 return nullptr;
795
796 const AttrInfo *DI = static_cast<const AttrInfo *>(AInfo);
797 if (const IBOutletCollectionInfo *
798 IBInfo = dyn_cast<IBOutletCollectionInfo>(Val: DI))
799 return &IBInfo->IBCollInfo;
800
801 return nullptr;
802}
803
804const CXIdxCXXClassDeclInfo *
805clang_index_getCXXClassDeclInfo(const CXIdxDeclInfo *DInfo) {
806 if (!DInfo)
807 return nullptr;
808
809 const DeclInfo *DI = static_cast<const DeclInfo *>(DInfo);
810 if (const CXXClassDeclInfo *ClassInfo = dyn_cast<CXXClassDeclInfo>(Val: DI))
811 return &ClassInfo->CXXClassInfo;
812
813 return nullptr;
814}
815
816CXIdxClientContainer
817clang_index_getClientContainer(const CXIdxContainerInfo *info) {
818 if (!info)
819 return nullptr;
820 const ContainerInfo *Container = static_cast<const ContainerInfo *>(info);
821 return Container->IndexCtx->getClientContainerForDC(DC: Container->DC);
822}
823
824void clang_index_setClientContainer(const CXIdxContainerInfo *info,
825 CXIdxClientContainer client) {
826 if (!info)
827 return;
828 const ContainerInfo *Container = static_cast<const ContainerInfo *>(info);
829 Container->IndexCtx->addContainerInMap(DC: Container->DC, container: client);
830}
831
832CXIdxClientEntity clang_index_getClientEntity(const CXIdxEntityInfo *info) {
833 if (!info)
834 return nullptr;
835 const EntityInfo *Entity = static_cast<const EntityInfo *>(info);
836 return Entity->IndexCtx->getClientEntity(D: Entity->Dcl);
837}
838
839void clang_index_setClientEntity(const CXIdxEntityInfo *info,
840 CXIdxClientEntity client) {
841 if (!info)
842 return;
843 const EntityInfo *Entity = static_cast<const EntityInfo *>(info);
844 Entity->IndexCtx->setClientEntity(D: Entity->Dcl, client);
845}
846
847CXIndexAction clang_IndexAction_create(CXIndex CIdx) {
848 return new IndexSessionData(CIdx);
849}
850
851void clang_IndexAction_dispose(CXIndexAction idxAction) {
852 if (idxAction)
853 delete static_cast<IndexSessionData *>(idxAction);
854}
855
856int clang_indexSourceFile(CXIndexAction idxAction,
857 CXClientData client_data,
858 IndexerCallbacks *index_callbacks,
859 unsigned index_callbacks_size,
860 unsigned index_options,
861 const char *source_filename,
862 const char * const *command_line_args,
863 int num_command_line_args,
864 struct CXUnsavedFile *unsaved_files,
865 unsigned num_unsaved_files,
866 CXTranslationUnit *out_TU,
867 unsigned TU_options) {
868 SmallVector<const char *, 4> Args;
869 Args.push_back(Elt: "clang");
870 Args.append(in_start: command_line_args, in_end: command_line_args + num_command_line_args);
871 return clang_indexSourceFileFullArgv(
872 idxAction, client_data, index_callbacks, index_callbacks_size,
873 index_options, source_filename, command_line_args: Args.data(), num_command_line_args: Args.size(), unsaved_files,
874 num_unsaved_files, out_TU, TU_options);
875}
876
877int clang_indexSourceFileFullArgv(
878 CXIndexAction idxAction, CXClientData client_data,
879 IndexerCallbacks *index_callbacks, unsigned index_callbacks_size,
880 unsigned index_options, const char *source_filename,
881 const char *const *command_line_args, int num_command_line_args,
882 struct CXUnsavedFile *unsaved_files, unsigned num_unsaved_files,
883 CXTranslationUnit *out_TU, unsigned TU_options) {
884 LOG_FUNC_SECTION {
885 *Log << source_filename << ": ";
886 for (int i = 0; i != num_command_line_args; ++i)
887 *Log << command_line_args[i] << " ";
888 }
889
890 if (num_unsaved_files && !unsaved_files)
891 return CXError_InvalidArguments;
892
893 CXErrorCode result = CXError_Failure;
894 auto IndexSourceFileImpl = [=, &result]() {
895 result = clang_indexSourceFile_Impl(
896 cxIdxAction: idxAction, client_data, client_index_callbacks: index_callbacks, index_callbacks_size,
897 index_options, source_filename, command_line_args,
898 num_command_line_args, unsaved_files: llvm::ArrayRef(unsaved_files, num_unsaved_files),
899 out_TU, TU_options);
900 };
901
902 llvm::CrashRecoveryContext CRC;
903
904 if (!RunSafely(CRC, Fn: IndexSourceFileImpl)) {
905 fprintf(stderr, format: "libclang: crash detected during indexing source file: {\n");
906 fprintf(stderr, format: " 'source_filename' : '%s'\n", source_filename);
907 fprintf(stderr, format: " 'command_line_args' : [");
908 for (int i = 0; i != num_command_line_args; ++i) {
909 if (i)
910 fprintf(stderr, format: ", ");
911 fprintf(stderr, format: "'%s'", command_line_args[i]);
912 }
913 fprintf(stderr, format: "],\n");
914 fprintf(stderr, format: " 'unsaved_files' : [");
915 for (unsigned i = 0; i != num_unsaved_files; ++i) {
916 if (i)
917 fprintf(stderr, format: ", ");
918 fprintf(stderr, format: "('%s', '...', %ld)", unsaved_files[i].Filename,
919 unsaved_files[i].Length);
920 }
921 fprintf(stderr, format: "],\n");
922 fprintf(stderr, format: " 'options' : %d,\n", TU_options);
923 fprintf(stderr, format: "}\n");
924
925 return 1;
926 } else if (getenv(name: "LIBCLANG_RESOURCE_USAGE")) {
927 if (out_TU)
928 PrintLibclangResourceUsage(TU: *out_TU);
929 }
930
931 return result;
932}
933
934int clang_indexTranslationUnit(CXIndexAction idxAction,
935 CXClientData client_data,
936 IndexerCallbacks *index_callbacks,
937 unsigned index_callbacks_size,
938 unsigned index_options,
939 CXTranslationUnit TU) {
940 LOG_FUNC_SECTION {
941 *Log << TU;
942 }
943
944 CXErrorCode result;
945 auto IndexTranslationUnitImpl = [=, &result]() {
946 result = clang_indexTranslationUnit_Impl(
947 idxAction, client_data, client_index_callbacks: index_callbacks, index_callbacks_size,
948 index_options, TU);
949 };
950
951 llvm::CrashRecoveryContext CRC;
952
953 if (!RunSafely(CRC, Fn: IndexTranslationUnitImpl)) {
954 fprintf(stderr, format: "libclang: crash detected during indexing TU\n");
955
956 return 1;
957 }
958
959 return result;
960}
961
962void clang_indexLoc_getFileLocation(CXIdxLoc location,
963 CXIdxClientFile *indexFile,
964 CXFile *file,
965 unsigned *line,
966 unsigned *column,
967 unsigned *offset) {
968 if (indexFile) *indexFile = nullptr;
969 if (file) *file = nullptr;
970 if (line) *line = 0;
971 if (column) *column = 0;
972 if (offset) *offset = 0;
973
974 SourceLocation Loc = SourceLocation::getFromRawEncoding(Encoding: location.int_data);
975 if (!location.ptr_data[0] || Loc.isInvalid())
976 return;
977
978 CXIndexDataConsumer &DataConsumer =
979 *static_cast<CXIndexDataConsumer*>(location.ptr_data[0]);
980 DataConsumer.translateLoc(Loc, indexFile, file, line, column, offset);
981}
982
983CXSourceLocation clang_indexLoc_getCXSourceLocation(CXIdxLoc location) {
984 SourceLocation Loc = SourceLocation::getFromRawEncoding(Encoding: location.int_data);
985 if (!location.ptr_data[0] || Loc.isInvalid())
986 return clang_getNullLocation();
987
988 CXIndexDataConsumer &DataConsumer =
989 *static_cast<CXIndexDataConsumer*>(location.ptr_data[0]);
990 return cxloc::translateSourceLocation(Context&: DataConsumer.getASTContext(), Loc);
991}
992