1 //===--- CrossTranslationUnit.h - -------------------------------*- C++ -*-===//
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 //  This file provides an interface to load binary AST dumps on demand. This
10 //  feature can be utilized for tools that require cross translation unit
11 //  support.
12 //
13 //===----------------------------------------------------------------------===//
14 #ifndef LLVM_CLANG_CROSSTU_CROSSTRANSLATIONUNIT_H
15 #define LLVM_CLANG_CROSSTU_CROSSTRANSLATIONUNIT_H
16 
17 #include "clang/AST/ASTImporterSharedState.h"
18 #include "clang/Basic/LLVM.h"
19 #include "llvm/ADT/DenseMap.h"
20 #include "llvm/ADT/Optional.h"
21 #include "llvm/ADT/SmallPtrSet.h"
22 #include "llvm/ADT/StringMap.h"
23 #include "llvm/Support/Error.h"
24 
25 namespace clang {
26 class CompilerInstance;
27 class ASTContext;
28 class ASTImporter;
29 class ASTUnit;
30 class DeclContext;
31 class FunctionDecl;
32 class VarDecl;
33 class NamedDecl;
34 class TranslationUnitDecl;
35 
36 namespace cross_tu {
37 
38 enum class index_error_code {
39   unspecified = 1,
40   missing_index_file,
41   invalid_index_format,
42   multiple_definitions,
43   missing_definition,
44   failed_import,
45   failed_to_get_external_ast,
46   failed_to_generate_usr,
47   triple_mismatch,
48   lang_mismatch,
49   lang_dialect_mismatch,
50   load_threshold_reached
51 };
52 
53 class IndexError : public llvm::ErrorInfo<IndexError> {
54 public:
55   static char ID;
56   IndexError(index_error_code C) : Code(C), LineNo(0) {}
57   IndexError(index_error_code C, std::string FileName, int LineNo = 0)
58       : Code(C), FileName(std::move(FileName)), LineNo(LineNo) {}
59   IndexError(index_error_code C, std::string FileName, std::string TripleToName,
60              std::string TripleFromName)
61       : Code(C), FileName(std::move(FileName)),
62         TripleToName(std::move(TripleToName)),
63         TripleFromName(std::move(TripleFromName)) {}
64   void log(raw_ostream &OS) const override;
65   std::error_code convertToErrorCode() const override;
66   index_error_code getCode() const { return Code; }
67   int getLineNum() const { return LineNo; }
68   std::string getFileName() const { return FileName; }
69   std::string getTripleToName() const { return TripleToName; }
70   std::string getTripleFromName() const { return TripleFromName; }
71 
72 private:
73   index_error_code Code;
74   std::string FileName;
75   int LineNo;
76   std::string TripleToName;
77   std::string TripleFromName;
78 };
79 
80 /// This function parses an index file that determines which
81 ///        translation unit contains which definition.
82 ///
83 /// The index file format is the following:
84 /// each line consists of an USR and a filepath separated by a space.
85 ///
86 /// \return Returns a map where the USR is the key and the filepath is the value
87 ///         or an error.
88 llvm::Expected<llvm::StringMap<std::string>>
89 parseCrossTUIndex(StringRef IndexPath, StringRef CrossTUDir);
90 
91 std::string createCrossTUIndexString(const llvm::StringMap<std::string> &Index);
92 
93 // Returns true if the variable or any field of a record variable is const.
94 bool containsConst(const VarDecl *VD, const ASTContext &ACtx);
95 
96 /// This class is used for tools that requires cross translation
97 ///        unit capability.
98 ///
99 /// This class can load definitions from external AST files.
100 /// The loaded definition will be merged back to the original AST using the
101 /// AST Importer.
102 /// In order to use this class, an index file is required that describes
103 /// the locations of the AST files for each definition.
104 ///
105 /// Note that this class also implements caching.
106 class CrossTranslationUnitContext {
107 public:
108   CrossTranslationUnitContext(CompilerInstance &CI);
109   ~CrossTranslationUnitContext();
110 
111   /// This function loads a function or variable definition from an
112   ///        external AST file and merges it into the original AST.
113   ///
114   /// This method should only be used on functions that have no definitions or
115   /// variables that have no initializer in
116   /// the current translation unit. A function definition with the same
117   /// declaration will be looked up in the index file which should be in the
118   /// \p CrossTUDir directory, called \p IndexName. In case the declaration is
119   /// found in the index the corresponding AST file will be loaded and the
120   /// definition will be merged into the original AST using the AST Importer.
121   ///
122   /// \return The declaration with the definition will be returned.
123   /// If no suitable definition is found in the index file or multiple
124   /// definitions found error will be returned.
125   ///
126   /// Note that the AST files should also be in the \p CrossTUDir.
127   llvm::Expected<const FunctionDecl *>
128   getCrossTUDefinition(const FunctionDecl *FD, StringRef CrossTUDir,
129                        StringRef IndexName, bool DisplayCTUProgress = false);
130   llvm::Expected<const VarDecl *>
131   getCrossTUDefinition(const VarDecl *VD, StringRef CrossTUDir,
132                        StringRef IndexName, bool DisplayCTUProgress = false);
133 
134   /// This function loads a definition from an external AST file.
135   ///
136   /// A definition with the same declaration will be looked up in the
137   /// index file which should be in the \p CrossTUDir directory, called
138   /// \p IndexName. In case the declaration is found in the index the
139   /// corresponding AST file will be loaded. If the number of TUs imported
140   /// reaches \p CTULoadTreshold, no loading is performed.
141   ///
142   /// \return Returns a pointer to the ASTUnit that contains the definition of
143   /// the looked up name or an Error.
144   /// The returned pointer is never a nullptr.
145   ///
146   /// Note that the AST files should also be in the \p CrossTUDir.
147   llvm::Expected<ASTUnit *> loadExternalAST(StringRef LookupName,
148                                             StringRef CrossTUDir,
149                                             StringRef IndexName,
150                                             bool DisplayCTUProgress = false);
151 
152   /// This function merges a definition from a separate AST Unit into
153   ///        the current one which was created by the compiler instance that
154   ///        was passed to the constructor.
155   ///
156   /// \return Returns the resulting definition or an error.
157   llvm::Expected<const FunctionDecl *> importDefinition(const FunctionDecl *FD,
158                                                         ASTUnit *Unit);
159   llvm::Expected<const VarDecl *> importDefinition(const VarDecl *VD,
160                                                    ASTUnit *Unit);
161 
162   /// Get a name to identify a named decl.
163   static llvm::Optional<std::string> getLookupName(const NamedDecl *ND);
164 
165   /// Emit diagnostics for the user for potential configuration errors.
166   void emitCrossTUDiagnostics(const IndexError &IE);
167 
168   /// Determine the original source location in the original TU for an
169   /// imported source location.
170   /// \p ToLoc Source location in the imported-to AST.
171   /// \return Source location in the imported-from AST and the corresponding
172   /// ASTUnit object (the AST was loaded from a file using an internal ASTUnit
173   /// object that is returned here).
174   /// If any error happens (ToLoc is a non-imported source location) empty is
175   /// returned.
176   llvm::Optional<std::pair<SourceLocation /*FromLoc*/, ASTUnit *>>
177   getImportedFromSourceLocation(const clang::SourceLocation &ToLoc) const;
178 
179 private:
180   using ImportedFileIDMap =
181       llvm::DenseMap<FileID, std::pair<FileID, ASTUnit *>>;
182 
183   void lazyInitImporterSharedSt(TranslationUnitDecl *ToTU);
184   ASTImporter &getOrCreateASTImporter(ASTUnit *Unit);
185   template <typename T>
186   llvm::Expected<const T *> getCrossTUDefinitionImpl(const T *D,
187                                                      StringRef CrossTUDir,
188                                                      StringRef IndexName,
189                                                      bool DisplayCTUProgress);
190   template <typename T>
191   const T *findDefInDeclContext(const DeclContext *DC,
192                                 StringRef LookupName);
193   template <typename T>
194   llvm::Expected<const T *> importDefinitionImpl(const T *D, ASTUnit *Unit);
195 
196   using ImporterMapTy =
197       llvm::DenseMap<TranslationUnitDecl *, std::unique_ptr<ASTImporter>>;
198 
199   ImporterMapTy ASTUnitImporterMap;
200 
201   ASTContext &Context;
202   std::shared_ptr<ASTImporterSharedState> ImporterSharedSt;
203   /// Map of imported FileID's (in "To" context) to FileID in "From" context
204   /// and the ASTUnit for the From context.
205   /// This map is used by getImportedFromSourceLocation to lookup a FileID and
206   /// its Preprocessor when knowing only the FileID in the 'To' context. The
207   /// FileID could be imported by any of multiple 'From' ASTImporter objects.
208   /// we do not want to loop over all ASTImporter's to find the one that
209   /// imported the FileID.
210   ImportedFileIDMap ImportedFileIDs;
211 
212   /// Functor for loading ASTUnits from AST-dump files.
213   class ASTFileLoader {
214   public:
215     ASTFileLoader(const CompilerInstance &CI);
216     std::unique_ptr<ASTUnit> operator()(StringRef ASTFilePath);
217 
218   private:
219     const CompilerInstance &CI;
220   };
221 
222   /// Maintain number of AST loads and check for reaching the load limit.
223   class ASTLoadGuard {
224   public:
225     ASTLoadGuard(unsigned Limit) : Limit(Limit) {}
226 
227     /// Indicates, whether a new load operation is permitted, it is within the
228     /// threshold.
229     operator bool() const { return Count < Limit; }
230 
231     /// Tell that a new AST was loaded successfully.
232     void indicateLoadSuccess() { ++Count; }
233 
234   private:
235     /// The number of ASTs actually imported.
236     unsigned Count{0u};
237     /// The limit (threshold) value for number of loaded ASTs.
238     const unsigned Limit;
239   };
240 
241   /// Storage and load of ASTUnits, cached access, and providing searchability
242   /// are the concerns of ASTUnitStorage class.
243   class ASTUnitStorage {
244   public:
245     ASTUnitStorage(const CompilerInstance &CI);
246     /// Loads an ASTUnit for a function.
247     ///
248     /// \param FunctionName USR name of the function.
249     /// \param CrossTUDir Path to the directory used to store CTU related files.
250     /// \param IndexName Name of the file inside \p CrossTUDir which maps
251     /// function USR names to file paths. These files contain the corresponding
252     /// AST-dumps.
253     /// \param DisplayCTUProgress Display a message about loading new ASTs.
254     ///
255     /// \return An Expected instance which contains the ASTUnit pointer or the
256     /// error occured during the load.
257     llvm::Expected<ASTUnit *> getASTUnitForFunction(StringRef FunctionName,
258                                                     StringRef CrossTUDir,
259                                                     StringRef IndexName,
260                                                     bool DisplayCTUProgress);
261     /// Identifies the path of the file which can be used to load the ASTUnit
262     /// for a given function.
263     ///
264     /// \param FunctionName USR name of the function.
265     /// \param CrossTUDir Path to the directory used to store CTU related files.
266     /// \param IndexName Name of the file inside \p CrossTUDir which maps
267     /// function USR names to file paths. These files contain the corresponding
268     /// AST-dumps.
269     ///
270     /// \return An Expected instance containing the filepath.
271     llvm::Expected<std::string> getFileForFunction(StringRef FunctionName,
272                                                    StringRef CrossTUDir,
273                                                    StringRef IndexName);
274 
275   private:
276     llvm::Error ensureCTUIndexLoaded(StringRef CrossTUDir, StringRef IndexName);
277     llvm::Expected<ASTUnit *> getASTUnitForFile(StringRef FileName,
278                                                 bool DisplayCTUProgress);
279 
280     template <typename... T> using BaseMapTy = llvm::StringMap<T...>;
281     using OwningMapTy = BaseMapTy<std::unique_ptr<clang::ASTUnit>>;
282     using NonOwningMapTy = BaseMapTy<clang::ASTUnit *>;
283 
284     OwningMapTy FileASTUnitMap;
285     NonOwningMapTy NameASTUnitMap;
286 
287     using IndexMapTy = BaseMapTy<std::string>;
288     IndexMapTy NameFileMap;
289 
290     ASTFileLoader FileAccessor;
291 
292     /// Limit the number of loaded ASTs. Used to limit the  memory usage of the
293     /// CrossTranslationUnitContext.
294     /// The ASTUnitStorage has the knowledge about if the AST to load is
295     /// actually loaded or returned from cache. This information is needed to
296     /// maintain the counter.
297     ASTLoadGuard LoadGuard;
298   };
299 
300   ASTUnitStorage ASTStorage;
301 
302 };
303 
304 } // namespace cross_tu
305 } // namespace clang
306 
307 #endif // LLVM_CLANG_CROSSTU_CROSSTRANSLATIONUNIT_H
308