1 //===--- CrossTranslationUnit.cpp - -----------------------------*- 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 implements the CrossTranslationUnit interface.
10 //
11 //===----------------------------------------------------------------------===//
12 #include "clang/CrossTU/CrossTranslationUnit.h"
13 #include "clang/AST/ASTImporter.h"
14 #include "clang/AST/Decl.h"
15 #include "clang/Basic/TargetInfo.h"
16 #include "clang/CrossTU/CrossTUDiagnostic.h"
17 #include "clang/Frontend/ASTUnit.h"
18 #include "clang/Frontend/CompilerInstance.h"
19 #include "clang/Frontend/TextDiagnosticPrinter.h"
20 #include "clang/Index/USRGeneration.h"
21 #include "llvm/ADT/Triple.h"
22 #include "llvm/ADT/Statistic.h"
23 #include "llvm/Support/ErrorHandling.h"
24 #include "llvm/Support/ManagedStatic.h"
25 #include "llvm/Support/Path.h"
26 #include "llvm/Support/raw_ostream.h"
27 #include <fstream>
28 #include <sstream>
29 
30 namespace clang {
31 namespace cross_tu {
32 
33 namespace {
34 
35 #define DEBUG_TYPE "CrossTranslationUnit"
36 STATISTIC(NumGetCTUCalled, "The # of getCTUDefinition function called");
37 STATISTIC(
38     NumNotInOtherTU,
39     "The # of getCTUDefinition called but the function is not in any other TU");
40 STATISTIC(NumGetCTUSuccess,
41           "The # of getCTUDefinition successfully returned the "
42           "requested function's body");
43 STATISTIC(NumTripleMismatch, "The # of triple mismatches");
44 STATISTIC(NumLangMismatch, "The # of language mismatches");
45 STATISTIC(NumLangDialectMismatch, "The # of language dialect mismatches");
46 
47 // Same as Triple's equality operator, but we check a field only if that is
48 // known in both instances.
49 bool hasEqualKnownFields(const llvm::Triple &Lhs, const llvm::Triple &Rhs) {
50   using llvm::Triple;
51   if (Lhs.getArch() != Triple::UnknownArch &&
52       Rhs.getArch() != Triple::UnknownArch && Lhs.getArch() != Rhs.getArch())
53     return false;
54   if (Lhs.getSubArch() != Triple::NoSubArch &&
55       Rhs.getSubArch() != Triple::NoSubArch &&
56       Lhs.getSubArch() != Rhs.getSubArch())
57     return false;
58   if (Lhs.getVendor() != Triple::UnknownVendor &&
59       Rhs.getVendor() != Triple::UnknownVendor &&
60       Lhs.getVendor() != Rhs.getVendor())
61     return false;
62   if (!Lhs.isOSUnknown() && !Rhs.isOSUnknown() &&
63       Lhs.getOS() != Rhs.getOS())
64     return false;
65   if (Lhs.getEnvironment() != Triple::UnknownEnvironment &&
66       Rhs.getEnvironment() != Triple::UnknownEnvironment &&
67       Lhs.getEnvironment() != Rhs.getEnvironment())
68     return false;
69   if (Lhs.getObjectFormat() != Triple::UnknownObjectFormat &&
70       Rhs.getObjectFormat() != Triple::UnknownObjectFormat &&
71       Lhs.getObjectFormat() != Rhs.getObjectFormat())
72     return false;
73   return true;
74 }
75 
76 // FIXME: This class is will be removed after the transition to llvm::Error.
77 class IndexErrorCategory : public std::error_category {
78 public:
79   const char *name() const noexcept override { return "clang.index"; }
80 
81   std::string message(int Condition) const override {
82     switch (static_cast<index_error_code>(Condition)) {
83     case index_error_code::unspecified:
84       return "An unknown error has occurred.";
85     case index_error_code::missing_index_file:
86       return "The index file is missing.";
87     case index_error_code::invalid_index_format:
88       return "Invalid index file format.";
89     case index_error_code::multiple_definitions:
90       return "Multiple definitions in the index file.";
91     case index_error_code::missing_definition:
92       return "Missing definition from the index file.";
93     case index_error_code::failed_import:
94       return "Failed to import the definition.";
95     case index_error_code::failed_to_get_external_ast:
96       return "Failed to load external AST source.";
97     case index_error_code::failed_to_generate_usr:
98       return "Failed to generate USR.";
99     case index_error_code::triple_mismatch:
100       return "Triple mismatch";
101     case index_error_code::lang_mismatch:
102       return "Language mismatch";
103     case index_error_code::lang_dialect_mismatch:
104       return "Language dialect mismatch";
105     }
106     llvm_unreachable("Unrecognized index_error_code.");
107   }
108 };
109 
110 static llvm::ManagedStatic<IndexErrorCategory> Category;
111 } // end anonymous namespace
112 
113 char IndexError::ID;
114 
115 void IndexError::log(raw_ostream &OS) const {
116   OS << Category->message(static_cast<int>(Code)) << '\n';
117 }
118 
119 std::error_code IndexError::convertToErrorCode() const {
120   return std::error_code(static_cast<int>(Code), *Category);
121 }
122 
123 llvm::Expected<llvm::StringMap<std::string>>
124 parseCrossTUIndex(StringRef IndexPath, StringRef CrossTUDir) {
125   std::ifstream ExternalMapFile(IndexPath);
126   if (!ExternalMapFile)
127     return llvm::make_error<IndexError>(index_error_code::missing_index_file,
128                                         IndexPath.str());
129 
130   llvm::StringMap<std::string> Result;
131   std::string Line;
132   unsigned LineNo = 1;
133   while (std::getline(ExternalMapFile, Line)) {
134     const size_t Pos = Line.find(" ");
135     if (Pos > 0 && Pos != std::string::npos) {
136       StringRef LineRef{Line};
137       StringRef LookupName = LineRef.substr(0, Pos);
138       if (Result.count(LookupName))
139         return llvm::make_error<IndexError>(
140             index_error_code::multiple_definitions, IndexPath.str(), LineNo);
141       StringRef FileName = LineRef.substr(Pos + 1);
142       SmallString<256> FilePath = CrossTUDir;
143       llvm::sys::path::append(FilePath, FileName);
144       Result[LookupName] = FilePath.str().str();
145     } else
146       return llvm::make_error<IndexError>(
147           index_error_code::invalid_index_format, IndexPath.str(), LineNo);
148     LineNo++;
149   }
150   return Result;
151 }
152 
153 std::string
154 createCrossTUIndexString(const llvm::StringMap<std::string> &Index) {
155   std::ostringstream Result;
156   for (const auto &E : Index)
157     Result << E.getKey().str() << " " << E.getValue() << '\n';
158   return Result.str();
159 }
160 
161 bool containsConst(const VarDecl *VD, const ASTContext &ACtx) {
162   CanQualType CT = ACtx.getCanonicalType(VD->getType());
163   if (!CT.isConstQualified()) {
164     const RecordType *RTy = CT->getAs<RecordType>();
165     if (!RTy || !RTy->hasConstFields())
166       return false;
167   }
168   return true;
169 }
170 
171 static bool hasBodyOrInit(const FunctionDecl *D, const FunctionDecl *&DefD) {
172   return D->hasBody(DefD);
173 }
174 static bool hasBodyOrInit(const VarDecl *D, const VarDecl *&DefD) {
175   return D->getAnyInitializer(DefD);
176 }
177 template <typename T> static bool hasBodyOrInit(const T *D) {
178   const T *Unused;
179   return hasBodyOrInit(D, Unused);
180 }
181 
182 CrossTranslationUnitContext::CrossTranslationUnitContext(CompilerInstance &CI)
183     : CI(CI), Context(CI.getASTContext()) {}
184 
185 CrossTranslationUnitContext::~CrossTranslationUnitContext() {}
186 
187 std::string CrossTranslationUnitContext::getLookupName(const NamedDecl *ND) {
188   SmallString<128> DeclUSR;
189   bool Ret = index::generateUSRForDecl(ND, DeclUSR);
190   (void)Ret;
191   assert(!Ret && "Unable to generate USR");
192   return DeclUSR.str();
193 }
194 
195 /// Recursively visits the decls of a DeclContext, and returns one with the
196 /// given USR.
197 template <typename T>
198 const T *
199 CrossTranslationUnitContext::findDefInDeclContext(const DeclContext *DC,
200                                                   StringRef LookupName) {
201   assert(DC && "Declaration Context must not be null");
202   for (const Decl *D : DC->decls()) {
203     const auto *SubDC = dyn_cast<DeclContext>(D);
204     if (SubDC)
205       if (const auto *ND = findDefInDeclContext<T>(SubDC, LookupName))
206         return ND;
207 
208     const auto *ND = dyn_cast<T>(D);
209     const T *ResultDecl;
210     if (!ND || !hasBodyOrInit(ND, ResultDecl))
211       continue;
212     if (getLookupName(ResultDecl) != LookupName)
213       continue;
214     return ResultDecl;
215   }
216   return nullptr;
217 }
218 
219 template <typename T>
220 llvm::Expected<const T *> CrossTranslationUnitContext::getCrossTUDefinitionImpl(
221     const T *D, StringRef CrossTUDir, StringRef IndexName,
222     bool DisplayCTUProgress) {
223   assert(D && "D is missing, bad call to this function!");
224   assert(!hasBodyOrInit(D) &&
225          "D has a body or init in current translation unit!");
226   ++NumGetCTUCalled;
227   const std::string LookupName = getLookupName(D);
228   if (LookupName.empty())
229     return llvm::make_error<IndexError>(
230         index_error_code::failed_to_generate_usr);
231   llvm::Expected<ASTUnit *> ASTUnitOrError =
232       loadExternalAST(LookupName, CrossTUDir, IndexName, DisplayCTUProgress);
233   if (!ASTUnitOrError)
234     return ASTUnitOrError.takeError();
235   ASTUnit *Unit = *ASTUnitOrError;
236   assert(&Unit->getFileManager() ==
237          &Unit->getASTContext().getSourceManager().getFileManager());
238 
239   const llvm::Triple &TripleTo = Context.getTargetInfo().getTriple();
240   const llvm::Triple &TripleFrom =
241       Unit->getASTContext().getTargetInfo().getTriple();
242   // The imported AST had been generated for a different target.
243   // Some parts of the triple in the loaded ASTContext can be unknown while the
244   // very same parts in the target ASTContext are known. Thus we check for the
245   // known parts only.
246   if (!hasEqualKnownFields(TripleTo, TripleFrom)) {
247     // TODO: Pass the SourceLocation of the CallExpression for more precise
248     // diagnostics.
249     ++NumTripleMismatch;
250     return llvm::make_error<IndexError>(index_error_code::triple_mismatch,
251                                         Unit->getMainFileName(), TripleTo.str(),
252                                         TripleFrom.str());
253   }
254 
255   const auto &LangTo = Context.getLangOpts();
256   const auto &LangFrom = Unit->getASTContext().getLangOpts();
257 
258   // FIXME: Currenty we do not support CTU across C++ and C and across
259   // different dialects of C++.
260   if (LangTo.CPlusPlus != LangFrom.CPlusPlus) {
261     ++NumLangMismatch;
262     return llvm::make_error<IndexError>(index_error_code::lang_mismatch);
263   }
264 
265   // If CPP dialects are different then return with error.
266   //
267   // Consider this STL code:
268   //   template<typename _Alloc>
269   //     struct __alloc_traits
270   //   #if __cplusplus >= 201103L
271   //     : std::allocator_traits<_Alloc>
272   //   #endif
273   //     { // ...
274   //     };
275   // This class template would create ODR errors during merging the two units,
276   // since in one translation unit the class template has a base class, however
277   // in the other unit it has none.
278   if (LangTo.CPlusPlus11 != LangFrom.CPlusPlus11 ||
279       LangTo.CPlusPlus14 != LangFrom.CPlusPlus14 ||
280       LangTo.CPlusPlus17 != LangFrom.CPlusPlus17 ||
281       LangTo.CPlusPlus2a != LangFrom.CPlusPlus2a) {
282     ++NumLangDialectMismatch;
283     return llvm::make_error<IndexError>(
284         index_error_code::lang_dialect_mismatch);
285   }
286 
287   TranslationUnitDecl *TU = Unit->getASTContext().getTranslationUnitDecl();
288   if (const T *ResultDecl = findDefInDeclContext<T>(TU, LookupName))
289     return importDefinition(ResultDecl);
290   return llvm::make_error<IndexError>(index_error_code::failed_import);
291 }
292 
293 llvm::Expected<const FunctionDecl *>
294 CrossTranslationUnitContext::getCrossTUDefinition(const FunctionDecl *FD,
295                                                   StringRef CrossTUDir,
296                                                   StringRef IndexName,
297                                                   bool DisplayCTUProgress) {
298   return getCrossTUDefinitionImpl(FD, CrossTUDir, IndexName,
299                                   DisplayCTUProgress);
300 }
301 
302 llvm::Expected<const VarDecl *>
303 CrossTranslationUnitContext::getCrossTUDefinition(const VarDecl *VD,
304                                                   StringRef CrossTUDir,
305                                                   StringRef IndexName,
306                                                   bool DisplayCTUProgress) {
307   return getCrossTUDefinitionImpl(VD, CrossTUDir, IndexName,
308                                   DisplayCTUProgress);
309 }
310 
311 void CrossTranslationUnitContext::emitCrossTUDiagnostics(const IndexError &IE) {
312   switch (IE.getCode()) {
313   case index_error_code::missing_index_file:
314     Context.getDiagnostics().Report(diag::err_ctu_error_opening)
315         << IE.getFileName();
316     break;
317   case index_error_code::invalid_index_format:
318     Context.getDiagnostics().Report(diag::err_extdefmap_parsing)
319         << IE.getFileName() << IE.getLineNum();
320     break;
321   case index_error_code::multiple_definitions:
322     Context.getDiagnostics().Report(diag::err_multiple_def_index)
323         << IE.getLineNum();
324     break;
325   case index_error_code::triple_mismatch:
326     Context.getDiagnostics().Report(diag::warn_ctu_incompat_triple)
327         << IE.getFileName() << IE.getTripleToName() << IE.getTripleFromName();
328     break;
329   default:
330     break;
331   }
332 }
333 
334 llvm::Expected<ASTUnit *> CrossTranslationUnitContext::loadExternalAST(
335     StringRef LookupName, StringRef CrossTUDir, StringRef IndexName,
336     bool DisplayCTUProgress) {
337   // FIXME: The current implementation only supports loading decls with
338   //        a lookup name from a single translation unit. If multiple
339   //        translation units contains decls with the same lookup name an
340   //        error will be returned.
341   ASTUnit *Unit = nullptr;
342   auto NameUnitCacheEntry = NameASTUnitMap.find(LookupName);
343   if (NameUnitCacheEntry == NameASTUnitMap.end()) {
344     if (NameFileMap.empty()) {
345       SmallString<256> IndexFile = CrossTUDir;
346       if (llvm::sys::path::is_absolute(IndexName))
347         IndexFile = IndexName;
348       else
349         llvm::sys::path::append(IndexFile, IndexName);
350       llvm::Expected<llvm::StringMap<std::string>> IndexOrErr =
351           parseCrossTUIndex(IndexFile, CrossTUDir);
352       if (IndexOrErr)
353         NameFileMap = *IndexOrErr;
354       else
355         return IndexOrErr.takeError();
356     }
357 
358     auto It = NameFileMap.find(LookupName);
359     if (It == NameFileMap.end()) {
360       ++NumNotInOtherTU;
361       return llvm::make_error<IndexError>(index_error_code::missing_definition);
362     }
363     StringRef ASTFileName = It->second;
364     auto ASTCacheEntry = FileASTUnitMap.find(ASTFileName);
365     if (ASTCacheEntry == FileASTUnitMap.end()) {
366       IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts = new DiagnosticOptions();
367       TextDiagnosticPrinter *DiagClient =
368           new TextDiagnosticPrinter(llvm::errs(), &*DiagOpts);
369       IntrusiveRefCntPtr<DiagnosticIDs> DiagID(new DiagnosticIDs());
370       IntrusiveRefCntPtr<DiagnosticsEngine> Diags(
371           new DiagnosticsEngine(DiagID, &*DiagOpts, DiagClient));
372 
373       std::unique_ptr<ASTUnit> LoadedUnit(ASTUnit::LoadFromASTFile(
374           ASTFileName, CI.getPCHContainerOperations()->getRawReader(),
375           ASTUnit::LoadEverything, Diags, CI.getFileSystemOpts()));
376       Unit = LoadedUnit.get();
377       FileASTUnitMap[ASTFileName] = std::move(LoadedUnit);
378       if (DisplayCTUProgress) {
379         llvm::errs() << "CTU loaded AST file: "
380                      << ASTFileName << "\n";
381       }
382     } else {
383       Unit = ASTCacheEntry->second.get();
384     }
385     NameASTUnitMap[LookupName] = Unit;
386   } else {
387     Unit = NameUnitCacheEntry->second;
388   }
389   if (!Unit)
390     return llvm::make_error<IndexError>(
391         index_error_code::failed_to_get_external_ast);
392   return Unit;
393 }
394 
395 template <typename T>
396 llvm::Expected<const T *>
397 CrossTranslationUnitContext::importDefinitionImpl(const T *D) {
398   assert(hasBodyOrInit(D) && "Decls to be imported should have body or init.");
399 
400   ASTImporter &Importer = getOrCreateASTImporter(D->getASTContext());
401   auto ToDeclOrError = Importer.Import(D);
402   if (!ToDeclOrError) {
403     handleAllErrors(ToDeclOrError.takeError(),
404                     [&](const ImportError &IE) {
405                       switch (IE.Error) {
406                       case ImportError::NameConflict:
407                         // FIXME: Add statistic.
408                          break;
409                       case ImportError::UnsupportedConstruct:
410                         // FIXME: Add statistic.
411                         break;
412                       case ImportError::Unknown:
413                         llvm_unreachable("Unknown import error happened.");
414                         break;
415                       }
416                     });
417     return llvm::make_error<IndexError>(index_error_code::failed_import);
418   }
419   auto *ToDecl = cast<T>(*ToDeclOrError);
420   assert(hasBodyOrInit(ToDecl) && "Imported Decl should have body or init.");
421   ++NumGetCTUSuccess;
422 
423   return ToDecl;
424 }
425 
426 llvm::Expected<const FunctionDecl *>
427 CrossTranslationUnitContext::importDefinition(const FunctionDecl *FD) {
428   return importDefinitionImpl(FD);
429 }
430 
431 llvm::Expected<const VarDecl *>
432 CrossTranslationUnitContext::importDefinition(const VarDecl *VD) {
433   return importDefinitionImpl(VD);
434 }
435 
436 void CrossTranslationUnitContext::lazyInitLookupTable(
437     TranslationUnitDecl *ToTU) {
438   if (!LookupTable)
439     LookupTable = llvm::make_unique<ASTImporterLookupTable>(*ToTU);
440 }
441 
442 ASTImporter &
443 CrossTranslationUnitContext::getOrCreateASTImporter(ASTContext &From) {
444   auto I = ASTUnitImporterMap.find(From.getTranslationUnitDecl());
445   if (I != ASTUnitImporterMap.end())
446     return *I->second;
447   lazyInitLookupTable(Context.getTranslationUnitDecl());
448   ASTImporter *NewImporter = new ASTImporter(
449       Context, Context.getSourceManager().getFileManager(), From,
450       From.getSourceManager().getFileManager(), false, LookupTable.get());
451   ASTUnitImporterMap[From.getTranslationUnitDecl()].reset(NewImporter);
452   return *NewImporter;
453 }
454 
455 } // namespace cross_tu
456 } // namespace clang
457