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