Coverage Report

Created: 2021-01-23 06:44

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