Coverage Report

Created: 2020-02-18 08: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/Basic/TargetInfo.h"
16
#include "clang/CrossTU/CrossTUDiagnostic.h"
17
#include "clang/Frontend/ASTUnit.h"
18
#include "clang/Frontend/CompilerInstance.h"
19
#include "clang/Frontend/TextDiagnosticPrinter.h"
20
#include "clang/Index/USRGeneration.h"
21
#include "llvm/ADT/Triple.h"
22
#include "llvm/ADT/Statistic.h"
23
#include "llvm/Support/ErrorHandling.h"
24
#include "llvm/Support/ManagedStatic.h"
25
#include "llvm/Support/Path.h"
26
#include "llvm/Support/raw_ostream.h"
27
#include <fstream>
28
#include <sstream>
29
30
namespace clang {
31
namespace cross_tu {
32
33
namespace {
34
35
#define DEBUG_TYPE "CrossTranslationUnit"
36
STATISTIC(NumGetCTUCalled, "The # of getCTUDefinition function called");
37
STATISTIC(
38
    NumNotInOtherTU,
39
    "The # of getCTUDefinition called but the function is not in any other TU");
40
STATISTIC(NumGetCTUSuccess,
41
          "The # of getCTUDefinition successfully returned the "
42
          "requested function's body");
43
STATISTIC(NumUnsupportedNodeFound, "The # of imports when the ASTImporter "
44
                                   "encountered an unsupported AST Node");
45
STATISTIC(NumNameConflicts, "The # of imports when the ASTImporter "
46
                            "encountered an ODR error");
47
STATISTIC(NumTripleMismatch, "The # of triple mismatches");
48
STATISTIC(NumLangMismatch, "The # of language mismatches");
49
STATISTIC(NumLangDialectMismatch, "The # of language dialect mismatches");
50
STATISTIC(NumASTLoadThresholdReached,
51
          "The # of ASTs not loaded because of threshold");
52
53
// Same as Triple's equality operator, but we check a field only if that is
54
// known in both instances.
55
67
bool hasEqualKnownFields(const llvm::Triple &Lhs, const llvm::Triple &Rhs) {
56
67
  using llvm::Triple;
57
67
  if (Lhs.getArch() != Triple::UnknownArch &&
58
67
      Rhs.getArch() != Triple::UnknownArch && Lhs.getArch() != Rhs.getArch())
59
1
    return false;
60
66
  if (Lhs.getSubArch() != Triple::NoSubArch &&
61
66
      
Rhs.getSubArch() != Triple::NoSubArch0
&&
62
66
      
Lhs.getSubArch() != Rhs.getSubArch()0
)
63
0
    return false;
64
66
  if (Lhs.getVendor() != Triple::UnknownVendor &&
65
66
      
Rhs.getVendor() != Triple::UnknownVendor65
&&
66
66
      
Lhs.getVendor() != Rhs.getVendor()65
)
67
0
    return false;
68
66
  if (!Lhs.isOSUnknown() && !Rhs.isOSUnknown() &&
69
66
      Lhs.getOS() != Rhs.getOS())
70
0
    return false;
71
66
  if (Lhs.getEnvironment() != Triple::UnknownEnvironment &&
72
66
      
Rhs.getEnvironment() != Triple::UnknownEnvironment61
&&
73
66
      
Lhs.getEnvironment() != Rhs.getEnvironment()61
)
74
0
    return false;
75
66
  if (Lhs.getObjectFormat() != Triple::UnknownObjectFormat &&
76
66
      Rhs.getObjectFormat() != Triple::UnknownObjectFormat &&
77
66
      Lhs.getObjectFormat() != Rhs.getObjectFormat())
78
0
    return false;
79
66
  return true;
80
66
}
81
82
// FIXME: This class is will be removed after the transition to llvm::Error.
83
class IndexErrorCategory : public std::error_category {
84
public:
85
0
  const char *name() const noexcept override { return "clang.index"; }
86
87
0
  std::string message(int Condition) const override {
88
0
    switch (static_cast<index_error_code>(Condition)) {
89
0
    case index_error_code::unspecified:
90
0
      return "An unknown error has occurred.";
91
0
    case index_error_code::missing_index_file:
92
0
      return "The index file is missing.";
93
0
    case index_error_code::invalid_index_format:
94
0
      return "Invalid index file format.";
95
0
    case index_error_code::multiple_definitions:
96
0
      return "Multiple definitions in the index file.";
97
0
    case index_error_code::missing_definition:
98
0
      return "Missing definition from the index file.";
99
0
    case index_error_code::failed_import:
100
0
      return "Failed to import the definition.";
101
0
    case index_error_code::failed_to_get_external_ast:
102
0
      return "Failed to load external AST source.";
103
0
    case index_error_code::failed_to_generate_usr:
104
0
      return "Failed to generate USR.";
105
0
    case index_error_code::triple_mismatch:
106
0
      return "Triple mismatch";
107
0
    case index_error_code::lang_mismatch:
108
0
      return "Language mismatch";
109
0
    case index_error_code::lang_dialect_mismatch:
110
0
      return "Language dialect mismatch";
111
0
    case index_error_code::load_threshold_reached:
112
0
      return "Load threshold reached";
113
0
    }
114
0
    llvm_unreachable("Unrecognized index_error_code.");
115
0
  }
116
};
117
118
static llvm::ManagedStatic<IndexErrorCategory> Category;
119
} // end anonymous namespace
120
121
char IndexError::ID;
122
123
0
void IndexError::log(raw_ostream &OS) const {
124
0
  OS << Category->message(static_cast<int>(Code)) << '\n';
125
0
}
126
127
0
std::error_code IndexError::convertToErrorCode() const {
128
0
  return std::error_code(static_cast<int>(Code), *Category);
129
0
}
130
131
llvm::Expected<llvm::StringMap<std::string>>
132
10
parseCrossTUIndex(StringRef IndexPath, StringRef CrossTUDir) {
133
10
  std::ifstream ExternalMapFile{std::string(IndexPath)};
134
10
  if (!ExternalMapFile)
135
0
    return llvm::make_error<IndexError>(index_error_code::missing_index_file,
136
0
                                        IndexPath.str());
137
10
138
10
  llvm::StringMap<std::string> Result;
139
10
  std::string Line;
140
10
  unsigned LineNo = 1;
141
146
  while (std::getline(ExternalMapFile, Line)) {
142
136
    const size_t Pos = Line.find(" ");
143
136
    if (Pos > 0 && Pos != std::string::npos) {
144
136
      StringRef LineRef{Line};
145
136
      StringRef LookupName = LineRef.substr(0, Pos);
146
136
      if (Result.count(LookupName))
147
0
        return llvm::make_error<IndexError>(
148
0
            index_error_code::multiple_definitions, IndexPath.str(), LineNo);
149
136
      StringRef FileName = LineRef.substr(Pos + 1);
150
136
      SmallString<256> FilePath = CrossTUDir;
151
136
      llvm::sys::path::append(FilePath, FileName);
152
136
      Result[LookupName] = std::string(FilePath);
153
136
    } else
154
0
      return llvm::make_error<IndexError>(
155
0
          index_error_code::invalid_index_format, IndexPath.str(), LineNo);
156
136
    LineNo++;
157
136
  }
158
10
  return Result;
159
10
}
160
161
std::string
162
3
createCrossTUIndexString(const llvm::StringMap<std::string> &Index) {
163
3
  std::ostringstream Result;
164
3
  for (const auto &E : Index)
165
11
    Result << E.getKey().str() << " " << E.getValue() << '\n';
166
3
  return Result.str();
167
3
}
168
169
43
bool containsConst(const VarDecl *VD, const ASTContext &ACtx) {
170
43
  CanQualType CT = ACtx.getCanonicalType(VD->getType());
171
43
  if (!CT.isConstQualified()) {
172
19
    const RecordType *RTy = CT->getAs<RecordType>();
173
19
    if (!RTy || 
!RTy->hasConstFields()18
)
174
2
      return false;
175
41
  }
176
41
  return true;
177
41
}
178
179
814
static bool hasBodyOrInit(const FunctionDecl *D, const FunctionDecl *&DefD) {
180
814
  return D->hasBody(DefD);
181
814
}
182
584
static bool hasBodyOrInit(const VarDecl *D, const VarDecl *&DefD) {
183
584
  return D->getAnyInitializer(DefD);
184
584
}
185
205
template <typename T> static bool hasBodyOrInit(const T *D) {
186
205
  const T *Unused;
187
205
  return hasBodyOrInit(D, Unused);
188
205
}
CrossTranslationUnit.cpp:bool clang::cross_tu::hasBodyOrInit<clang::FunctionDecl>(clang::FunctionDecl const*)
Line
Count
Source
185
139
template <typename T> static bool hasBodyOrInit(const T *D) {
186
139
  const T *Unused;
187
139
  return hasBodyOrInit(D, Unused);
188
139
}
CrossTranslationUnit.cpp:bool clang::cross_tu::hasBodyOrInit<clang::VarDecl>(clang::VarDecl const*)
Line
Count
Source
185
66
template <typename T> static bool hasBodyOrInit(const T *D) {
186
66
  const T *Unused;
187
66
  return hasBodyOrInit(D, Unused);
188
66
}
189
190
CrossTranslationUnitContext::CrossTranslationUnitContext(CompilerInstance &CI)
191
1.25k
    : Context(CI.getASTContext()), ASTStorage(CI) {}
192
193
1.25k
CrossTranslationUnitContext::~CrossTranslationUnitContext() {}
194
195
llvm::Optional<std::string>
196
675
CrossTranslationUnitContext::getLookupName(const NamedDecl *ND) {
197
675
  SmallString<128> DeclUSR;
198
675
  bool Ret = index::generateUSRForDecl(ND, DeclUSR);
199
675
  if (Ret)
200
3
    return {};
201
672
  return std::string(DeclUSR.str());
202
672
}
203
204
/// Recursively visits the decls of a DeclContext, and returns one with the
205
/// given USR.
206
template <typename T>
207
const T *
208
CrossTranslationUnitContext::findDefInDeclContext(const DeclContext *DC,
209
2.50k
                                                  StringRef LookupName) {
210
2.50k
  assert(DC && "Declaration Context must not be null");
211
4.05k
  for (const Decl *D : DC->decls()) {
212
4.05k
    const auto *SubDC = dyn_cast<DeclContext>(D);
213
4.05k
    if (SubDC)
214
2.43k
      if (const auto *ND = findDefInDeclContext<T>(SubDC, LookupName))
215
26
        return ND;
216
4.03k
217
4.03k
    const auto *ND = dyn_cast<T>(D);
218
4.03k
    const T *ResultDecl;
219
4.03k
    if (!ND || 
!hasBodyOrInit(ND, ResultDecl)1.19k
)
220
3.44k
      continue;
221
591
    llvm::Optional<std::string> ResultLookupName = getLookupName(ResultDecl);
222
591
    if (!ResultLookupName || 
*ResultLookupName != LookupName589
)
223
525
      continue;
224
66
    return ResultDecl;
225
66
  }
226
2.50k
  
return nullptr2.40k
;
227
2.50k
}
clang::FunctionDecl const* clang::cross_tu::CrossTranslationUnitContext::findDefInDeclContext<clang::FunctionDecl>(clang::DeclContext const*, llvm::StringRef)
Line
Count
Source
209
1.04k
                                                  StringRef LookupName) {
210
1.04k
  assert(DC && "Declaration Context must not be null");
211
1.76k
  for (const Decl *D : DC->decls()) {
212
1.76k
    const auto *SubDC = dyn_cast<DeclContext>(D);
213
1.76k
    if (SubDC)
214
997
      if (const auto *ND = findDefInDeclContext<T>(SubDC, LookupName))
215
18
        return ND;
216
1.74k
217
1.74k
    const auto *ND = dyn_cast<T>(D);
218
1.74k
    const T *ResultDecl;
219
1.74k
    if (!ND || 
!hasBodyOrInit(ND, ResultDecl)675
)
220
1.33k
      continue;
221
411
    llvm::Optional<std::string> ResultLookupName = getLookupName(ResultDecl);
222
411
    if (!ResultLookupName || *ResultLookupName != LookupName)
223
367
      continue;
224
44
    return ResultDecl;
225
44
  }
226
1.04k
  
return nullptr979
;
227
1.04k
}
clang::VarDecl const* clang::cross_tu::CrossTranslationUnitContext::findDefInDeclContext<clang::VarDecl>(clang::DeclContext const*, llvm::StringRef)
Line
Count
Source
209
1.46k
                                                  StringRef LookupName) {
210
1.46k
  assert(DC && "Declaration Context must not be null");
211
2.29k
  for (const Decl *D : DC->decls()) {
212
2.29k
    const auto *SubDC = dyn_cast<DeclContext>(D);
213
2.29k
    if (SubDC)
214
1.43k
      if (const auto *ND = findDefInDeclContext<T>(SubDC, LookupName))
215
8
        return ND;
216
2.28k
217
2.28k
    const auto *ND = dyn_cast<T>(D);
218
2.28k
    const T *ResultDecl;
219
2.28k
    if (!ND || 
!hasBodyOrInit(ND, ResultDecl)518
)
220
2.10k
      continue;
221
180
    llvm::Optional<std::string> ResultLookupName = getLookupName(ResultDecl);
222
180
    if (!ResultLookupName || 
*ResultLookupName != LookupName178
)
223
158
      continue;
224
22
    return ResultDecl;
225
22
  }
226
1.46k
  
return nullptr1.43k
;
227
1.46k
}
228
229
template <typename T>
230
llvm::Expected<const T *> CrossTranslationUnitContext::getCrossTUDefinitionImpl(
231
    const T *D, StringRef CrossTUDir, StringRef IndexName,
232
74
    bool DisplayCTUProgress) {
233
74
  assert(D && "D is missing, bad call to this function!");
234
74
  assert(!hasBodyOrInit(D) &&
235
74
         "D has a body or init in current translation unit!");
236
74
  ++NumGetCTUCalled;
237
74
  const llvm::Optional<std::string> LookupName = getLookupName(D);
238
74
  if (!LookupName)
239
0
    return llvm::make_error<IndexError>(
240
0
        index_error_code::failed_to_generate_usr);
241
74
  llvm::Expected<ASTUnit *> ASTUnitOrError =
242
74
      loadExternalAST(*LookupName, CrossTUDir, IndexName, DisplayCTUProgress);
243
74
  if (!ASTUnitOrError)
244
7
    return ASTUnitOrError.takeError();
245
67
  ASTUnit *Unit = *ASTUnitOrError;
246
67
  assert(&Unit->getFileManager() ==
247
67
         &Unit->getASTContext().getSourceManager().getFileManager());
248
67
249
67
  const llvm::Triple &TripleTo = Context.getTargetInfo().getTriple();
250
67
  const llvm::Triple &TripleFrom =
251
67
      Unit->getASTContext().getTargetInfo().getTriple();
252
67
  // The imported AST had been generated for a different target.
253
67
  // Some parts of the triple in the loaded ASTContext can be unknown while the
254
67
  // very same parts in the target ASTContext are known. Thus we check for the
255
67
  // known parts only.
256
67
  if (!hasEqualKnownFields(TripleTo, TripleFrom)) {
257
1
    // TODO: Pass the SourceLocation of the CallExpression for more precise
258
1
    // diagnostics.
259
1
    ++NumTripleMismatch;
260
1
    return llvm::make_error<IndexError>(index_error_code::triple_mismatch,
261
1
                                        std::string(Unit->getMainFileName()),
262
1
                                        TripleTo.str(), TripleFrom.str());
263
1
  }
264
66
265
66
  const auto &LangTo = Context.getLangOpts();
266
66
  const auto &LangFrom = Unit->getASTContext().getLangOpts();
267
66
268
66
  // FIXME: Currenty we do not support CTU across C++ and C and across
269
66
  // different dialects of C++.
270
66
  if (LangTo.CPlusPlus != LangFrom.CPlusPlus) {
271
0
    ++NumLangMismatch;
272
0
    return llvm::make_error<IndexError>(index_error_code::lang_mismatch);
273
0
  }
274
66
275
66
  // If CPP dialects are different then return with error.
276
66
  //
277
66
  // Consider this STL code:
278
66
  //   template<typename _Alloc>
279
66
  //     struct __alloc_traits
280
66
  //   #if __cplusplus >= 201103L
281
66
  //     : std::allocator_traits<_Alloc>
282
66
  //   #endif
283
66
  //     { // ...
284
66
  //     };
285
66
  // This class template would create ODR errors during merging the two units,
286
66
  // since in one translation unit the class template has a base class, however
287
66
  // in the other unit it has none.
288
66
  if (LangTo.CPlusPlus11 != LangFrom.CPlusPlus11 ||
289
66
      LangTo.CPlusPlus14 != LangFrom.CPlusPlus14 ||
290
66
      LangTo.CPlusPlus17 != LangFrom.CPlusPlus17 ||
291
66
      LangTo.CPlusPlus2a != LangFrom.CPlusPlus2a) {
292
0
    ++NumLangDialectMismatch;
293
0
    return llvm::make_error<IndexError>(
294
0
        index_error_code::lang_dialect_mismatch);
295
0
  }
296
66
297
66
  TranslationUnitDecl *TU = Unit->getASTContext().getTranslationUnitDecl();
298
66
  if (const T *ResultDecl = findDefInDeclContext<T>(TU, *LookupName))
299
66
    return importDefinition(ResultDecl, Unit);
300
0
  return llvm::make_error<IndexError>(index_error_code::failed_import);
301
0
}
llvm::Expected<clang::FunctionDecl const*> clang::cross_tu::CrossTranslationUnitContext::getCrossTUDefinitionImpl<clang::FunctionDecl>(clang::FunctionDecl const*, llvm::StringRef, llvm::StringRef, bool)
Line
Count
Source
232
52
    bool DisplayCTUProgress) {
233
52
  assert(D && "D is missing, bad call to this function!");
234
52
  assert(!hasBodyOrInit(D) &&
235
52
         "D has a body or init in current translation unit!");
236
52
  ++NumGetCTUCalled;
237
52
  const llvm::Optional<std::string> LookupName = getLookupName(D);
238
52
  if (!LookupName)
239
0
    return llvm::make_error<IndexError>(
240
0
        index_error_code::failed_to_generate_usr);
241
52
  llvm::Expected<ASTUnit *> ASTUnitOrError =
242
52
      loadExternalAST(*LookupName, CrossTUDir, IndexName, DisplayCTUProgress);
243
52
  if (!ASTUnitOrError)
244
7
    return ASTUnitOrError.takeError();
245
45
  ASTUnit *Unit = *ASTUnitOrError;
246
45
  assert(&Unit->getFileManager() ==
247
45
         &Unit->getASTContext().getSourceManager().getFileManager());
248
45
249
45
  const llvm::Triple &TripleTo = Context.getTargetInfo().getTriple();
250
45
  const llvm::Triple &TripleFrom =
251
45
      Unit->getASTContext().getTargetInfo().getTriple();
252
45
  // The imported AST had been generated for a different target.
253
45
  // Some parts of the triple in the loaded ASTContext can be unknown while the
254
45
  // very same parts in the target ASTContext are known. Thus we check for the
255
45
  // known parts only.
256
45
  if (!hasEqualKnownFields(TripleTo, TripleFrom)) {
257
1
    // TODO: Pass the SourceLocation of the CallExpression for more precise
258
1
    // diagnostics.
259
1
    ++NumTripleMismatch;
260
1
    return llvm::make_error<IndexError>(index_error_code::triple_mismatch,
261
1
                                        std::string(Unit->getMainFileName()),
262
1
                                        TripleTo.str(), TripleFrom.str());
263
1
  }
264
44
265
44
  const auto &LangTo = Context.getLangOpts();
266
44
  const auto &LangFrom = Unit->getASTContext().getLangOpts();
267
44
268
44
  // FIXME: Currenty we do not support CTU across C++ and C and across
269
44
  // different dialects of C++.
270
44
  if (LangTo.CPlusPlus != LangFrom.CPlusPlus) {
271
0
    ++NumLangMismatch;
272
0
    return llvm::make_error<IndexError>(index_error_code::lang_mismatch);
273
0
  }
274
44
275
44
  // If CPP dialects are different then return with error.
276
44
  //
277
44
  // Consider this STL code:
278
44
  //   template<typename _Alloc>
279
44
  //     struct __alloc_traits
280
44
  //   #if __cplusplus >= 201103L
281
44
  //     : std::allocator_traits<_Alloc>
282
44
  //   #endif
283
44
  //     { // ...
284
44
  //     };
285
44
  // This class template would create ODR errors during merging the two units,
286
44
  // since in one translation unit the class template has a base class, however
287
44
  // in the other unit it has none.
288
44
  if (LangTo.CPlusPlus11 != LangFrom.CPlusPlus11 ||
289
44
      LangTo.CPlusPlus14 != LangFrom.CPlusPlus14 ||
290
44
      LangTo.CPlusPlus17 != LangFrom.CPlusPlus17 ||
291
44
      LangTo.CPlusPlus2a != LangFrom.CPlusPlus2a) {
292
0
    ++NumLangDialectMismatch;
293
0
    return llvm::make_error<IndexError>(
294
0
        index_error_code::lang_dialect_mismatch);
295
0
  }
296
44
297
44
  TranslationUnitDecl *TU = Unit->getASTContext().getTranslationUnitDecl();
298
44
  if (const T *ResultDecl = findDefInDeclContext<T>(TU, *LookupName))
299
44
    return importDefinition(ResultDecl, Unit);
300
0
  return llvm::make_error<IndexError>(index_error_code::failed_import);
301
0
}
llvm::Expected<clang::VarDecl const*> clang::cross_tu::CrossTranslationUnitContext::getCrossTUDefinitionImpl<clang::VarDecl>(clang::VarDecl const*, llvm::StringRef, llvm::StringRef, bool)
Line
Count
Source
232
22
    bool DisplayCTUProgress) {
233
22
  assert(D && "D is missing, bad call to this function!");
234
22
  assert(!hasBodyOrInit(D) &&
235
22
         "D has a body or init in current translation unit!");
236
22
  ++NumGetCTUCalled;
237
22
  const llvm::Optional<std::string> LookupName = getLookupName(D);
238
22
  if (!LookupName)
239
0
    return llvm::make_error<IndexError>(
240
0
        index_error_code::failed_to_generate_usr);
241
22
  llvm::Expected<ASTUnit *> ASTUnitOrError =
242
22
      loadExternalAST(*LookupName, CrossTUDir, IndexName, DisplayCTUProgress);
243
22
  if (!ASTUnitOrError)
244
0
    return ASTUnitOrError.takeError();
245
22
  ASTUnit *Unit = *ASTUnitOrError;
246
22
  assert(&Unit->getFileManager() ==
247
22
         &Unit->getASTContext().getSourceManager().getFileManager());
248
22
249
22
  const llvm::Triple &TripleTo = Context.getTargetInfo().getTriple();
250
22
  const llvm::Triple &TripleFrom =
251
22
      Unit->getASTContext().getTargetInfo().getTriple();
252
22
  // The imported AST had been generated for a different target.
253
22
  // Some parts of the triple in the loaded ASTContext can be unknown while the
254
22
  // very same parts in the target ASTContext are known. Thus we check for the
255
22
  // known parts only.
256
22
  if (!hasEqualKnownFields(TripleTo, TripleFrom)) {
257
0
    // TODO: Pass the SourceLocation of the CallExpression for more precise
258
0
    // diagnostics.
259
0
    ++NumTripleMismatch;
260
0
    return llvm::make_error<IndexError>(index_error_code::triple_mismatch,
261
0
                                        std::string(Unit->getMainFileName()),
262
0
                                        TripleTo.str(), TripleFrom.str());
263
0
  }
264
22
265
22
  const auto &LangTo = Context.getLangOpts();
266
22
  const auto &LangFrom = Unit->getASTContext().getLangOpts();
267
22
268
22
  // FIXME: Currenty we do not support CTU across C++ and C and across
269
22
  // different dialects of C++.
270
22
  if (LangTo.CPlusPlus != LangFrom.CPlusPlus) {
271
0
    ++NumLangMismatch;
272
0
    return llvm::make_error<IndexError>(index_error_code::lang_mismatch);
273
0
  }
274
22
275
22
  // If CPP dialects are different then return with error.
276
22
  //
277
22
  // Consider this STL code:
278
22
  //   template<typename _Alloc>
279
22
  //     struct __alloc_traits
280
22
  //   #if __cplusplus >= 201103L
281
22
  //     : std::allocator_traits<_Alloc>
282
22
  //   #endif
283
22
  //     { // ...
284
22
  //     };
285
22
  // This class template would create ODR errors during merging the two units,
286
22
  // since in one translation unit the class template has a base class, however
287
22
  // in the other unit it has none.
288
22
  if (LangTo.CPlusPlus11 != LangFrom.CPlusPlus11 ||
289
22
      LangTo.CPlusPlus14 != LangFrom.CPlusPlus14 ||
290
22
      LangTo.CPlusPlus17 != LangFrom.CPlusPlus17 ||
291
22
      LangTo.CPlusPlus2a != LangFrom.CPlusPlus2a) {
292
0
    ++NumLangDialectMismatch;
293
0
    return llvm::make_error<IndexError>(
294
0
        index_error_code::lang_dialect_mismatch);
295
0
  }
296
22
297
22
  TranslationUnitDecl *TU = Unit->getASTContext().getTranslationUnitDecl();
298
22
  if (const T *ResultDecl = findDefInDeclContext<T>(TU, *LookupName))
299
22
    return importDefinition(ResultDecl, Unit);
300
0
  return llvm::make_error<IndexError>(index_error_code::failed_import);
301
0
}
302
303
llvm::Expected<const FunctionDecl *>
304
CrossTranslationUnitContext::getCrossTUDefinition(const FunctionDecl *FD,
305
                                                  StringRef CrossTUDir,
306
                                                  StringRef IndexName,
307
52
                                                  bool DisplayCTUProgress) {
308
52
  return getCrossTUDefinitionImpl(FD, CrossTUDir, IndexName,
309
52
                                  DisplayCTUProgress);
310
52
}
311
312
llvm::Expected<const VarDecl *>
313
CrossTranslationUnitContext::getCrossTUDefinition(const VarDecl *VD,
314
                                                  StringRef CrossTUDir,
315
                                                  StringRef IndexName,
316
22
                                                  bool DisplayCTUProgress) {
317
22
  return getCrossTUDefinitionImpl(VD, CrossTUDir, IndexName,
318
22
                                  DisplayCTUProgress);
319
22
}
320
321
8
void CrossTranslationUnitContext::emitCrossTUDiagnostics(const IndexError &IE) {
322
8
  switch (IE.getCode()) {
323
0
  case index_error_code::missing_index_file:
324
0
    Context.getDiagnostics().Report(diag::err_ctu_error_opening)
325
0
        << IE.getFileName();
326
0
    break;
327
0
  case index_error_code::invalid_index_format:
328
0
    Context.getDiagnostics().Report(diag::err_extdefmap_parsing)
329
0
        << IE.getFileName() << IE.getLineNum();
330
0
    break;
331
0
  case index_error_code::multiple_definitions:
332
0
    Context.getDiagnostics().Report(diag::err_multiple_def_index)
333
0
        << IE.getLineNum();
334
0
    break;
335
1
  case index_error_code::triple_mismatch:
336
1
    Context.getDiagnostics().Report(diag::warn_ctu_incompat_triple)
337
1
        << IE.getFileName() << IE.getTripleToName() << IE.getTripleFromName();
338
1
    break;
339
7
  default:
340
7
    break;
341
8
  }
342
8
}
343
344
CrossTranslationUnitContext::ASTFileLoader::ASTFileLoader(
345
    const CompilerInstance &CI)
346
1.25k
    : CI(CI) {}
347
348
std::unique_ptr<ASTUnit>
349
9
CrossTranslationUnitContext::ASTFileLoader::operator()(StringRef ASTFilePath) {
350
9
  // Load AST from ast-dump.
351
9
  IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts = new DiagnosticOptions();
352
9
  TextDiagnosticPrinter *DiagClient =
353
9
      new TextDiagnosticPrinter(llvm::errs(), &*DiagOpts);
354
9
  IntrusiveRefCntPtr<DiagnosticIDs> DiagID(new DiagnosticIDs());
355
9
  IntrusiveRefCntPtr<DiagnosticsEngine> Diags(
356
9
      new DiagnosticsEngine(DiagID, &*DiagOpts, DiagClient));
357
9
358
9
  return ASTUnit::LoadFromASTFile(
359
9
      std::string(ASTFilePath), CI.getPCHContainerOperations()->getRawReader(),
360
9
      ASTUnit::LoadEverything, Diags, CI.getFileSystemOpts());
361
9
}
362
363
CrossTranslationUnitContext::ASTUnitStorage::ASTUnitStorage(
364
    const CompilerInstance &CI)
365
    : FileAccessor(CI), LoadGuard(const_cast<CompilerInstance &>(CI)
366
                                      .getAnalyzerOpts()
367
1.25k
                                      ->CTUImportThreshold) {}
368
369
llvm::Expected<ASTUnit *>
370
CrossTranslationUnitContext::ASTUnitStorage::getASTUnitForFile(
371
68
    StringRef FileName, bool DisplayCTUProgress) {
372
68
  // Try the cache first.
373
68
  auto ASTCacheEntry = FileASTUnitMap.find(FileName);
374
68
  if (ASTCacheEntry == FileASTUnitMap.end()) {
375
10
376
10
    // Do not load if the limit is reached.
377
10
    if (!LoadGuard) {
378
1
      ++NumASTLoadThresholdReached;
379
1
      return llvm::make_error<IndexError>(
380
1
          index_error_code::load_threshold_reached);
381
1
    }
382
9
383
9
    // Load the ASTUnit from the pre-dumped AST file specified by ASTFileName.
384
9
    std::unique_ptr<ASTUnit> LoadedUnit = FileAccessor(FileName);
385
9
386
9
    // Need the raw pointer and the unique_ptr as well.
387
9
    ASTUnit *Unit = LoadedUnit.get();
388
9
389
9
    // Update the cache.
390
9
    FileASTUnitMap[FileName] = std::move(LoadedUnit);
391
9
392
9
    LoadGuard.indicateLoadSuccess();
393
9
394
9
    if (DisplayCTUProgress)
395
2
      llvm::errs() << "CTU loaded AST file: " << FileName << "\n";
396
9
397
9
    return Unit;
398
9
399
58
  } else {
400
58
    // Found in the cache.
401
58
    return ASTCacheEntry->second.get();
402
58
  }
403
68
}
404
405
llvm::Expected<ASTUnit *>
406
CrossTranslationUnitContext::ASTUnitStorage::getASTUnitForFunction(
407
    StringRef FunctionName, StringRef CrossTUDir, StringRef IndexName,
408
74
    bool DisplayCTUProgress) {
409
74
  // Try the cache first.
410
74
  auto ASTCacheEntry = NameASTUnitMap.find(FunctionName);
411
74
  if (ASTCacheEntry == NameASTUnitMap.end()) {
412
74
    // Load the ASTUnit from the pre-dumped AST file specified by ASTFileName.
413
74
414
74
    // Ensure that the Index is loaded, as we need to search in it.
415
74
    if (llvm::Error IndexLoadError =
416
0
            ensureCTUIndexLoaded(CrossTUDir, IndexName))
417
0
      return std::move(IndexLoadError);
418
74
419
74
    // Check if there is and entry in the index for the function.
420
74
    if (!NameFileMap.count(FunctionName)) {
421
6
      ++NumNotInOtherTU;
422
6
      return llvm::make_error<IndexError>(index_error_code::missing_definition);
423
6
    }
424
68
425
68
    // Search in the index for the filename where the definition of FuncitonName
426
68
    // resides.
427
68
    if (llvm::Expected<ASTUnit *> FoundForFile =
428
67
            getASTUnitForFile(NameFileMap[FunctionName], DisplayCTUProgress)) {
429
67
430
67
      // Update the cache.
431
67
      NameASTUnitMap[FunctionName] = *FoundForFile;
432
67
      return *FoundForFile;
433
67
434
67
    } else {
435
1
      return FoundForFile.takeError();
436
1
    }
437
0
  } else {
438
0
    // Found in the cache.
439
0
    return ASTCacheEntry->second;
440
0
  }
441
74
}
442
443
llvm::Expected<std::string>
444
CrossTranslationUnitContext::ASTUnitStorage::getFileForFunction(
445
0
    StringRef FunctionName, StringRef CrossTUDir, StringRef IndexName) {
446
0
  if (llvm::Error IndexLoadError = ensureCTUIndexLoaded(CrossTUDir, IndexName))
447
0
    return std::move(IndexLoadError);
448
0
  return NameFileMap[FunctionName];
449
0
}
450
451
llvm::Error CrossTranslationUnitContext::ASTUnitStorage::ensureCTUIndexLoaded(
452
74
    StringRef CrossTUDir, StringRef IndexName) {
453
74
  // Dont initialize if the map is filled.
454
74
  if (!NameFileMap.empty())
455
66
    return llvm::Error::success();
456
8
457
8
  // Get the absolute path to the index file.
458
8
  SmallString<256> IndexFile = CrossTUDir;
459
8
  if (llvm::sys::path::is_absolute(IndexName))
460
2
    IndexFile = IndexName;
461
6
  else
462
6
    llvm::sys::path::append(IndexFile, IndexName);
463
8
464
8
  if (auto IndexMapping = parseCrossTUIndex(IndexFile, CrossTUDir)) {
465
8
    // Initialize member map.
466
8
    NameFileMap = *IndexMapping;
467
8
    return llvm::Error::success();
468
8
  } else {
469
0
    // Error while parsing CrossTU index file.
470
0
    return IndexMapping.takeError();
471
0
  };
472
0
}
473
474
llvm::Expected<ASTUnit *> CrossTranslationUnitContext::loadExternalAST(
475
    StringRef LookupName, StringRef CrossTUDir, StringRef IndexName,
476
74
    bool DisplayCTUProgress) {
477
74
  // FIXME: The current implementation only supports loading decls with
478
74
  //        a lookup name from a single translation unit. If multiple
479
74
  //        translation units contains decls with the same lookup name an
480
74
  //        error will be returned.
481
74
482
74
  // Try to get the value from the heavily cached storage.
483
74
  llvm::Expected<ASTUnit *> Unit = ASTStorage.getASTUnitForFunction(
484
74
      LookupName, CrossTUDir, IndexName, DisplayCTUProgress);
485
74
486
74
  if (!Unit)
487
7
    return Unit.takeError();
488
67
489
67
  // Check whether the backing pointer of the Expected is a nullptr.
490
67
  if (!*Unit)
491
0
    return llvm::make_error<IndexError>(
492
0
        index_error_code::failed_to_get_external_ast);
493
67
494
67
  return Unit;
495
67
}
496
497
template <typename T>
498
llvm::Expected<const T *>
499
66
CrossTranslationUnitContext::importDefinitionImpl(const T *D, ASTUnit *Unit) {
500
66
  assert(hasBodyOrInit(D) && "Decls to be imported should have body or init.");
501
66
502
66
  assert(&D->getASTContext() == &Unit->getASTContext() &&
503
66
         "ASTContext of Decl and the unit should match.");
504
66
  ASTImporter &Importer = getOrCreateASTImporter(Unit);
505
66
506
66
  auto ToDeclOrError = Importer.Import(D);
507
66
  if (!ToDeclOrError) {
508
1
    handleAllErrors(ToDeclOrError.takeError(),
509
1
                    [&](const ImportError &IE) {
510
1
                      switch (IE.Error) {
511
0
                      case ImportError::NameConflict:
512
0
                        ++NumNameConflicts;
513
0
                         break;
514
1
                      case ImportError::UnsupportedConstruct:
515
1
                        ++NumUnsupportedNodeFound;
516
1
                        break;
517
0
                      case ImportError::Unknown:
518
0
                        llvm_unreachable("Unknown import error happened.");
519
0
                        break;
520
1
                      }
521
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
509
1
                    [&](const ImportError &IE) {
510
1
                      switch (IE.Error) {
511
0
                      case ImportError::NameConflict:
512
0
                        ++NumNameConflicts;
513
0
                         break;
514
1
                      case ImportError::UnsupportedConstruct:
515
1
                        ++NumUnsupportedNodeFound;
516
1
                        break;
517
0
                      case ImportError::Unknown:
518
0
                        llvm_unreachable("Unknown import error happened.");
519
0
                        break;
520
1
                      }
521
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
522
1
    return llvm::make_error<IndexError>(index_error_code::failed_import);
523
1
  }
524
65
  auto *ToDecl = cast<T>(*ToDeclOrError);
525
65
  assert(hasBodyOrInit(ToDecl) && "Imported Decl should have body or init.");
526
65
  ++NumGetCTUSuccess;
527
65
528
65
  return ToDecl;
529
65
}
llvm::Expected<clang::FunctionDecl const*> clang::cross_tu::CrossTranslationUnitContext::importDefinitionImpl<clang::FunctionDecl>(clang::FunctionDecl const*, clang::ASTUnit*)
Line
Count
Source
499
44
CrossTranslationUnitContext::importDefinitionImpl(const T *D, ASTUnit *Unit) {
500
44
  assert(hasBodyOrInit(D) && "Decls to be imported should have body or init.");
501
44
502
44
  assert(&D->getASTContext() == &Unit->getASTContext() &&
503
44
         "ASTContext of Decl and the unit should match.");
504
44
  ASTImporter &Importer = getOrCreateASTImporter(Unit);
505
44
506
44
  auto ToDeclOrError = Importer.Import(D);
507
44
  if (!ToDeclOrError) {
508
1
    handleAllErrors(ToDeclOrError.takeError(),
509
1
                    [&](const ImportError &IE) {
510
1
                      switch (IE.Error) {
511
1
                      case ImportError::NameConflict:
512
1
                        ++NumNameConflicts;
513
1
                         break;
514
1
                      case ImportError::UnsupportedConstruct:
515
1
                        ++NumUnsupportedNodeFound;
516
1
                        break;
517
1
                      case ImportError::Unknown:
518
1
                        llvm_unreachable("Unknown import error happened.");
519
1
                        break;
520
1
                      }
521
1
                    });
522
1
    return llvm::make_error<IndexError>(index_error_code::failed_import);
523
1
  }
524
43
  auto *ToDecl = cast<T>(*ToDeclOrError);
525
43
  assert(hasBodyOrInit(ToDecl) && "Imported Decl should have body or init.");
526
43
  ++NumGetCTUSuccess;
527
43
528
43
  return ToDecl;
529
43
}
llvm::Expected<clang::VarDecl const*> clang::cross_tu::CrossTranslationUnitContext::importDefinitionImpl<clang::VarDecl>(clang::VarDecl const*, clang::ASTUnit*)
Line
Count
Source
499
22
CrossTranslationUnitContext::importDefinitionImpl(const T *D, ASTUnit *Unit) {
500
22
  assert(hasBodyOrInit(D) && "Decls to be imported should have body or init.");
501
22
502
22
  assert(&D->getASTContext() == &Unit->getASTContext() &&
503
22
         "ASTContext of Decl and the unit should match.");
504
22
  ASTImporter &Importer = getOrCreateASTImporter(Unit);
505
22
506
22
  auto ToDeclOrError = Importer.Import(D);
507
22
  if (!ToDeclOrError) {
508
0
    handleAllErrors(ToDeclOrError.takeError(),
509
0
                    [&](const ImportError &IE) {
510
0
                      switch (IE.Error) {
511
0
                      case ImportError::NameConflict:
512
0
                        ++NumNameConflicts;
513
0
                         break;
514
0
                      case ImportError::UnsupportedConstruct:
515
0
                        ++NumUnsupportedNodeFound;
516
0
                        break;
517
0
                      case ImportError::Unknown:
518
0
                        llvm_unreachable("Unknown import error happened.");
519
0
                        break;
520
0
                      }
521
0
                    });
522
0
    return llvm::make_error<IndexError>(index_error_code::failed_import);
523
0
  }
524
22
  auto *ToDecl = cast<T>(*ToDeclOrError);
525
22
  assert(hasBodyOrInit(ToDecl) && "Imported Decl should have body or init.");
526
22
  ++NumGetCTUSuccess;
527
22
528
22
  return ToDecl;
529
22
}
530
531
llvm::Expected<const FunctionDecl *>
532
CrossTranslationUnitContext::importDefinition(const FunctionDecl *FD,
533
44
                                              ASTUnit *Unit) {
534
44
  return importDefinitionImpl(FD, Unit);
535
44
}
536
537
llvm::Expected<const VarDecl *>
538
CrossTranslationUnitContext::importDefinition(const VarDecl *VD,
539
22
                                              ASTUnit *Unit) {
540
22
  return importDefinitionImpl(VD, Unit);
541
22
}
542
543
void CrossTranslationUnitContext::lazyInitImporterSharedSt(
544
8
    TranslationUnitDecl *ToTU) {
545
8
  if (!ImporterSharedSt)
546
6
    ImporterSharedSt = std::make_shared<ASTImporterSharedState>(*ToTU);
547
8
}
548
549
ASTImporter &
550
66
CrossTranslationUnitContext::getOrCreateASTImporter(ASTUnit *Unit) {
551
66
  ASTContext &From = Unit->getASTContext();
552
66
553
66
  auto I = ASTUnitImporterMap.find(From.getTranslationUnitDecl());
554
66
  if (I != ASTUnitImporterMap.end())
555
58
    return *I->second;
556
8
  lazyInitImporterSharedSt(Context.getTranslationUnitDecl());
557
8
  ASTImporter *NewImporter = new ASTImporter(
558
8
      Context, Context.getSourceManager().getFileManager(), From,
559
8
      From.getSourceManager().getFileManager(), false, ImporterSharedSt);
560
19
  NewImporter->setFileIDImportHandler([this, Unit](FileID ToID, FileID FromID) {
561
19
    assert(ImportedFileIDs.find(ToID) == ImportedFileIDs.end() &&
562
19
           "FileID already imported, should not happen.");
563
19
    ImportedFileIDs[ToID] = std::make_pair(FromID, Unit);
564
19
  });
565
8
  ASTUnitImporterMap[From.getTranslationUnitDecl()].reset(NewImporter);
566
8
  return *NewImporter;
567
8
}
568
569
llvm::Optional<std::pair<SourceLocation, ASTUnit *>>
570
CrossTranslationUnitContext::getImportedFromSourceLocation(
571
40
    const clang::SourceLocation &ToLoc) const {
572
40
  const SourceManager &SM = Context.getSourceManager();
573
40
  auto DecToLoc = SM.getDecomposedLoc(ToLoc);
574
40
575
40
  auto I = ImportedFileIDs.find(DecToLoc.first);
576
40
  if (I == ImportedFileIDs.end())
577
33
    return {};
578
7
579
7
  FileID FromID = I->second.first;
580
7
  clang::ASTUnit *Unit = I->second.second;
581
7
  SourceLocation FromLoc =
582
7
      Unit->getSourceManager().getComposedLoc(FromID, DecToLoc.second);
583
7
584
7
  return std::make_pair(FromLoc, Unit);
585
7
}
586
587
} // namespace cross_tu
588
} // namespace clang