blob: 3e787419aa49c0a7017093acad93eafb20d4bcfd [file] [log] [blame]
Gabor Horvathe350b0a2017-09-22 11:11:01 +00001//===--- CrossTranslationUnit.cpp - -----------------------------*- C++ -*-===//
2//
Chandler Carruth2946cd72019-01-19 08:50:56 +00003// 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
Gabor Horvathe350b0a2017-09-22 11:11:01 +00006//
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"
Gabor Horvathe350b0a2017-09-22 11:11:01 +000019#include "clang/Frontend/TextDiagnosticPrinter.h"
20#include "clang/Index/USRGeneration.h"
21#include "llvm/ADT/Triple.h"
Gabor Marton700a29a2018-12-07 11:55:22 +000022#include "llvm/ADT/Statistic.h"
Gabor Horvathe350b0a2017-09-22 11:11:01 +000023#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
30namespace clang {
31namespace cross_tu {
32
33namespace {
Gabor Marton32aff2e2018-12-07 16:32:43 +000034
Gabor Marton700a29a2018-12-07 11:55:22 +000035#define DEBUG_TYPE "CrossTranslationUnit"
36STATISTIC(NumGetCTUCalled, "The # of getCTUDefinition function called");
37STATISTIC(
38 NumNotInOtherTU,
39 "The # of getCTUDefinition called but the function is not in any other TU");
40STATISTIC(NumGetCTUSuccess,
41 "The # of getCTUDefinition successfully returned the "
42 "requested function's body");
Gabor Marton32aff2e2018-12-07 16:32:43 +000043STATISTIC(NumTripleMismatch, "The # of triple mismatches");
44STATISTIC(NumLangMismatch, "The # of language mismatches");
Gabor Martona006b802019-02-28 15:24:59 +000045STATISTIC(NumLangDialectMismatch, "The # of language dialect mismatches");
Gabor Marton32aff2e2018-12-07 16:32:43 +000046
47// Same as Triple's equality operator, but we check a field only if that is
48// known in both instances.
49bool hasEqualKnownFields(const llvm::Triple &Lhs, const llvm::Triple &Rhs) {
50 using llvm::Triple;
51 if (Lhs.getArch() != Triple::UnknownArch &&
52 Rhs.getArch() != Triple::UnknownArch && Lhs.getArch() != Rhs.getArch())
53 return false;
54 if (Lhs.getSubArch() != Triple::NoSubArch &&
55 Rhs.getSubArch() != Triple::NoSubArch &&
56 Lhs.getSubArch() != Rhs.getSubArch())
57 return false;
58 if (Lhs.getVendor() != Triple::UnknownVendor &&
59 Rhs.getVendor() != Triple::UnknownVendor &&
60 Lhs.getVendor() != Rhs.getVendor())
61 return false;
62 if (!Lhs.isOSUnknown() && !Rhs.isOSUnknown() &&
63 Lhs.getOS() != Rhs.getOS())
64 return false;
65 if (Lhs.getEnvironment() != Triple::UnknownEnvironment &&
66 Rhs.getEnvironment() != Triple::UnknownEnvironment &&
67 Lhs.getEnvironment() != Rhs.getEnvironment())
68 return false;
69 if (Lhs.getObjectFormat() != Triple::UnknownObjectFormat &&
70 Rhs.getObjectFormat() != Triple::UnknownObjectFormat &&
71 Lhs.getObjectFormat() != Rhs.getObjectFormat())
72 return false;
73 return true;
74}
Gabor Marton700a29a2018-12-07 11:55:22 +000075
Gabor Horvathe350b0a2017-09-22 11:11:01 +000076// FIXME: This class is will be removed after the transition to llvm::Error.
77class IndexErrorCategory : public std::error_category {
78public:
79 const char *name() const noexcept override { return "clang.index"; }
80
81 std::string message(int Condition) const override {
82 switch (static_cast<index_error_code>(Condition)) {
83 case index_error_code::unspecified:
84 return "An unknown error has occurred.";
85 case index_error_code::missing_index_file:
86 return "The index file is missing.";
87 case index_error_code::invalid_index_format:
88 return "Invalid index file format.";
89 case index_error_code::multiple_definitions:
90 return "Multiple definitions in the index file.";
91 case index_error_code::missing_definition:
92 return "Missing definition from the index file.";
93 case index_error_code::failed_import:
94 return "Failed to import the definition.";
95 case index_error_code::failed_to_get_external_ast:
96 return "Failed to load external AST source.";
97 case index_error_code::failed_to_generate_usr:
98 return "Failed to generate USR.";
Gabor Marton32aff2e2018-12-07 16:32:43 +000099 case index_error_code::triple_mismatch:
100 return "Triple mismatch";
101 case index_error_code::lang_mismatch:
102 return "Language mismatch";
Gabor Martona006b802019-02-28 15:24:59 +0000103 case index_error_code::lang_dialect_mismatch:
104 return "Language dialect mismatch";
Gabor Horvathe350b0a2017-09-22 11:11:01 +0000105 }
106 llvm_unreachable("Unrecognized index_error_code.");
107 }
108};
109
110static llvm::ManagedStatic<IndexErrorCategory> Category;
111} // end anonymous namespace
112
113char IndexError::ID;
114
115void IndexError::log(raw_ostream &OS) const {
116 OS << Category->message(static_cast<int>(Code)) << '\n';
117}
118
119std::error_code IndexError::convertToErrorCode() const {
120 return std::error_code(static_cast<int>(Code), *Category);
121}
122
123llvm::Expected<llvm::StringMap<std::string>>
124parseCrossTUIndex(StringRef IndexPath, StringRef CrossTUDir) {
Rafael Stahl8c487052019-01-10 17:44:04 +0000125 std::ifstream ExternalMapFile(IndexPath);
126 if (!ExternalMapFile)
Gabor Horvathe350b0a2017-09-22 11:11:01 +0000127 return llvm::make_error<IndexError>(index_error_code::missing_index_file,
128 IndexPath.str());
129
130 llvm::StringMap<std::string> Result;
131 std::string Line;
132 unsigned LineNo = 1;
Rafael Stahl8c487052019-01-10 17:44:04 +0000133 while (std::getline(ExternalMapFile, Line)) {
Gabor Horvathe350b0a2017-09-22 11:11:01 +0000134 const size_t Pos = Line.find(" ");
135 if (Pos > 0 && Pos != std::string::npos) {
136 StringRef LineRef{Line};
Rafael Stahl8c487052019-01-10 17:44:04 +0000137 StringRef LookupName = LineRef.substr(0, Pos);
138 if (Result.count(LookupName))
Gabor Horvathe350b0a2017-09-22 11:11:01 +0000139 return llvm::make_error<IndexError>(
140 index_error_code::multiple_definitions, IndexPath.str(), LineNo);
141 StringRef FileName = LineRef.substr(Pos + 1);
142 SmallString<256> FilePath = CrossTUDir;
Gabor Horvath724beac2017-10-27 12:53:37 +0000143 llvm::sys::path::append(FilePath, FileName);
Rafael Stahl8c487052019-01-10 17:44:04 +0000144 Result[LookupName] = FilePath.str().str();
Gabor Horvathe350b0a2017-09-22 11:11:01 +0000145 } else
146 return llvm::make_error<IndexError>(
147 index_error_code::invalid_index_format, IndexPath.str(), LineNo);
148 LineNo++;
149 }
150 return Result;
151}
152
153std::string
154createCrossTUIndexString(const llvm::StringMap<std::string> &Index) {
155 std::ostringstream Result;
156 for (const auto &E : Index)
157 Result << E.getKey().str() << " " << E.getValue() << '\n';
158 return Result.str();
159}
160
161CrossTranslationUnitContext::CrossTranslationUnitContext(CompilerInstance &CI)
162 : CI(CI), Context(CI.getASTContext()) {}
163
164CrossTranslationUnitContext::~CrossTranslationUnitContext() {}
165
166std::string CrossTranslationUnitContext::getLookupName(const NamedDecl *ND) {
167 SmallString<128> DeclUSR;
Richard Trieu8bd58bf2017-09-22 22:16:13 +0000168 bool Ret = index::generateUSRForDecl(ND, DeclUSR); (void)Ret;
Gabor Horvathe350b0a2017-09-22 11:11:01 +0000169 assert(!Ret && "Unable to generate USR");
170 return DeclUSR.str();
171}
172
173/// Recursively visits the function decls of a DeclContext, and looks up a
174/// function based on USRs.
175const FunctionDecl *
176CrossTranslationUnitContext::findFunctionInDeclContext(const DeclContext *DC,
177 StringRef LookupFnName) {
178 assert(DC && "Declaration Context must not be null");
179 for (const Decl *D : DC->decls()) {
180 const auto *SubDC = dyn_cast<DeclContext>(D);
181 if (SubDC)
182 if (const auto *FD = findFunctionInDeclContext(SubDC, LookupFnName))
183 return FD;
184
185 const auto *ND = dyn_cast<FunctionDecl>(D);
186 const FunctionDecl *ResultDecl;
187 if (!ND || !ND->hasBody(ResultDecl))
188 continue;
189 if (getLookupName(ResultDecl) != LookupFnName)
190 continue;
191 return ResultDecl;
192 }
193 return nullptr;
194}
195
196llvm::Expected<const FunctionDecl *>
197CrossTranslationUnitContext::getCrossTUDefinition(const FunctionDecl *FD,
198 StringRef CrossTUDir,
Gabor Marton9419eb42018-12-07 14:56:02 +0000199 StringRef IndexName,
200 bool DisplayCTUProgress) {
Gabor Martonb7f30dd2018-12-07 12:21:43 +0000201 assert(FD && "FD is missing, bad call to this function!");
Gabor Horvathe350b0a2017-09-22 11:11:01 +0000202 assert(!FD->hasBody() && "FD has a definition in current translation unit!");
Gabor Marton700a29a2018-12-07 11:55:22 +0000203 ++NumGetCTUCalled;
Gabor Horvathe350b0a2017-09-22 11:11:01 +0000204 const std::string LookupFnName = getLookupName(FD);
205 if (LookupFnName.empty())
206 return llvm::make_error<IndexError>(
207 index_error_code::failed_to_generate_usr);
208 llvm::Expected<ASTUnit *> ASTUnitOrError =
Gabor Marton9419eb42018-12-07 14:56:02 +0000209 loadExternalAST(LookupFnName, CrossTUDir, IndexName, DisplayCTUProgress);
Gabor Horvathe350b0a2017-09-22 11:11:01 +0000210 if (!ASTUnitOrError)
211 return ASTUnitOrError.takeError();
212 ASTUnit *Unit = *ASTUnitOrError;
Gabor Horvathe350b0a2017-09-22 11:11:01 +0000213 assert(&Unit->getFileManager() ==
214 &Unit->getASTContext().getSourceManager().getFileManager());
215
Gabor Marton32aff2e2018-12-07 16:32:43 +0000216 const llvm::Triple &TripleTo = Context.getTargetInfo().getTriple();
217 const llvm::Triple &TripleFrom =
218 Unit->getASTContext().getTargetInfo().getTriple();
219 // The imported AST had been generated for a different target.
220 // Some parts of the triple in the loaded ASTContext can be unknown while the
221 // very same parts in the target ASTContext are known. Thus we check for the
222 // known parts only.
223 if (!hasEqualKnownFields(TripleTo, TripleFrom)) {
224 // TODO: Pass the SourceLocation of the CallExpression for more precise
225 // diagnostics.
226 ++NumTripleMismatch;
227 return llvm::make_error<IndexError>(index_error_code::triple_mismatch,
228 Unit->getMainFileName(), TripleTo.str(),
229 TripleFrom.str());
230 }
231
232 const auto &LangTo = Context.getLangOpts();
233 const auto &LangFrom = Unit->getASTContext().getLangOpts();
Gabor Martona006b802019-02-28 15:24:59 +0000234
Gabor Marton32aff2e2018-12-07 16:32:43 +0000235 // FIXME: Currenty we do not support CTU across C++ and C and across
236 // different dialects of C++.
237 if (LangTo.CPlusPlus != LangFrom.CPlusPlus) {
238 ++NumLangMismatch;
239 return llvm::make_error<IndexError>(index_error_code::lang_mismatch);
240 }
241
Gabor Martona006b802019-02-28 15:24:59 +0000242 // If CPP dialects are different then return with error.
243 //
244 // Consider this STL code:
245 // template<typename _Alloc>
246 // struct __alloc_traits
247 // #if __cplusplus >= 201103L
248 // : std::allocator_traits<_Alloc>
249 // #endif
250 // { // ...
251 // };
252 // This class template would create ODR errors during merging the two units,
253 // since in one translation unit the class template has a base class, however
254 // in the other unit it has none.
255 if (LangTo.CPlusPlus11 != LangFrom.CPlusPlus11 ||
256 LangTo.CPlusPlus14 != LangFrom.CPlusPlus14 ||
257 LangTo.CPlusPlus17 != LangFrom.CPlusPlus17 ||
258 LangTo.CPlusPlus2a != LangFrom.CPlusPlus2a) {
259 ++NumLangDialectMismatch;
260 return llvm::make_error<IndexError>(
261 index_error_code::lang_dialect_mismatch);
262 }
263
Gabor Horvathe350b0a2017-09-22 11:11:01 +0000264 TranslationUnitDecl *TU = Unit->getASTContext().getTranslationUnitDecl();
265 if (const FunctionDecl *ResultDecl =
266 findFunctionInDeclContext(TU, LookupFnName))
267 return importDefinition(ResultDecl);
268 return llvm::make_error<IndexError>(index_error_code::failed_import);
269}
270
271void CrossTranslationUnitContext::emitCrossTUDiagnostics(const IndexError &IE) {
272 switch (IE.getCode()) {
273 case index_error_code::missing_index_file:
Richard Trieu0f25c742018-12-14 03:35:10 +0000274 Context.getDiagnostics().Report(diag::err_ctu_error_opening)
275 << IE.getFileName();
Gabor Horvathe350b0a2017-09-22 11:11:01 +0000276 break;
277 case index_error_code::invalid_index_format:
Rafael Stahl8c487052019-01-10 17:44:04 +0000278 Context.getDiagnostics().Report(diag::err_extdefmap_parsing)
Gabor Horvathe350b0a2017-09-22 11:11:01 +0000279 << IE.getFileName() << IE.getLineNum();
Simon Pilgrim554ab532017-09-24 15:17:46 +0000280 break;
Gabor Horvathe350b0a2017-09-22 11:11:01 +0000281 case index_error_code::multiple_definitions:
282 Context.getDiagnostics().Report(diag::err_multiple_def_index)
283 << IE.getLineNum();
284 break;
Gabor Marton32aff2e2018-12-07 16:32:43 +0000285 case index_error_code::triple_mismatch:
286 Context.getDiagnostics().Report(diag::warn_ctu_incompat_triple)
287 << IE.getFileName() << IE.getTripleToName() << IE.getTripleFromName();
288 break;
Gabor Horvathe350b0a2017-09-22 11:11:01 +0000289 default:
290 break;
291 }
292}
293
294llvm::Expected<ASTUnit *> CrossTranslationUnitContext::loadExternalAST(
Gabor Marton9419eb42018-12-07 14:56:02 +0000295 StringRef LookupName, StringRef CrossTUDir, StringRef IndexName,
296 bool DisplayCTUProgress) {
Gabor Horvathe350b0a2017-09-22 11:11:01 +0000297 // FIXME: The current implementation only supports loading functions with
298 // a lookup name from a single translation unit. If multiple
299 // translation units contains functions with the same lookup name an
300 // error will be returned.
301 ASTUnit *Unit = nullptr;
302 auto FnUnitCacheEntry = FunctionASTUnitMap.find(LookupName);
303 if (FnUnitCacheEntry == FunctionASTUnitMap.end()) {
304 if (FunctionFileMap.empty()) {
305 SmallString<256> IndexFile = CrossTUDir;
306 if (llvm::sys::path::is_absolute(IndexName))
307 IndexFile = IndexName;
308 else
309 llvm::sys::path::append(IndexFile, IndexName);
310 llvm::Expected<llvm::StringMap<std::string>> IndexOrErr =
311 parseCrossTUIndex(IndexFile, CrossTUDir);
312 if (IndexOrErr)
313 FunctionFileMap = *IndexOrErr;
314 else
315 return IndexOrErr.takeError();
316 }
317
318 auto It = FunctionFileMap.find(LookupName);
Gabor Marton700a29a2018-12-07 11:55:22 +0000319 if (It == FunctionFileMap.end()) {
320 ++NumNotInOtherTU;
Gabor Horvathe350b0a2017-09-22 11:11:01 +0000321 return llvm::make_error<IndexError>(index_error_code::missing_definition);
Gabor Marton700a29a2018-12-07 11:55:22 +0000322 }
Gabor Horvathe350b0a2017-09-22 11:11:01 +0000323 StringRef ASTFileName = It->second;
324 auto ASTCacheEntry = FileASTUnitMap.find(ASTFileName);
325 if (ASTCacheEntry == FileASTUnitMap.end()) {
326 IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts = new DiagnosticOptions();
327 TextDiagnosticPrinter *DiagClient =
328 new TextDiagnosticPrinter(llvm::errs(), &*DiagOpts);
329 IntrusiveRefCntPtr<DiagnosticIDs> DiagID(new DiagnosticIDs());
330 IntrusiveRefCntPtr<DiagnosticsEngine> Diags(
331 new DiagnosticsEngine(DiagID, &*DiagOpts, DiagClient));
332
333 std::unique_ptr<ASTUnit> LoadedUnit(ASTUnit::LoadFromASTFile(
334 ASTFileName, CI.getPCHContainerOperations()->getRawReader(),
335 ASTUnit::LoadEverything, Diags, CI.getFileSystemOpts()));
336 Unit = LoadedUnit.get();
337 FileASTUnitMap[ASTFileName] = std::move(LoadedUnit);
Gabor Marton9419eb42018-12-07 14:56:02 +0000338 if (DisplayCTUProgress) {
339 llvm::errs() << "CTU loaded AST file: "
340 << ASTFileName << "\n";
341 }
Gabor Horvathe350b0a2017-09-22 11:11:01 +0000342 } else {
343 Unit = ASTCacheEntry->second.get();
344 }
345 FunctionASTUnitMap[LookupName] = Unit;
346 } else {
347 Unit = FnUnitCacheEntry->second;
348 }
Gabor Marton30816902019-01-07 14:05:19 +0000349 if (!Unit)
350 return llvm::make_error<IndexError>(
351 index_error_code::failed_to_get_external_ast);
Gabor Horvathe350b0a2017-09-22 11:11:01 +0000352 return Unit;
353}
354
355llvm::Expected<const FunctionDecl *>
356CrossTranslationUnitContext::importDefinition(const FunctionDecl *FD) {
Gabor Martonb7f30dd2018-12-07 12:21:43 +0000357 assert(FD->hasBody() && "Functions to be imported should have body.");
358
Gabor Horvathe350b0a2017-09-22 11:11:01 +0000359 ASTImporter &Importer = getOrCreateASTImporter(FD->getASTContext());
Balazs Keria1f6b102019-04-08 13:59:15 +0000360 auto ToDeclOrError = Importer.Import_New(FD);
361 if (!ToDeclOrError) {
362 handleAllErrors(ToDeclOrError.takeError(),
363 [&](const ImportError &IE) {
364 switch (IE.Error) {
365 case ImportError::NameConflict:
366 // FIXME: Add statistic.
367 break;
368 case ImportError::UnsupportedConstruct:
369 // FIXME: Add statistic.
370 break;
371 case ImportError::Unknown:
372 llvm_unreachable("Unknown import error happened.");
373 break;
374 }
375 });
Gabor Martonb87251d2018-12-07 16:05:58 +0000376 return llvm::make_error<IndexError>(index_error_code::failed_import);
Balazs Keria1f6b102019-04-08 13:59:15 +0000377 }
378 auto *ToDecl = cast<FunctionDecl>(*ToDeclOrError);
379 assert(ToDecl->hasBody() && "Imported function should have body.");
Gabor Marton700a29a2018-12-07 11:55:22 +0000380 ++NumGetCTUSuccess;
Balazs Keria1f6b102019-04-08 13:59:15 +0000381
Gabor Horvathe350b0a2017-09-22 11:11:01 +0000382 return ToDecl;
383}
384
Gabor Marton54058b52018-12-17 13:53:12 +0000385void CrossTranslationUnitContext::lazyInitLookupTable(
386 TranslationUnitDecl *ToTU) {
387 if (!LookupTable)
388 LookupTable = llvm::make_unique<ASTImporterLookupTable>(*ToTU);
389}
390
Gabor Horvathe350b0a2017-09-22 11:11:01 +0000391ASTImporter &
392CrossTranslationUnitContext::getOrCreateASTImporter(ASTContext &From) {
393 auto I = ASTUnitImporterMap.find(From.getTranslationUnitDecl());
394 if (I != ASTUnitImporterMap.end())
395 return *I->second;
Gabor Marton54058b52018-12-17 13:53:12 +0000396 lazyInitLookupTable(Context.getTranslationUnitDecl());
397 ASTImporter *NewImporter = new ASTImporter(
398 Context, Context.getSourceManager().getFileManager(), From,
399 From.getSourceManager().getFileManager(), false, LookupTable.get());
Gabor Horvathe350b0a2017-09-22 11:11:01 +0000400 ASTUnitImporterMap[From.getTranslationUnitDecl()].reset(NewImporter);
401 return *NewImporter;
402}
403
404} // namespace cross_tu
405} // namespace clang