Coverage Report

Created: 2022-07-16 07:03

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