Clang Project

clang_source_code/lib/CrossTU/CrossTranslationUnit.cpp
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
30namespace clang {
31namespace cross_tu {
32
33namespace {
34
35#define DEBUG_TYPE "CrossTranslationUnit"
36STATISTIC(NumGetCTUCalled, "The # of getCTUDefinition function called");
37STATISTIC(
38    NumNotInOtherTU,
39    "The # of getCTUDefinition called but the function is not in any other TU");
40STATISTIC(NumGetCTUSuccess,
41          "The # of getCTUDefinition successfully returned the "
42          "requested function's body");
43STATISTIC(NumTripleMismatch, "The # of triple mismatches");
44STATISTIC(NumLangMismatch, "The # of language mismatches");
45STATISTIC(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.
49bool hasEqualKnownFields(const llvm::Triple &Lhsconst 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.
77class IndexErrorCategory : public std::error_category {
78public:
79  const char *name() const noexcept override { return "clang.index"; }
80
81  std::string message(int Conditionconst 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
110static llvm::ManagedStatic<IndexErrorCategory> Category;
111// end anonymous namespace
112
113char IndexError::ID;
114
115void IndexError::log(raw_ostream &OSconst {
116  OS << Category->message(static_cast<int>(Code)) << '\n';
117}
118
119std::error_code IndexError::convertToErrorCode() const {
120  return std::error_code(static_cast<int>(Code), *Category);
121}
122
123llvm::Expected<llvm::StringMap<std::string>>
124parseCrossTUIndex(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
153std::string
154createCrossTUIndexString(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
161CrossTranslationUnitContext::CrossTranslationUnitContext(CompilerInstance &CI)
162    : CI(CI), Context(CI.getASTContext()) {}
163
164CrossTranslationUnitContext::~CrossTranslationUnitContext() {}
165
166std::string CrossTranslationUnitContext::getLookupName(const NamedDecl *ND) {
167  SmallString<128DeclUSR;
168  bool Ret = index::generateUSRForDecl(ND, DeclUSR); (void)Ret;
169   (0) . __assert_fail ("!Ret && \"Unable to generate USR\"", "/home/seafit/code_projects/clang_source/clang/lib/CrossTU/CrossTranslationUnit.cpp", 169, __PRETTY_FUNCTION__))" file_link="../../../include/assert.h.html#88" macro="true">assert(!Ret && "Unable to generate USR");
170  return DeclUSR.str();
171}
172
173/// Recursively visits the function decls of a DeclContext, and looks up a
174/// function based on USRs.
175const FunctionDecl *
176CrossTranslationUnitContext::findFunctionInDeclContext(const DeclContext *DC,
177                                                       StringRef LookupFnName) {
178   (0) . __assert_fail ("DC && \"Declaration Context must not be null\"", "/home/seafit/code_projects/clang_source/clang/lib/CrossTU/CrossTranslationUnit.cpp", 178, __PRETTY_FUNCTION__))" file_link="../../../include/assert.h.html#88" macro="true">assert(DC && "Declaration Context must not be null");
179  for (const Decl *D : DC->decls()) {
180    const auto *SubDC = dyn_cast<DeclContext>(D);
181    if (SubDC)
182      if (const auto *FD = findFunctionInDeclContext(SubDC, LookupFnName))
183        return FD;
184
185    const auto *ND = dyn_cast<FunctionDecl>(D);
186    const FunctionDecl *ResultDecl;
187    if (!ND || !ND->hasBody(ResultDecl))
188      continue;
189    if (getLookupName(ResultDecl) != LookupFnName)
190      continue;
191    return ResultDecl;
192  }
193  return nullptr;
194}
195
196llvm::Expected<const FunctionDecl *>
197CrossTranslationUnitContext::getCrossTUDefinition(const FunctionDecl *FD,
198                                                  StringRef CrossTUDir,
199                                                  StringRef IndexName,
200                                                  bool DisplayCTUProgress) {
201   (0) . __assert_fail ("FD && \"FD is missing, bad call to this function!\"", "/home/seafit/code_projects/clang_source/clang/lib/CrossTU/CrossTranslationUnit.cpp", 201, __PRETTY_FUNCTION__))" file_link="../../../include/assert.h.html#88" macro="true">assert(FD && "FD is missing, bad call to this function!");
202   (0) . __assert_fail ("!FD->hasBody() && \"FD has a definition in current translation unit!\"", "/home/seafit/code_projects/clang_source/clang/lib/CrossTU/CrossTranslationUnit.cpp", 202, __PRETTY_FUNCTION__))" file_link="../../../include/assert.h.html#88" macro="true">assert(!FD->hasBody() && "FD has a definition in current translation unit!");
203  ++NumGetCTUCalled;
204  const std::string LookupFnName = getLookupName(FD);
205  if (LookupFnName.empty())
206    return llvm::make_error<IndexError>(
207        index_error_code::failed_to_generate_usr);
208  llvm::Expected<ASTUnit *> ASTUnitOrError =
209      loadExternalAST(LookupFnName, CrossTUDir, IndexName, DisplayCTUProgress);
210  if (!ASTUnitOrError)
211    return ASTUnitOrError.takeError();
212  ASTUnit *Unit = *ASTUnitOrError;
213  getFileManager() == &Unit->getASTContext().getSourceManager().getFileManager()", "/home/seafit/code_projects/clang_source/clang/lib/CrossTU/CrossTranslationUnit.cpp", 214, __PRETTY_FUNCTION__))" file_link="../../../include/assert.h.html#88" macro="true">assert(&Unit->getFileManager() ==
214getFileManager() == &Unit->getASTContext().getSourceManager().getFileManager()", "/home/seafit/code_projects/clang_source/clang/lib/CrossTU/CrossTranslationUnit.cpp", 214, __PRETTY_FUNCTION__))" file_link="../../../include/assert.h.html#88" macro="true">         &Unit->getASTContext().getSourceManager().getFileManager());
215
216  const llvm::Triple &TripleTo = Context.getTargetInfo().getTriple();
217  const llvm::Triple &TripleFrom =
218      Unit->getASTContext().getTargetInfo().getTriple();
219  // The imported AST had been generated for a different target.
220  // Some parts of the triple in the loaded ASTContext can be unknown while the
221  // very same parts in the target ASTContext are known. Thus we check for the
222  // known parts only.
223  if (!hasEqualKnownFields(TripleToTripleFrom)) {
224    // TODO: Pass the SourceLocation of the CallExpression for more precise
225    // diagnostics.
226    ++NumTripleMismatch;
227    return llvm::make_error<IndexError>(index_error_code::triple_mismatch,
228                                        Unit->getMainFileName(), TripleTo.str(),
229                                        TripleFrom.str());
230  }
231
232  const auto &LangTo = Context.getLangOpts();
233  const auto &LangFrom = Unit->getASTContext().getLangOpts();
234
235  // FIXME: Currenty we do not support CTU across C++ and C and across
236  // different dialects of C++.
237  if (LangTo.CPlusPlus != LangFrom.CPlusPlus) {
238    ++NumLangMismatch;
239    return llvm::make_error<IndexError>(index_error_code::lang_mismatch);
240  }
241
242  // If CPP dialects are different then return with error.
243  //
244  // Consider this STL code:
245  //   template<typename _Alloc>
246  //     struct __alloc_traits
247  //   #if __cplusplus >= 201103L
248  //     : std::allocator_traits<_Alloc>
249  //   #endif
250  //     { // ...
251  //     };
252  // This class template would create ODR errors during merging the two units,
253  // since in one translation unit the class template has a base class, however
254  // in the other unit it has none.
255  if (LangTo.CPlusPlus11 != LangFrom.CPlusPlus11 ||
256      LangTo.CPlusPlus14 != LangFrom.CPlusPlus14 ||
257      LangTo.CPlusPlus17 != LangFrom.CPlusPlus17 ||
258      LangTo.CPlusPlus2a != LangFrom.CPlusPlus2a) {
259    ++NumLangDialectMismatch;
260    return llvm::make_error<IndexError>(
261        index_error_code::lang_dialect_mismatch);
262  }
263
264  TranslationUnitDecl *TU = Unit->getASTContext().getTranslationUnitDecl();
265  if (const FunctionDecl *ResultDecl =
266          findFunctionInDeclContext(TU, LookupFnName))
267    return importDefinition(ResultDecl);
268  return llvm::make_error<IndexError>(index_error_code::failed_import);
269}
270
271void CrossTranslationUnitContext::emitCrossTUDiagnostics(const IndexError &IE) {
272  switch (IE.getCode()) {
273  case index_error_code::missing_index_file:
274    Context.getDiagnostics().Report(diag::err_ctu_error_opening)
275        << IE.getFileName();
276    break;
277  case index_error_code::invalid_index_format:
278    Context.getDiagnostics().Report(diag::err_extdefmap_parsing)
279        << IE.getFileName() << IE.getLineNum();
280    break;
281  case index_error_code::multiple_definitions:
282    Context.getDiagnostics().Report(diag::err_multiple_def_index)
283        << IE.getLineNum();
284    break;
285  case index_error_code::triple_mismatch:
286    Context.getDiagnostics().Report(diag::warn_ctu_incompat_triple)
287        << IE.getFileName() << IE.getTripleToName() << IE.getTripleFromName();
288    break;
289  default:
290    break;
291  }
292}
293
294llvm::Expected<ASTUnit *> CrossTranslationUnitContext::loadExternalAST(
295    StringRef LookupNameStringRef CrossTUDirStringRef IndexName,
296    bool DisplayCTUProgress) {
297  // FIXME: The current implementation only supports loading functions with
298  //        a lookup name from a single translation unit. If multiple
299  //        translation units contains functions with the same lookup name an
300  //        error will be returned.
301  ASTUnit *Unit = nullptr;
302  auto FnUnitCacheEntry = FunctionASTUnitMap.find(LookupName);
303  if (FnUnitCacheEntry == FunctionASTUnitMap.end()) {
304    if (FunctionFileMap.empty()) {
305      SmallString<256IndexFile = CrossTUDir;
306      if (llvm::sys::path::is_absolute(IndexName))
307        IndexFile = IndexName;
308      else
309        llvm::sys::path::append(IndexFile, IndexName);
310      llvm::Expected<llvm::StringMap<std::string>> IndexOrErr =
311          parseCrossTUIndex(IndexFile, CrossTUDir);
312      if (IndexOrErr)
313        FunctionFileMap = *IndexOrErr;
314      else
315        return IndexOrErr.takeError();
316    }
317
318    auto It = FunctionFileMap.find(LookupName);
319    if (It == FunctionFileMap.end()) {
320      ++NumNotInOtherTU;
321      return llvm::make_error<IndexError>(index_error_code::missing_definition);
322    }
323    StringRef ASTFileName = It->second;
324    auto ASTCacheEntry = FileASTUnitMap.find(ASTFileName);
325    if (ASTCacheEntry == FileASTUnitMap.end()) {
326      IntrusiveRefCntPtr<DiagnosticOptionsDiagOpts = new DiagnosticOptions();
327      TextDiagnosticPrinter *DiagClient =
328          new TextDiagnosticPrinter(llvm::errs(), &*DiagOpts);
329      IntrusiveRefCntPtr<DiagnosticIDsDiagID(new DiagnosticIDs());
330      IntrusiveRefCntPtr<DiagnosticsEngineDiags(
331          new DiagnosticsEngine(DiagID, &*DiagOpts, DiagClient));
332
333      std::unique_ptr<ASTUnitLoadedUnit(ASTUnit::LoadFromASTFile(
334          ASTFileName, CI.getPCHContainerOperations()->getRawReader(),
335          ASTUnit::LoadEverything, Diags, CI.getFileSystemOpts()));
336      Unit = LoadedUnit.get();
337      FileASTUnitMap[ASTFileName] = std::move(LoadedUnit);
338      if (DisplayCTUProgress) {
339        llvm::errs() << "CTU loaded AST file: "
340                     << ASTFileName << "\n";
341      }
342    } else {
343      Unit = ASTCacheEntry->second.get();
344    }
345    FunctionASTUnitMap[LookupName] = Unit;
346  } else {
347    Unit = FnUnitCacheEntry->second;
348  }
349  if (!Unit)
350    return llvm::make_error<IndexError>(
351        index_error_code::failed_to_get_external_ast);
352  return Unit;
353}
354
355llvm::Expected<const FunctionDecl *>
356CrossTranslationUnitContext::importDefinition(const FunctionDecl *FD) {
357   (0) . __assert_fail ("FD->hasBody() && \"Functions to be imported should have body.\"", "/home/seafit/code_projects/clang_source/clang/lib/CrossTU/CrossTranslationUnit.cpp", 357, __PRETTY_FUNCTION__))" file_link="../../../include/assert.h.html#88" macro="true">assert(FD->hasBody() && "Functions to be imported should have body.");
358
359  ASTImporter &Importer = getOrCreateASTImporter(FD->getASTContext());
360  auto *ToDecl =
361      cast_or_null<FunctionDecl>(Importer.Import(const_cast<FunctionDecl *>(FD)));
362  if (!ToDecl)
363    return llvm::make_error<IndexError>(index_error_code::failed_import);
364  hasBody()", "/home/seafit/code_projects/clang_source/clang/lib/CrossTU/CrossTranslationUnit.cpp", 364, __PRETTY_FUNCTION__))" file_link="../../../include/assert.h.html#88" macro="true">assert(ToDecl->hasBody());
365   (0) . __assert_fail ("FD->hasBody() && \"Functions already imported should have body.\"", "/home/seafit/code_projects/clang_source/clang/lib/CrossTU/CrossTranslationUnit.cpp", 365, __PRETTY_FUNCTION__))" file_link="../../../include/assert.h.html#88" macro="true">assert(FD->hasBody() && "Functions already imported should have body.");
366  ++NumGetCTUSuccess;
367  return ToDecl;
368}
369
370void CrossTranslationUnitContext::lazyInitLookupTable(
371    TranslationUnitDecl *ToTU) {
372  if (!LookupTable)
373    LookupTable = llvm::make_unique<ASTImporterLookupTable>(*ToTU);
374}
375
376ASTImporter &
377CrossTranslationUnitContext::getOrCreateASTImporter(ASTContext &From) {
378  auto I = ASTUnitImporterMap.find(From.getTranslationUnitDecl());
379  if (I != ASTUnitImporterMap.end())
380    return *I->second;
381  lazyInitLookupTable(Context.getTranslationUnitDecl());
382  ASTImporter *NewImporter = new ASTImporter(
383      ContextContext.getSourceManager().getFileManager(), From,
384      From.getSourceManager().getFileManager(), falseLookupTable.get());
385  ASTUnitImporterMap[From.getTranslationUnitDecl()].reset(NewImporter);
386  return *NewImporter;
387}
388
389// namespace cross_tu
390// namespace clang
391
clang::cross_tu::IndexError::ID
clang::cross_tu::IndexError::log
clang::cross_tu::IndexError::convertToErrorCode
clang::cross_tu::CrossTranslationUnitContext::getLookupName
clang::cross_tu::CrossTranslationUnitContext::findFunctionInDeclContext
clang::cross_tu::CrossTranslationUnitContext::getCrossTUDefinition
clang::cross_tu::CrossTranslationUnitContext::emitCrossTUDiagnostics
clang::cross_tu::CrossTranslationUnitContext::loadExternalAST
clang::cross_tu::CrossTranslationUnitContext::importDefinition
clang::cross_tu::CrossTranslationUnitContext::lazyInitLookupTable
clang::cross_tu::CrossTranslationUnitContext::getOrCreateASTImporter