Coverage Report

Created: 2020-09-22 08:39

/Users/buildslave/jenkins/workspace/coverage/llvm-project/clang/lib/CrossTU/CrossTranslationUnit.cpp
Line
Count
Source (jump to first uncovered line)
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/AST/ParentMapContext.h"
16
#include "clang/Basic/TargetInfo.h"
17
#include "clang/CrossTU/CrossTUDiagnostic.h"
18
#include "clang/Frontend/ASTUnit.h"
19
#include "clang/Frontend/CompilerInstance.h"
20
#include "clang/Frontend/TextDiagnosticPrinter.h"
21
#include "clang/Index/USRGeneration.h"
22
#include "llvm/ADT/Optional.h"
23
#include "llvm/ADT/Statistic.h"
24
#include "llvm/ADT/Triple.h"
25
#include "llvm/Option/ArgList.h"
26
#include "llvm/Support/ErrorHandling.h"
27
#include "llvm/Support/ManagedStatic.h"
28
#include "llvm/Support/Path.h"
29
#include "llvm/Support/YAMLParser.h"
30
#include "llvm/Support/raw_ostream.h"
31
#include <algorithm>
32
#include <fstream>
33
#include <sstream>
34
#include <tuple>
35
36
namespace clang {
37
namespace cross_tu {
38
39
namespace {
40
41
#define DEBUG_TYPE "CrossTranslationUnit"
42
STATISTIC(NumGetCTUCalled, "The # of getCTUDefinition function called");
43
STATISTIC(
44
    NumNotInOtherTU,
45
    "The # of getCTUDefinition called but the function is not in any other TU");
46
STATISTIC(NumGetCTUSuccess,
47
          "The # of getCTUDefinition successfully returned the "
48
          "requested function's body");
49
STATISTIC(NumUnsupportedNodeFound, "The # of imports when the ASTImporter "
50
                                   "encountered an unsupported AST Node");
51
STATISTIC(NumNameConflicts, "The # of imports when the ASTImporter "
52
                            "encountered an ODR error");
53
STATISTIC(NumTripleMismatch, "The # of triple mismatches");
54
STATISTIC(NumLangMismatch, "The # of language mismatches");
55
STATISTIC(NumLangDialectMismatch, "The # of language dialect mismatches");
56
STATISTIC(NumASTLoadThresholdReached,
57
          "The # of ASTs not loaded because of threshold");
58
59
// Same as Triple's equality operator, but we check a field only if that is
60
// known in both instances.
61
69
bool hasEqualKnownFields(const llvm::Triple &Lhs, const llvm::Triple &Rhs) {
62
69
  using llvm::Triple;
63
69
  if (Lhs.getArch() != Triple::UnknownArch &&
64
69
      Rhs.getArch() != Triple::UnknownArch && Lhs.getArch() != Rhs.getArch())
65
1
    return false;
66
68
  if (Lhs.getSubArch() != Triple::NoSubArch &&
67
0
      Rhs.getSubArch() != Triple::NoSubArch &&
68
0
      Lhs.getSubArch() != Rhs.getSubArch())
69
0
    return false;
70
68
  if (Lhs.getVendor() != Triple::UnknownVendor &&
71
67
      Rhs.getVendor() != Triple::UnknownVendor &&
72
67
      Lhs.getVendor() != Rhs.getVendor())
73
0
    return false;
74
68
  if (!Lhs.isOSUnknown() && !Rhs.isOSUnknown() &&
75
68
      Lhs.getOS() != Rhs.getOS())
76
0
    return false;
77
68
  if (Lhs.getEnvironment() != Triple::UnknownEnvironment &&
78
62
      Rhs.getEnvironment() != Triple::UnknownEnvironment &&
79
62
      Lhs.getEnvironment() != Rhs.getEnvironment())
80
0
    return false;
81
68
  if (Lhs.getObjectFormat() != Triple::UnknownObjectFormat &&
82
68
      Rhs.getObjectFormat() != Triple::UnknownObjectFormat &&
83
68
      Lhs.getObjectFormat() != Rhs.getObjectFormat())
84
0
    return false;
85
68
  return true;
86
68
}
87
88
// FIXME: This class is will be removed after the transition to llvm::Error.
89
class IndexErrorCategory : public std::error_category {
90
public:
91
0
  const char *name() const noexcept override { return "clang.index"; }
92
93
0
  std::string message(int Condition) const override {
94
0
    switch (static_cast<index_error_code>(Condition)) {
95
0
    case index_error_code::unspecified:
96
0
      return "An unknown error has occurred.";
97
0
    case index_error_code::missing_index_file:
98
0
      return "The index file is missing.";
99
0
    case index_error_code::invalid_index_format:
100
0
      return "Invalid index file format.";
101
0
    case index_error_code::multiple_definitions:
102
0
      return "Multiple definitions in the index file.";
103
0
    case index_error_code::missing_definition:
104
0
      return "Missing definition from the index file.";
105
0
    case index_error_code::failed_import:
106
0
      return "Failed to import the definition.";
107
0
    case index_error_code::failed_to_get_external_ast:
108
0
      return "Failed to load external AST source.";
109
0
    case index_error_code::failed_to_generate_usr:
110
0
      return "Failed to generate USR.";
111
0
    case index_error_code::triple_mismatch:
112
0
      return "Triple mismatch";
113
0
    case index_error_code::lang_mismatch:
114
0
      return "Language mismatch";
115
0
    case index_error_code::lang_dialect_mismatch:
116
0
      return "Language dialect mismatch";
117
0
    case index_error_code::load_threshold_reached:
118
0
      return "Load threshold reached";
119
0
    case index_error_code::invocation_list_ambiguous:
120
0
      return "Invocation list file contains multiple references to the same "
121
0
             "source file.";
122
0
    case index_error_code::invocation_list_file_not_found:
123
0
      return "Invocation list file is not found.";
124
0
    case index_error_code::invocation_list_empty:
125
0
      return "Invocation list file is empty.";
126
0
    case index_error_code::invocation_list_wrong_format:
127
0
      return "Invocation list file is in wrong format.";
128
0
    case index_error_code::invocation_list_lookup_unsuccessful:
129
0
      return "Invocation list file does not contain the requested source file.";
130
0
    }
131
0
    llvm_unreachable("Unrecognized index_error_code.");
132
0
  }
133
};
134
135
static llvm::ManagedStatic<IndexErrorCategory> Category;
136
} // end anonymous namespace
137
138
char IndexError::ID;
139
140
0
void IndexError::log(raw_ostream &OS) const {
141
0
  OS << Category->message(static_cast<int>(Code)) << '\n';
142
0
}
143
144
0
std::error_code IndexError::convertToErrorCode() const {
145
0
  return std::error_code(static_cast<int>(Code), *Category);
146
0
}
147
148
llvm::Expected<llvm::StringMap<std::string>>
149
10
parseCrossTUIndex(StringRef IndexPath) {
150
10
  std::ifstream ExternalMapFile{std::string(IndexPath)};
151
10
  if (!ExternalMapFile)
152
0
    return llvm::make_error<IndexError>(index_error_code::missing_index_file,
153
0
                                        IndexPath.str());
154
155
10
  llvm::StringMap<std::string> Result;
156
10
  std::string Line;
157
10
  unsigned LineNo = 1;
158
146
  while (std::getline(ExternalMapFile, Line)) {
159
136
    StringRef LineRef{Line};
160
136
    const size_t Delimiter = LineRef.find(" ");
161
136
    if (Delimiter > 0 && Delimiter != std::string::npos) {
162
136
      StringRef LookupName = LineRef.substr(0, Delimiter);
163
164
      // Store paths with posix-style directory separator.
165
136
      SmallVector<char, 32> FilePath;
166
136
      llvm::Twine{LineRef.substr(Delimiter + 1)}.toVector(FilePath);
167
136
      llvm::sys::path::native(FilePath, llvm::sys::path::Style::posix);
168
169
136
      bool InsertionOccured;
170
136
      std::tie(std::ignore, InsertionOccured) =
171
136
          Result.try_emplace(LookupName, FilePath.begin(), FilePath.end());
172
136
      if (!InsertionOccured)
173
0
        return llvm::make_error<IndexError>(
174
0
            index_error_code::multiple_definitions, IndexPath.str(), LineNo);
175
0
    } else
176
0
      return llvm::make_error<IndexError>(
177
0
          index_error_code::invalid_index_format, IndexPath.str(), LineNo);
178
136
    ++LineNo;
179
136
  }
180
10
  return Result;
181
10
}
182
183
std::string
184
2
createCrossTUIndexString(const llvm::StringMap<std::string> &Index) {
185
2
  std::ostringstream Result;
186
2
  for (const auto &E : Index)
187
10
    Result << E.getKey().str() << " " << E.getValue() << '\n';
188
2
  return Result.str();
189
2
}
190
191
43
bool containsConst(const VarDecl *VD, const ASTContext &ACtx) {
192
43
  CanQualType CT = ACtx.getCanonicalType(VD->getType());
193
43
  if (!CT.isConstQualified()) {
194
19
    const RecordType *RTy = CT->getAs<RecordType>();
195
19
    if (!RTy || 
!RTy->hasConstFields()18
)
196
2
      return false;
197
41
  }
198
41
  return true;
199
41
}
200
201
832
static bool hasBodyOrInit(const FunctionDecl *D, const FunctionDecl *&DefD) {
202
832
  return D->hasBody(DefD);
203
832
}
204
584
static bool hasBodyOrInit(const VarDecl *D, const VarDecl *&DefD) {
205
584
  return D->getAnyInitializer(DefD);
206
584
}
207
211
template <typename T> static bool hasBodyOrInit(const T *D) {
208
211
  const T *Unused;
209
211
  return hasBodyOrInit(D, Unused);
210
211
}
CrossTranslationUnit.cpp:bool clang::cross_tu::hasBodyOrInit<clang::FunctionDecl>(clang::FunctionDecl const*)
Line
Count
Source
207
145
template <typename T> static bool hasBodyOrInit(const T *D) {
208
145
  const T *Unused;
209
145
  return hasBodyOrInit(D, Unused);
210
145
}
CrossTranslationUnit.cpp:bool clang::cross_tu::hasBodyOrInit<clang::VarDecl>(clang::VarDecl const*)
Line
Count
Source
207
66
template <typename T> static bool hasBodyOrInit(const T *D) {
208
66
  const T *Unused;
209
66
  return hasBodyOrInit(D, Unused);
210
66
}
211
212
CrossTranslationUnitContext::CrossTranslationUnitContext(CompilerInstance &CI)
213
1.34k
    : Context(CI.getASTContext()), ASTStorage(CI) {}
214
215
1.34k
CrossTranslationUnitContext::~CrossTranslationUnitContext() {}
216
217
llvm::Optional<std::string>
218
684
CrossTranslationUnitContext::getLookupName(const NamedDecl *ND) {
219
684
  SmallString<128> DeclUSR;
220
684
  bool Ret = index::generateUSRForDecl(ND, DeclUSR);
221
684
  if (Ret)
222
3
    return {};
223
681
  return std::string(DeclUSR.str());
224
681
}
225
226
/// Recursively visits the decls of a DeclContext, and returns one with the
227
/// given USR.
228
template <typename T>
229
const T *
230
CrossTranslationUnitContext::findDefInDeclContext(const DeclContext *DC,
231
2.52k
                                                  StringRef LookupName) {
232
2.52k
  assert(DC && "Declaration Context must not be null");
233
4.10k
  for (const Decl *D : DC->decls()) {
234
4.10k
    const auto *SubDC = dyn_cast<DeclContext>(D);
235
4.10k
    if (SubDC)
236
2.46k
      if (const auto *ND = findDefInDeclContext<T>(SubDC, LookupName))
237
26
        return ND;
238
239
4.08k
    const auto *ND = dyn_cast<T>(D);
240
4.08k
    const T *ResultDecl;
241
4.08k
    if (!ND || 
!hasBodyOrInit(ND, ResultDecl)1.20k
)
242
3.48k
      continue;
243
598
    llvm::Optional<std::string> ResultLookupName = getLookupName(ResultDecl);
244
598
    if (!ResultLookupName || 
*ResultLookupName != LookupName596
)
245
530
      continue;
246
68
    return ResultDecl;
247
68
  }
248
2.43k
  return nullptr;
249
2.52k
}
clang::FunctionDecl const* clang::cross_tu::CrossTranslationUnitContext::findDefInDeclContext<clang::FunctionDecl>(clang::DeclContext const*, llvm::StringRef)
Line
Count
Source
231
1.05k
                                                  StringRef LookupName) {
232
1.05k
  assert(DC && "Declaration Context must not be null");
233
1.80k
  for (const Decl *D : DC->decls()) {
234
1.80k
    const auto *SubDC = dyn_cast<DeclContext>(D);
235
1.80k
    if (SubDC)
236
1.01k
      if (const auto *ND = findDefInDeclContext<T>(SubDC, LookupName))
237
18
        return ND;
238
239
1.78k
    const auto *ND = dyn_cast<T>(D);
240
1.78k
    const T *ResultDecl;
241
1.78k
    if (!ND || 
!hasBodyOrInit(ND, ResultDecl)687
)
242
1.36k
      continue;
243
418
    llvm::Optional<std::string> ResultLookupName = getLookupName(ResultDecl);
244
418
    if (!ResultLookupName || *ResultLookupName != LookupName)
245
372
      continue;
246
46
    return ResultDecl;
247
46
  }
248
994
  return nullptr;
249
1.05k
}
clang::VarDecl const* clang::cross_tu::CrossTranslationUnitContext::findDefInDeclContext<clang::VarDecl>(clang::DeclContext const*, llvm::StringRef)
Line
Count
Source
231
1.47k
                                                  StringRef LookupName) {
232
1.47k
  assert(DC && "Declaration Context must not be null");
233
2.30k
  for (const Decl *D : DC->decls()) {
234
2.30k
    const auto *SubDC = dyn_cast<DeclContext>(D);
235
2.30k
    if (SubDC)
236
1.44k
      if (const auto *ND = findDefInDeclContext<T>(SubDC, LookupName))
237
8
        return ND;
238
239
2.29k
    const auto *ND = dyn_cast<T>(D);
240
2.29k
    const T *ResultDecl;
241
2.29k
    if (!ND || 
!hasBodyOrInit(ND, ResultDecl)518
)
242
2.11k
      continue;
243
180
    llvm::Optional<std::string> ResultLookupName = getLookupName(ResultDecl);
244
180
    if (!ResultLookupName || 
*ResultLookupName != LookupName178
)
245
158
      continue;
246
22
    return ResultDecl;
247
22
  }
248
1.44k
  return nullptr;
249
1.47k
}
250
251
template <typename T>
252
llvm::Expected<const T *> CrossTranslationUnitContext::getCrossTUDefinitionImpl(
253
    const T *D, StringRef CrossTUDir, StringRef IndexName,
254
76
    bool DisplayCTUProgress) {
255
76
  assert(D && "D is missing, bad call to this function!");
256
76
  assert(!hasBodyOrInit(D) &&
257
76
         "D has a body or init in current translation unit!");
258
76
  ++NumGetCTUCalled;
259
76
  const llvm::Optional<std::string> LookupName = getLookupName(D);
260
76
  if (!LookupName)
261
0
    return llvm::make_error<IndexError>(
262
0
        index_error_code::failed_to_generate_usr);
263
76
  llvm::Expected<ASTUnit *> ASTUnitOrError =
264
76
      loadExternalAST(*LookupName, CrossTUDir, IndexName, DisplayCTUProgress);
265
76
  if (!ASTUnitOrError)
266
7
    return ASTUnitOrError.takeError();
267
69
  ASTUnit *Unit = *ASTUnitOrError;
268
69
  assert(&Unit->getFileManager() ==
269
69
         &Unit->getASTContext().getSourceManager().getFileManager());
270
271
69
  const llvm::Triple &TripleTo = Context.getTargetInfo().getTriple();
272
69
  const llvm::Triple &TripleFrom =
273
69
      Unit->getASTContext().getTargetInfo().getTriple();
274
  // The imported AST had been generated for a different target.
275
  // Some parts of the triple in the loaded ASTContext can be unknown while the
276
  // very same parts in the target ASTContext are known. Thus we check for the
277
  // known parts only.
278
69
  if (!hasEqualKnownFields(TripleTo, TripleFrom)) {
279
    // TODO: Pass the SourceLocation of the CallExpression for more precise
280
    // diagnostics.
281
1
    ++NumTripleMismatch;
282
1
    return llvm::make_error<IndexError>(index_error_code::triple_mismatch,
283
1
                                        std::string(Unit->getMainFileName()),
284
1
                                        TripleTo.str(), TripleFrom.str());
285
1
  }
286
287
68
  const auto &LangTo = Context.getLangOpts();
288
68
  const auto &LangFrom = Unit->getASTContext().getLangOpts();
289
290
  // FIXME: Currenty we do not support CTU across C++ and C and across
291
  // different dialects of C++.
292
68
  if (LangTo.CPlusPlus != LangFrom.CPlusPlus) {
293
0
    ++NumLangMismatch;
294
0
    return llvm::make_error<IndexError>(index_error_code::lang_mismatch);
295
0
  }
296
297
  // If CPP dialects are different then return with error.
298
  //
299
  // Consider this STL code:
300
  //   template<typename _Alloc>
301
  //     struct __alloc_traits
302
  //   #if __cplusplus >= 201103L
303
  //     : std::allocator_traits<_Alloc>
304
  //   #endif
305
  //     { // ...
306
  //     };
307
  // This class template would create ODR errors during merging the two units,
308
  // since in one translation unit the class template has a base class, however
309
  // in the other unit it has none.
310
68
  if (LangTo.CPlusPlus11 != LangFrom.CPlusPlus11 ||
311
68
      LangTo.CPlusPlus14 != LangFrom.CPlusPlus14 ||
312
68
      LangTo.CPlusPlus17 != LangFrom.CPlusPlus17 ||
313
68
      LangTo.CPlusPlus20 != LangFrom.CPlusPlus20) {
314
0
    ++NumLangDialectMismatch;
315
0
    return llvm::make_error<IndexError>(
316
0
        index_error_code::lang_dialect_mismatch);
317
0
  }
318
319
68
  TranslationUnitDecl *TU = Unit->getASTContext().getTranslationUnitDecl();
320
68
  if (const T *ResultDecl = findDefInDeclContext<T>(TU, *LookupName))
321
68
    return importDefinition(ResultDecl, Unit);
322
0
  return llvm::make_error<IndexError>(index_error_code::failed_import);
323
0
}
llvm::Expected<clang::FunctionDecl const*> clang::cross_tu::CrossTranslationUnitContext::getCrossTUDefinitionImpl<clang::FunctionDecl>(clang::FunctionDecl const*, llvm::StringRef, llvm::StringRef, bool)
Line
Count
Source
254
54
    bool DisplayCTUProgress) {
255
54
  assert(D && "D is missing, bad call to this function!");
256
54
  assert(!hasBodyOrInit(D) &&
257
54
         "D has a body or init in current translation unit!");
258
54
  ++NumGetCTUCalled;
259
54
  const llvm::Optional<std::string> LookupName = getLookupName(D);
260
54
  if (!LookupName)
261
0
    return llvm::make_error<IndexError>(
262
0
        index_error_code::failed_to_generate_usr);
263
54
  llvm::Expected<ASTUnit *> ASTUnitOrError =
264
54
      loadExternalAST(*LookupName, CrossTUDir, IndexName, DisplayCTUProgress);
265
54
  if (!ASTUnitOrError)
266
7
    return ASTUnitOrError.takeError();
267
47
  ASTUnit *Unit = *ASTUnitOrError;
268
47
  assert(&Unit->getFileManager() ==
269
47
         &Unit->getASTContext().getSourceManager().getFileManager());
270
271
47
  const llvm::Triple &TripleTo = Context.getTargetInfo().getTriple();
272
47
  const llvm::Triple &TripleFrom =
273
47
      Unit->getASTContext().getTargetInfo().getTriple();
274
  // The imported AST had been generated for a different target.
275
  // Some parts of the triple in the loaded ASTContext can be unknown while the
276
  // very same parts in the target ASTContext are known. Thus we check for the
277
  // known parts only.
278
47
  if (!hasEqualKnownFields(TripleTo, TripleFrom)) {
279
    // TODO: Pass the SourceLocation of the CallExpression for more precise
280
    // diagnostics.
281
1
    ++NumTripleMismatch;
282
1
    return llvm::make_error<IndexError>(index_error_code::triple_mismatch,
283
1
                                        std::string(Unit->getMainFileName()),
284
1
                                        TripleTo.str(), TripleFrom.str());
285
1
  }
286
287
46
  const auto &LangTo = Context.getLangOpts();
288
46
  const auto &LangFrom = Unit->getASTContext().getLangOpts();
289
290
  // FIXME: Currenty we do not support CTU across C++ and C and across
291
  // different dialects of C++.
292
46
  if (LangTo.CPlusPlus != LangFrom.CPlusPlus) {
293
0
    ++NumLangMismatch;
294
0
    return llvm::make_error<IndexError>(index_error_code::lang_mismatch);
295
0
  }
296
297
  // If CPP dialects are different then return with error.
298
  //
299
  // Consider this STL code:
300
  //   template<typename _Alloc>
301
  //     struct __alloc_traits
302
  //   #if __cplusplus >= 201103L
303
  //     : std::allocator_traits<_Alloc>
304
  //   #endif
305
  //     { // ...
306
  //     };
307
  // This class template would create ODR errors during merging the two units,
308
  // since in one translation unit the class template has a base class, however
309
  // in the other unit it has none.
310
46
  if (LangTo.CPlusPlus11 != LangFrom.CPlusPlus11 ||
311
46
      LangTo.CPlusPlus14 != LangFrom.CPlusPlus14 ||
312
46
      LangTo.CPlusPlus17 != LangFrom.CPlusPlus17 ||
313
46
      LangTo.CPlusPlus20 != LangFrom.CPlusPlus20) {
314
0
    ++NumLangDialectMismatch;
315
0
    return llvm::make_error<IndexError>(
316
0
        index_error_code::lang_dialect_mismatch);
317
0
  }
318
319
46
  TranslationUnitDecl *TU = Unit->getASTContext().getTranslationUnitDecl();
320
46
  if (const T *ResultDecl = findDefInDeclContext<T>(TU, *LookupName))
321
46
    return importDefinition(ResultDecl, Unit);
322
0
  return llvm::make_error<IndexError>(index_error_code::failed_import);
323
0
}
llvm::Expected<clang::VarDecl const*> clang::cross_tu::CrossTranslationUnitContext::getCrossTUDefinitionImpl<clang::VarDecl>(clang::VarDecl const*, llvm::StringRef, llvm::StringRef, bool)
Line
Count
Source
254
22
    bool DisplayCTUProgress) {
255
22
  assert(D && "D is missing, bad call to this function!");
256
22
  assert(!hasBodyOrInit(D) &&
257
22
         "D has a body or init in current translation unit!");
258
22
  ++NumGetCTUCalled;
259
22
  const llvm::Optional<std::string> LookupName = getLookupName(D);
260
22
  if (!LookupName)
261
0
    return llvm::make_error<IndexError>(
262
0
        index_error_code::failed_to_generate_usr);
263
22
  llvm::Expected<ASTUnit *> ASTUnitOrError =
264
22
      loadExternalAST(*LookupName, CrossTUDir, IndexName, DisplayCTUProgress);
265
22
  if (!ASTUnitOrError)
266
0
    return ASTUnitOrError.takeError();
267
22
  ASTUnit *Unit = *ASTUnitOrError;
268
22
  assert(&Unit->getFileManager() ==
269
22
         &Unit->getASTContext().getSourceManager().getFileManager());
270
271
22
  const llvm::Triple &TripleTo = Context.getTargetInfo().getTriple();
272
22
  const llvm::Triple &TripleFrom =
273
22
      Unit->getASTContext().getTargetInfo().getTriple();
274
  // The imported AST had been generated for a different target.
275
  // Some parts of the triple in the loaded ASTContext can be unknown while the
276
  // very same parts in the target ASTContext are known. Thus we check for the
277
  // known parts only.
278
22
  if (!hasEqualKnownFields(TripleTo, TripleFrom)) {
279
    // TODO: Pass the SourceLocation of the CallExpression for more precise
280
    // diagnostics.
281
0
    ++NumTripleMismatch;
282
0
    return llvm::make_error<IndexError>(index_error_code::triple_mismatch,
283
0
                                        std::string(Unit->getMainFileName()),
284
0
                                        TripleTo.str(), TripleFrom.str());
285
0
  }
286
287
22
  const auto &LangTo = Context.getLangOpts();
288
22
  const auto &LangFrom = Unit->getASTContext().getLangOpts();
289
290
  // FIXME: Currenty we do not support CTU across C++ and C and across
291
  // different dialects of C++.
292
22
  if (LangTo.CPlusPlus != LangFrom.CPlusPlus) {
293
0
    ++NumLangMismatch;
294
0
    return llvm::make_error<IndexError>(index_error_code::lang_mismatch);
295
0
  }
296
297
  // If CPP dialects are different then return with error.
298
  //
299
  // Consider this STL code:
300
  //   template<typename _Alloc>
301
  //     struct __alloc_traits
302
  //   #if __cplusplus >= 201103L
303
  //     : std::allocator_traits<_Alloc>
304
  //   #endif
305
  //     { // ...
306
  //     };
307
  // This class template would create ODR errors during merging the two units,
308
  // since in one translation unit the class template has a base class, however
309
  // in the other unit it has none.
310
22
  if (LangTo.CPlusPlus11 != LangFrom.CPlusPlus11 ||
311
22
      LangTo.CPlusPlus14 != LangFrom.CPlusPlus14 ||
312
22
      LangTo.CPlusPlus17 != LangFrom.CPlusPlus17 ||
313
22
      LangTo.CPlusPlus20 != LangFrom.CPlusPlus20) {
314
0
    ++NumLangDialectMismatch;
315
0
    return llvm::make_error<IndexError>(
316
0
        index_error_code::lang_dialect_mismatch);
317
0
  }
318
319
22
  TranslationUnitDecl *TU = Unit->getASTContext().getTranslationUnitDecl();
320
22
  if (const T *ResultDecl = findDefInDeclContext<T>(TU, *LookupName))
321
22
    return importDefinition(ResultDecl, Unit);
322
0
  return llvm::make_error<IndexError>(index_error_code::failed_import);
323
0
}
324
325
llvm::Expected<const FunctionDecl *>
326
CrossTranslationUnitContext::getCrossTUDefinition(const FunctionDecl *FD,
327
                                                  StringRef CrossTUDir,
328
                                                  StringRef IndexName,
329
54
                                                  bool DisplayCTUProgress) {
330
54
  return getCrossTUDefinitionImpl(FD, CrossTUDir, IndexName,
331
54
                                  DisplayCTUProgress);
332
54
}
333
334
llvm::Expected<const VarDecl *>
335
CrossTranslationUnitContext::getCrossTUDefinition(const VarDecl *VD,
336
                                                  StringRef CrossTUDir,
337
                                                  StringRef IndexName,
338
22
                                                  bool DisplayCTUProgress) {
339
22
  return getCrossTUDefinitionImpl(VD, CrossTUDir, IndexName,
340
22
                                  DisplayCTUProgress);
341
22
}
342
343
8
void CrossTranslationUnitContext::emitCrossTUDiagnostics(const IndexError &IE) {
344
8
  switch (IE.getCode()) {
345
0
  case index_error_code::missing_index_file:
346
0
    Context.getDiagnostics().Report(diag::err_ctu_error_opening)
347
0
        << IE.getFileName();
348
0
    break;
349
0
  case index_error_code::invalid_index_format:
350
0
    Context.getDiagnostics().Report(diag::err_extdefmap_parsing)
351
0
        << IE.getFileName() << IE.getLineNum();
352
0
    break;
353
0
  case index_error_code::multiple_definitions:
354
0
    Context.getDiagnostics().Report(diag::err_multiple_def_index)
355
0
        << IE.getLineNum();
356
0
    break;
357
1
  case index_error_code::triple_mismatch:
358
1
    Context.getDiagnostics().Report(diag::warn_ctu_incompat_triple)
359
1
        << IE.getFileName() << IE.getTripleToName() << IE.getTripleFromName();
360
1
    break;
361
7
  default:
362
7
    break;
363
8
  }
364
8
}
365
366
CrossTranslationUnitContext::ASTUnitStorage::ASTUnitStorage(
367
    CompilerInstance &CI)
368
    : Loader(CI, CI.getAnalyzerOpts()->CTUDir,
369
             CI.getAnalyzerOpts()->CTUInvocationList),
370
      LoadGuard(CI.getASTContext().getLangOpts().CPlusPlus
371
                    ? CI.getAnalyzerOpts()->CTUImportCppThreshold
372
1.34k
                    : CI.getAnalyzerOpts()->CTUImportThreshold) {}
373
374
llvm::Expected<ASTUnit *>
375
CrossTranslationUnitContext::ASTUnitStorage::getASTUnitForFile(
376
69
    StringRef FileName, bool DisplayCTUProgress) {
377
  // Try the cache first.
378
69
  auto ASTCacheEntry = FileASTUnitMap.find(FileName);
379
69
  if (ASTCacheEntry == FileASTUnitMap.end()) {
380
381
    // Do not load if the limit is reached.
382
11
    if (!LoadGuard) {
383
1
      ++NumASTLoadThresholdReached;
384
1
      return llvm::make_error<IndexError>(
385
1
          index_error_code::load_threshold_reached);
386
1
    }
387
388
10
    auto LoadAttempt = Loader.load(FileName);
389
390
10
    if (!LoadAttempt)
391
0
      return LoadAttempt.takeError();
392
393
10
    std::unique_ptr<ASTUnit> LoadedUnit = std::move(LoadAttempt.get());
394
395
    // Need the raw pointer and the unique_ptr as well.
396
10
    ASTUnit *Unit = LoadedUnit.get();
397
398
    // Update the cache.
399
10
    FileASTUnitMap[FileName] = std::move(LoadedUnit);
400
401
10
    LoadGuard.indicateLoadSuccess();
402
403
10
    if (DisplayCTUProgress)
404
3
      llvm::errs() << "CTU loaded AST file: " << FileName << "\n";
405
406
10
    return Unit;
407
408
58
  } else {
409
    // Found in the cache.
410
58
    return ASTCacheEntry->second.get();
411
58
  }
412
69
}
413
414
llvm::Expected<ASTUnit *>
415
CrossTranslationUnitContext::ASTUnitStorage::getASTUnitForFunction(
416
    StringRef FunctionName, StringRef CrossTUDir, StringRef IndexName,
417
76
    bool DisplayCTUProgress) {
418
  // Try the cache first.
419
76
  auto ASTCacheEntry = NameASTUnitMap.find(FunctionName);
420
76
  if (ASTCacheEntry == NameASTUnitMap.end()) {
421
    // Load the ASTUnit from the pre-dumped AST file specified by ASTFileName.
422
423
    // Ensure that the Index is loaded, as we need to search in it.
424
75
    if (llvm::Error IndexLoadError =
425
0
            ensureCTUIndexLoaded(CrossTUDir, IndexName))
426
0
      return std::move(IndexLoadError);
427
428
    // Check if there is and entry in the index for the function.
429
75
    if (!NameFileMap.count(FunctionName)) {
430
6
      ++NumNotInOtherTU;
431
6
      return llvm::make_error<IndexError>(index_error_code::missing_definition);
432
6
    }
433
434
    // Search in the index for the filename where the definition of FuncitonName
435
    // resides.
436
69
    if (llvm::Expected<ASTUnit *> FoundForFile =
437
68
            getASTUnitForFile(NameFileMap[FunctionName], DisplayCTUProgress)) {
438
439
      // Update the cache.
440
68
      NameASTUnitMap[FunctionName] = *FoundForFile;
441
68
      return *FoundForFile;
442
443
1
    } else {
444
1
      return FoundForFile.takeError();
445
1
    }
446
1
  } else {
447
    // Found in the cache.
448
1
    return ASTCacheEntry->second;
449
1
  }
450
76
}
451
452
llvm::Expected<std::string>
453
CrossTranslationUnitContext::ASTUnitStorage::getFileForFunction(
454
0
    StringRef FunctionName, StringRef CrossTUDir, StringRef IndexName) {
455
0
  if (llvm::Error IndexLoadError = ensureCTUIndexLoaded(CrossTUDir, IndexName))
456
0
    return std::move(IndexLoadError);
457
0
  return NameFileMap[FunctionName];
458
0
}
459
460
llvm::Error CrossTranslationUnitContext::ASTUnitStorage::ensureCTUIndexLoaded(
461
75
    StringRef CrossTUDir, StringRef IndexName) {
462
  // Dont initialize if the map is filled.
463
75
  if (!NameFileMap.empty())
464
66
    return llvm::Error::success();
465
466
  // Get the absolute path to the index file.
467
9
  SmallString<256> IndexFile = CrossTUDir;
468
9
  if (llvm::sys::path::is_absolute(IndexName))
469
2
    IndexFile = IndexName;
470
7
  else
471
7
    llvm::sys::path::append(IndexFile, IndexName);
472
473
9
  if (auto IndexMapping = parseCrossTUIndex(IndexFile)) {
474
    // Initialize member map.
475
9
    NameFileMap = *IndexMapping;
476
9
    return llvm::Error::success();
477
0
  } else {
478
    // Error while parsing CrossTU index file.
479
0
    return IndexMapping.takeError();
480
0
  };
481
0
}
482
483
llvm::Expected<ASTUnit *> CrossTranslationUnitContext::loadExternalAST(
484
    StringRef LookupName, StringRef CrossTUDir, StringRef IndexName,
485
76
    bool DisplayCTUProgress) {
486
  // FIXME: The current implementation only supports loading decls with
487
  //        a lookup name from a single translation unit. If multiple
488
  //        translation units contains decls with the same lookup name an
489
  //        error will be returned.
490
491
  // Try to get the value from the heavily cached storage.
492
76
  llvm::Expected<ASTUnit *> Unit = ASTStorage.getASTUnitForFunction(
493
76
      LookupName, CrossTUDir, IndexName, DisplayCTUProgress);
494
495
76
  if (!Unit)
496
7
    return Unit.takeError();
497
498
  // Check whether the backing pointer of the Expected is a nullptr.
499
69
  if (!*Unit)
500
0
    return llvm::make_error<IndexError>(
501
0
        index_error_code::failed_to_get_external_ast);
502
503
69
  return Unit;
504
69
}
505
506
CrossTranslationUnitContext::ASTLoader::ASTLoader(
507
    CompilerInstance &CI, StringRef CTUDir, StringRef InvocationListFilePath)
508
1.34k
    : CI(CI), CTUDir(CTUDir), InvocationListFilePath(InvocationListFilePath) {}
509
510
CrossTranslationUnitContext::LoadResultTy
511
10
CrossTranslationUnitContext::ASTLoader::load(StringRef Identifier) {
512
10
  llvm::SmallString<256> Path;
513
10
  if (llvm::sys::path::is_absolute(Identifier, PathStyle)) {
514
1
    Path = Identifier;
515
9
  } else {
516
9
    Path = CTUDir;
517
9
    llvm::sys::path::append(Path, PathStyle, Identifier);
518
9
  }
519
520
  // The path is stored in the InvocationList member in posix style. To
521
  // successfully lookup an entry based on filepath, it must be converted.
522
10
  llvm::sys::path::native(Path, PathStyle);
523
524
  // Normalize by removing relative path components.
525
10
  llvm::sys::path::remove_dots(Path, /*remove_dot_dot*/ true, PathStyle);
526
527
10
  if (Path.endswith(".ast"))
528
10
    return loadFromDump(Path);
529
0
  else
530
0
    return loadFromSource(Path);
531
10
}
532
533
CrossTranslationUnitContext::LoadResultTy
534
10
CrossTranslationUnitContext::ASTLoader::loadFromDump(StringRef ASTDumpPath) {
535
10
  IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts = new DiagnosticOptions();
536
10
  TextDiagnosticPrinter *DiagClient =
537
10
      new TextDiagnosticPrinter(llvm::errs(), &*DiagOpts);
538
10
  IntrusiveRefCntPtr<DiagnosticIDs> DiagID(new DiagnosticIDs());
539
10
  IntrusiveRefCntPtr<DiagnosticsEngine> Diags(
540
10
      new DiagnosticsEngine(DiagID, &*DiagOpts, DiagClient));
541
10
  return ASTUnit::LoadFromASTFile(
542
10
      std::string(ASTDumpPath.str()),
543
10
      CI.getPCHContainerOperations()->getRawReader(), ASTUnit::LoadEverything,
544
10
      Diags, CI.getFileSystemOpts());
545
10
}
546
547
/// Load the AST from a source-file, which is supposed to be located inside the
548
/// YAML formatted invocation list file under the filesystem path specified by
549
/// \p InvocationList. The invocation list should contain absolute paths.
550
/// \p SourceFilePath is the absolute path of the source file that contains the
551
/// function definition the analysis is looking for. The Index is built by the
552
/// \p clang-extdef-mapping tool, which is also supposed to be generating
553
/// absolute paths.
554
///
555
/// Proper diagnostic emission requires absolute paths, so even if a future
556
/// change introduces the handling of relative paths, this must be taken into
557
/// consideration.
558
CrossTranslationUnitContext::LoadResultTy
559
CrossTranslationUnitContext::ASTLoader::loadFromSource(
560
0
    StringRef SourceFilePath) {
561
562
0
  if (llvm::Error InitError = lazyInitInvocationList())
563
0
    return std::move(InitError);
564
0
  assert(InvocationList);
565
566
0
  auto Invocation = InvocationList->find(SourceFilePath);
567
0
  if (Invocation == InvocationList->end())
568
0
    return llvm::make_error<IndexError>(
569
0
        index_error_code::invocation_list_lookup_unsuccessful);
570
571
0
  const InvocationListTy::mapped_type &InvocationCommand = Invocation->second;
572
573
0
  SmallVector<const char *, 32> CommandLineArgs(InvocationCommand.size());
574
0
  std::transform(InvocationCommand.begin(), InvocationCommand.end(),
575
0
                 CommandLineArgs.begin(),
576
0
                 [](auto &&CmdPart) { return CmdPart.c_str(); });
577
578
0
  IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts{&CI.getDiagnosticOpts()};
579
0
  auto *DiagClient = new ForwardingDiagnosticConsumer{CI.getDiagnosticClient()};
580
0
  IntrusiveRefCntPtr<DiagnosticIDs> DiagID{
581
0
      CI.getDiagnostics().getDiagnosticIDs()};
582
0
  IntrusiveRefCntPtr<DiagnosticsEngine> Diags(
583
0
      new DiagnosticsEngine{DiagID, &*DiagOpts, DiagClient});
584
585
0
  return std::unique_ptr<ASTUnit>(ASTUnit::LoadFromCommandLine(
586
0
      CommandLineArgs.begin(), (CommandLineArgs.end()),
587
0
      CI.getPCHContainerOperations(), Diags,
588
0
      CI.getHeaderSearchOpts().ResourceDir));
589
0
}
590
591
llvm::Expected<InvocationListTy>
592
4
parseInvocationList(StringRef FileContent, llvm::sys::path::Style PathStyle) {
593
4
  InvocationListTy InvocationList;
594
595
  /// LLVM YAML parser is used to extract information from invocation list file.
596
4
  llvm::SourceMgr SM;
597
4
  llvm::yaml::Stream InvocationFile(FileContent, SM);
598
599
  /// Only the first document is processed.
600
4
  llvm::yaml::document_iterator FirstInvocationFile = InvocationFile.begin();
601
602
  /// There has to be at least one document available.
603
4
  if (FirstInvocationFile == InvocationFile.end())
604
0
    return llvm::make_error<IndexError>(
605
0
        index_error_code::invocation_list_empty);
606
607
4
  llvm::yaml::Node *DocumentRoot = FirstInvocationFile->getRoot();
608
4
  if (!DocumentRoot)
609
0
    return llvm::make_error<IndexError>(
610
0
        index_error_code::invocation_list_wrong_format);
611
612
  /// According to the format specified the document must be a mapping, where
613
  /// the keys are paths to source files, and values are sequences of invocation
614
  /// parts.
615
4
  auto *Mappings = dyn_cast<llvm::yaml::MappingNode>(DocumentRoot);
616
4
  if (!Mappings)
617
1
    return llvm::make_error<IndexError>(
618
1
        index_error_code::invocation_list_wrong_format);
619
620
5
  
for (auto &NextMapping : *Mappings)3
{
621
    /// The keys should be strings, which represent a source-file path.
622
5
    auto *Key = dyn_cast<llvm::yaml::ScalarNode>(NextMapping.getKey());
623
5
    if (!Key)
624
0
      return llvm::make_error<IndexError>(
625
0
          index_error_code::invocation_list_wrong_format);
626
627
5
    SmallVector<char, 32> ValueStorage;
628
5
    StringRef SourcePath = Key->getValue(ValueStorage);
629
630
    // Store paths with PathStyle directory separator.
631
5
    SmallVector<char, 32> NativeSourcePath;
632
5
    llvm::Twine{SourcePath}.toVector(NativeSourcePath);
633
5
    llvm::sys::path::native(NativeSourcePath, PathStyle);
634
635
5
    StringRef InvocationKey{NativeSourcePath.begin(), NativeSourcePath.size()};
636
637
5
    if (InvocationList.find(InvocationKey) != InvocationList.end())
638
1
      return llvm::make_error<IndexError>(
639
1
          index_error_code::invocation_list_ambiguous);
640
641
    /// The values should be sequences of strings, each representing a part of
642
    /// the invocation.
643
4
    auto *Args = dyn_cast<llvm::yaml::SequenceNode>(NextMapping.getValue());
644
4
    if (!Args)
645
0
      return llvm::make_error<IndexError>(
646
0
          index_error_code::invocation_list_wrong_format);
647
648
16
    
for (auto &Arg : *Args)4
{
649
16
      auto *CmdString = dyn_cast<llvm::yaml::ScalarNode>(&Arg);
650
16
      if (!CmdString)
651
0
        return llvm::make_error<IndexError>(
652
0
            index_error_code::invocation_list_wrong_format);
653
      /// Every conversion starts with an empty working storage, as it is not
654
      /// clear if this is a requirement of the YAML parser.
655
16
      ValueStorage.clear();
656
16
      InvocationList[InvocationKey].emplace_back(
657
16
          CmdString->getValue(ValueStorage));
658
16
    }
659
660
4
    if (InvocationList[InvocationKey].empty())
661
0
      return llvm::make_error<IndexError>(
662
0
          index_error_code::invocation_list_wrong_format);
663
4
  }
664
665
2
  return InvocationList;
666
3
}
667
668
0
llvm::Error CrossTranslationUnitContext::ASTLoader::lazyInitInvocationList() {
669
  /// Lazily initialize the invocation list member used for on-demand parsing.
670
0
  if (InvocationList)
671
0
    return llvm::Error::success();
672
673
0
  llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> FileContent =
674
0
      llvm::MemoryBuffer::getFile(InvocationListFilePath);
675
0
  if (!FileContent)
676
0
    return llvm::make_error<IndexError>(
677
0
        index_error_code::invocation_list_file_not_found);
678
0
  std::unique_ptr<llvm::MemoryBuffer> ContentBuffer = std::move(*FileContent);
679
0
  assert(ContentBuffer && "If no error was produced after loading, the pointer "
680
0
                          "should not be nullptr.");
681
682
0
  llvm::Expected<InvocationListTy> ExpectedInvocationList =
683
0
      parseInvocationList(ContentBuffer->getBuffer(), PathStyle);
684
685
0
  if (!ExpectedInvocationList)
686
0
    return ExpectedInvocationList.takeError();
687
688
0
  InvocationList = *ExpectedInvocationList;
689
690
0
  return llvm::Error::success();
691
0
}
692
693
template <typename T>
694
llvm::Expected<const T *>
695
68
CrossTranslationUnitContext::importDefinitionImpl(const T *D, ASTUnit *Unit) {
696
68
  assert(hasBodyOrInit(D) && "Decls to be imported should have body or init.");
697
698
68
  assert(&D->getASTContext() == &Unit->getASTContext() &&
699
68
         "ASTContext of Decl and the unit should match.");
700
68
  ASTImporter &Importer = getOrCreateASTImporter(Unit);
701
702
68
  auto ToDeclOrError = Importer.Import(D);
703
68
  if (!ToDeclOrError) {
704
1
    handleAllErrors(ToDeclOrError.takeError(),
705
1
                    [&](const ImportError &IE) {
706
1
                      switch (IE.Error) {
707
0
                      case ImportError::NameConflict:
708
0
                        ++NumNameConflicts;
709
0
                         break;
710
1
                      case ImportError::UnsupportedConstruct:
711
1
                        ++NumUnsupportedNodeFound;
712
1
                        break;
713
0
                      case ImportError::Unknown:
714
0
                        llvm_unreachable("Unknown import error happened.");
715
0
                        break;
716
1
                      }
717
1
                    });
llvm::Expected<clang::FunctionDecl const*> clang::cross_tu::CrossTranslationUnitContext::importDefinitionImpl<clang::FunctionDecl>(clang::FunctionDecl const*, clang::ASTUnit*)::'lambda'(clang::ImportError const&)::operator()(clang::ImportError const&) const
Line
Count
Source
705
1
                    [&](const ImportError &IE) {
706
1
                      switch (IE.Error) {
707
0
                      case ImportError::NameConflict:
708
0
                        ++NumNameConflicts;
709
0
                         break;
710
1
                      case ImportError::UnsupportedConstruct:
711
1
                        ++NumUnsupportedNodeFound;
712
1
                        break;
713
0
                      case ImportError::Unknown:
714
0
                        llvm_unreachable("Unknown import error happened.");
715
0
                        break;
716
1
                      }
717
1
                    });
Unexecuted instantiation: llvm::Expected<clang::VarDecl const*> clang::cross_tu::CrossTranslationUnitContext::importDefinitionImpl<clang::VarDecl>(clang::VarDecl const*, clang::ASTUnit*)::'lambda'(clang::ImportError const&)::operator()(clang::ImportError const&) const
718
1
    return llvm::make_error<IndexError>(index_error_code::failed_import);
719
1
  }
720
67
  auto *ToDecl = cast<T>(*ToDeclOrError);
721
67
  assert(hasBodyOrInit(ToDecl) && "Imported Decl should have body or init.");
722
67
  ++NumGetCTUSuccess;
723
724
  // Parent map is invalidated after changing the AST.
725
67
  ToDecl->getASTContext().getParentMapContext().clear();
726
727
67
  return ToDecl;
728
67
}
llvm::Expected<clang::FunctionDecl const*> clang::cross_tu::CrossTranslationUnitContext::importDefinitionImpl<clang::FunctionDecl>(clang::FunctionDecl const*, clang::ASTUnit*)
Line
Count
Source
695
46
CrossTranslationUnitContext::importDefinitionImpl(const T *D, ASTUnit *Unit) {
696
46
  assert(hasBodyOrInit(D) && "Decls to be imported should have body or init.");
697
698
46
  assert(&D->getASTContext() == &Unit->getASTContext() &&
699
46
         "ASTContext of Decl and the unit should match.");
700
46
  ASTImporter &Importer = getOrCreateASTImporter(Unit);
701
702
46
  auto ToDeclOrError = Importer.Import(D);
703
46
  if (!ToDeclOrError) {
704
1
    handleAllErrors(ToDeclOrError.takeError(),
705
1
                    [&](const ImportError &IE) {
706
1
                      switch (IE.Error) {
707
1
                      case ImportError::NameConflict:
708
1
                        ++NumNameConflicts;
709
1
                         break;
710
1
                      case ImportError::UnsupportedConstruct:
711
1
                        ++NumUnsupportedNodeFound;
712
1
                        break;
713
1
                      case ImportError::Unknown:
714
1
                        llvm_unreachable("Unknown import error happened.");
715
1
                        break;
716
1
                      }
717
1
                    });
718
1
    return llvm::make_error<IndexError>(index_error_code::failed_import);
719
1
  }
720
45
  auto *ToDecl = cast<T>(*ToDeclOrError);
721
45
  assert(hasBodyOrInit(ToDecl) && "Imported Decl should have body or init.");
722
45
  ++NumGetCTUSuccess;
723
724
  // Parent map is invalidated after changing the AST.
725
45
  ToDecl->getASTContext().getParentMapContext().clear();
726
727
45
  return ToDecl;
728
45
}
llvm::Expected<clang::VarDecl const*> clang::cross_tu::CrossTranslationUnitContext::importDefinitionImpl<clang::VarDecl>(clang::VarDecl const*, clang::ASTUnit*)
Line
Count
Source
695
22
CrossTranslationUnitContext::importDefinitionImpl(const T *D, ASTUnit *Unit) {
696
22
  assert(hasBodyOrInit(D) && "Decls to be imported should have body or init.");
697
698
22
  assert(&D->getASTContext() == &Unit->getASTContext() &&
699
22
         "ASTContext of Decl and the unit should match.");
700
22
  ASTImporter &Importer = getOrCreateASTImporter(Unit);
701
702
22
  auto ToDeclOrError = Importer.Import(D);
703
22
  if (!ToDeclOrError) {
704
0
    handleAllErrors(ToDeclOrError.takeError(),
705
0
                    [&](const ImportError &IE) {
706
0
                      switch (IE.Error) {
707
0
                      case ImportError::NameConflict:
708
0
                        ++NumNameConflicts;
709
0
                         break;
710
0
                      case ImportError::UnsupportedConstruct:
711
0
                        ++NumUnsupportedNodeFound;
712
0
                        break;
713
0
                      case ImportError::Unknown:
714
0
                        llvm_unreachable("Unknown import error happened.");
715
0
                        break;
716
0
                      }
717
0
                    });
718
0
    return llvm::make_error<IndexError>(index_error_code::failed_import);
719
0
  }
720
22
  auto *ToDecl = cast<T>(*ToDeclOrError);
721
22
  assert(hasBodyOrInit(ToDecl) && "Imported Decl should have body or init.");
722
22
  ++NumGetCTUSuccess;
723
724
  // Parent map is invalidated after changing the AST.
725
22
  ToDecl->getASTContext().getParentMapContext().clear();
726
727
22
  return ToDecl;
728
22
}
729
730
llvm::Expected<const FunctionDecl *>
731
CrossTranslationUnitContext::importDefinition(const FunctionDecl *FD,
732
46
                                              ASTUnit *Unit) {
733
46
  return importDefinitionImpl(FD, Unit);
734
46
}
735
736
llvm::Expected<const VarDecl *>
737
CrossTranslationUnitContext::importDefinition(const VarDecl *VD,
738
22
                                              ASTUnit *Unit) {
739
22
  return importDefinitionImpl(VD, Unit);
740
22
}
741
742
void CrossTranslationUnitContext::lazyInitImporterSharedSt(
743
9
    TranslationUnitDecl *ToTU) {
744
9
  if (!ImporterSharedSt)
745
7
    ImporterSharedSt = std::make_shared<ASTImporterSharedState>(*ToTU);
746
9
}
747
748
ASTImporter &
749
68
CrossTranslationUnitContext::getOrCreateASTImporter(ASTUnit *Unit) {
750
68
  ASTContext &From = Unit->getASTContext();
751
752
68
  auto I = ASTUnitImporterMap.find(From.getTranslationUnitDecl());
753
68
  if (I != ASTUnitImporterMap.end())
754
59
    return *I->second;
755
9
  lazyInitImporterSharedSt(Context.getTranslationUnitDecl());
756
9
  ASTImporter *NewImporter = new ASTImporter(
757
9
      Context, Context.getSourceManager().getFileManager(), From,
758
9
      From.getSourceManager().getFileManager(), false, ImporterSharedSt);
759
20
  NewImporter->setFileIDImportHandler([this, Unit](FileID ToID, FileID FromID) {
760
20
    assert(ImportedFileIDs.find(ToID) == ImportedFileIDs.end() &&
761
20
           "FileID already imported, should not happen.");
762
20
    ImportedFileIDs[ToID] = std::make_pair(FromID, Unit);
763
20
  });
764
9
  ASTUnitImporterMap[From.getTranslationUnitDecl()].reset(NewImporter);
765
9
  return *NewImporter;
766
9
}
767
768
llvm::Optional<std::pair<SourceLocation, ASTUnit *>>
769
CrossTranslationUnitContext::getImportedFromSourceLocation(
770
46
    const clang::SourceLocation &ToLoc) const {
771
46
  const SourceManager &SM = Context.getSourceManager();
772
46
  auto DecToLoc = SM.getDecomposedLoc(ToLoc);
773
774
46
  auto I = ImportedFileIDs.find(DecToLoc.first);
775
46
  if (I == ImportedFileIDs.end())
776
39
    return {};
777
778
7
  FileID FromID = I->second.first;
779
7
  clang::ASTUnit *Unit = I->second.second;
780
7
  SourceLocation FromLoc =
781
7
      Unit->getSourceManager().getComposedLoc(FromID, DecToLoc.second);
782
783
7
  return std::make_pair(FromLoc, Unit);
784
7
}
785
786
} // namespace cross_tu
787
} // namespace clang