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