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