clang  9.0.0svn
CrossTranslationUnit.cpp
Go to the documentation of this file.
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 //===----------------------------------------------------------------------===//
13 #include "clang/AST/ASTImporter.h"
14 #include "clang/AST/Decl.h"
15 #include "clang/Basic/TargetInfo.h"
17 #include "clang/Frontend/ASTUnit.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)) {
84  return "An unknown error has occurred.";
86  return "The index file is missing.";
88  return "Invalid index file format.";
90  return "Multiple definitions in the index file.";
92  return "Missing definition from the index file.";
94  return "Failed to import the definition.";
96  return "Failed to load external AST source.";
98  return "Failed to generate USR.";
100  return "Triple mismatch";
102  return "Language 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 
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 
183  : CI(CI), Context(CI.getASTContext()) {}
184 
186 
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>(
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() ==
238 
239  const llvm::Triple &TripleTo = Context.getTargetInfo().getTriple();
240  const llvm::Triple &TripleFrom =
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>(
285  }
286 
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 
295  StringRef CrossTUDir,
296  StringRef IndexName,
297  bool DisplayCTUProgress) {
298  return getCrossTUDefinitionImpl(FD, CrossTUDir, IndexName,
299  DisplayCTUProgress);
300 }
301 
304  StringRef CrossTUDir,
305  StringRef IndexName,
306  bool DisplayCTUProgress) {
307  return getCrossTUDefinitionImpl(VD, CrossTUDir, IndexName,
308  DisplayCTUProgress);
309 }
310 
312  switch (IE.getCode()) {
314  Context.getDiagnostics().Report(diag::err_ctu_error_opening)
315  << IE.getFileName();
316  break;
318  Context.getDiagnostics().Report(diag::err_extdefmap_parsing)
319  << IE.getFileName() << IE.getLineNum();
320  break;
322  Context.getDiagnostics().Report(diag::err_multiple_def_index)
323  << IE.getLineNum();
324  break;
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 
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);
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()) {
367  TextDiagnosticPrinter *DiagClient =
368  new TextDiagnosticPrinter(llvm::errs(), &*DiagOpts);
371  new DiagnosticsEngine(DiagID, &*DiagOpts, DiagClient));
372 
373  std::unique_ptr<ASTUnit> LoadedUnit(ASTUnit::LoadFromASTFile(
374  ASTFileName, CI.getPCHContainerOperations()->getRawReader(),
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>(
392  return Unit;
393 }
394 
395 template <typename 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) {
407  // FIXME: Add statistic.
408  break;
410  // FIXME: Add statistic.
411  break;
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 
428  return importDefinitionImpl(FD);
429 }
430 
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
Represents a function declaration or definition.
Definition: Decl.h:1743
bool isConstQualified() const
const llvm::Triple & getTriple() const
Returns the target triple of the primary target.
Definition: TargetInfo.h:983
Load everything, including Sema.
Definition: ASTUnit.h:675
std::shared_ptr< PCHContainerOperations > getPCHContainerOperations() const
Decl - This represents one declaration (or definition), e.g.
Definition: DeclBase.h:88
const FileManager & getFileManager() const
Definition: ASTUnit.h:473
llvm::Expected< ASTUnit * > loadExternalAST(StringRef LookupName, StringRef CrossTUDir, StringRef IndexName, bool DisplayCTUProgress=false)
This function loads a definition from an external AST file.
void log(raw_ostream &OS) const override
DiagnosticsEngine & getDiagnostics() const
DiagnosticBuilder Report(SourceLocation Loc, unsigned DiagID)
Issue the message to the client.
Definition: Diagnostic.h:1296
const TargetInfo & getTargetInfo() const
Definition: ASTContext.h:690
static std::unique_ptr< ASTUnit > LoadFromASTFile(const std::string &Filename, const PCHContainerReader &PCHContainerRdr, WhatToLoad ToLoad, IntrusiveRefCntPtr< DiagnosticsEngine > Diags, const FileSystemOptions &FileSystemOpts, bool UseDebugInfo=false, bool OnlyLocalDecls=false, ArrayRef< RemappedFile > RemappedFiles=None, bool CaptureDiagnostics=false, bool AllowPCHWithCompilerErrors=false, bool UserFilesAreVolatile=false)
Create a ASTUnit from an AST file.
Definition: ASTUnit.cpp:732
const Expr * getAnyInitializer() const
Get the initializer for this variable, no matter which declaration it is attached to...
Definition: Decl.h:1209
Not supported node or case.
Definition: ASTImporter.h:55
Represents a variable declaration or definition.
Definition: Decl.h:812
decl_range decls() const
decls_begin/decls_end - Iterate over the declarations stored in this context.
Definition: DeclBase.h:2012
FileManager & getFileManager() const
Holds long-lived AST nodes (such as types and decls) that can be referred to throughout the semantic ...
Definition: ASTContext.h:154
Utility class for loading a ASTContext from an AST file.
Definition: ASTUnit.h:86
int Category
Definition: Format.cpp:1710
StringRef getMainFileName() const
Definition: ASTUnit.cpp:1437
Concrete class used by the front-end to report problems and issues.
Definition: Diagnostic.h:148
bool containsConst(const VarDecl *VD, const ASTContext &ACtx)
STATISTIC(NumObjCCallEdges, "Number of Objective-C method call edges")
const AnnotatedLine * Line
std::string createCrossTUIndexString(const llvm::StringMap< std::string > &Index)
bool hasBody(const FunctionDecl *&Definition) const
Returns true if the function has a body.
Definition: Decl.cpp:2751
RangeSelector name(StringRef ID)
Given a node with a "name", (like NamedDecl, DeclRefExpr or CxxCtorInitializer) selects the name&#39;s to...
llvm::Expected< llvm::StringMap< std::string > > parseCrossTUIndex(StringRef IndexPath, StringRef CrossTUDir)
This function parses an index file that determines which translation unit contains which definition...
FileSystemOptions & getFileSystemOpts()
CompilerInstance - Helper class for managing a single instance of the Clang compiler.
Options for controlling the compiler diagnostics engine.
llvm::Expected< QualType > Import(QualType FromT)
Import the given type from the "from" context into the "to" context.
static bool hasBodyOrInit(const FunctionDecl *D, const FunctionDecl *&DefD)
Naming ambiguity (likely ODR violation).
Definition: ASTImporter.h:54
bool hasConstFields() const
Recursively check all fields in the record for const-ness.
Definition: Type.cpp:3275
CanProxy< U > getAs() const
Retrieve a canonical type pointer with a different static type, upcasting or downcasting as needed...
Dataflow Directional Tag Classes.
DeclContext - This is used only as base class of specific decl types that can act as declaration cont...
Definition: DeclBase.h:1264
Used for handling and querying diagnostic IDs.
A helper class that allows the use of isa/cast/dyncast to detect TagType objects of structs/unions/cl...
Definition: Type.h:4417
index_error_code getCode() const
Imports selected nodes from one AST context into another context, merging AST nodes where appropriate...
Definition: ASTImporter.h:83
llvm::Expected< const FunctionDecl * > importDefinition(const FunctionDecl *FD)
This function merges a definition from a separate AST Unit into the current one which was created by ...
const ASTContext & getASTContext() const
Definition: ASTUnit.h:436
SourceManager & getSourceManager()
Definition: ASTContext.h:662
CanQualType getCanonicalType(QualType T) const
Return the canonical (structural) type corresponding to the specified potentially non-canonical type ...
Definition: ASTContext.h:2277
llvm::Expected< const FunctionDecl * > getCrossTUDefinition(const FunctionDecl *FD, StringRef CrossTUDir, StringRef IndexName, bool DisplayCTUProgress=false)
This function loads a function or variable definition from an external AST file and merges it into th...
std::error_code convertToErrorCode() const override
TranslationUnitDecl * getTranslationUnitDecl() const
Definition: ASTContext.h:1004
Defines the clang::TargetInfo interface.
static std::string getLookupName(const NamedDecl *ND)
Get a name to identify a named decl.
void emitCrossTUDiagnostics(const IndexError &IE)
Emit diagnostics for the user for potential configuration errors.
The top declaration context.
Definition: Decl.h:107
QualType getType() const
Definition: Decl.h:647
bool generateUSRForDecl(const Decl *D, SmallVectorImpl< char > &Buf)
Generate a USR for a Decl, including the USR prefix.
This represents a decl that may have a name.
Definition: Decl.h:248
std::string getTripleToName() const
const LangOptions & getLangOpts() const
Definition: ASTContext.h:707
std::string getTripleFromName() const