blob: ec8ac6498ff9353d33bb59f64c77565549dbf66d [file] [log] [blame]
Gabor Horvathe350b0a2017-09-22 11:11:01 +00001//===--- CrossTranslationUnit.cpp - -----------------------------*- C++ -*-===//
2//
3// The LLVM Compiler Infrastructure
4//
5// This file is distributed under the University of Illinois Open Source
6// License. See LICENSE.TXT for details.
7//
8//===----------------------------------------------------------------------===//
9//
10// This file implements the CrossTranslationUnit interface.
11//
12//===----------------------------------------------------------------------===//
13#include "clang/CrossTU/CrossTranslationUnit.h"
14#include "clang/AST/ASTImporter.h"
15#include "clang/AST/Decl.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/FrontendDiagnostic.h"
21#include "clang/Frontend/TextDiagnosticPrinter.h"
22#include "clang/Index/USRGeneration.h"
23#include "llvm/ADT/Triple.h"
Gabor Marton700a29a2018-12-07 11:55:22 +000024#include "llvm/ADT/Statistic.h"
Gabor Horvathe350b0a2017-09-22 11:11:01 +000025#include "llvm/Support/ErrorHandling.h"
26#include "llvm/Support/ManagedStatic.h"
27#include "llvm/Support/Path.h"
28#include "llvm/Support/raw_ostream.h"
29#include <fstream>
30#include <sstream>
31
32namespace clang {
33namespace cross_tu {
34
35namespace {
Gabor Marton700a29a2018-12-07 11:55:22 +000036#define DEBUG_TYPE "CrossTranslationUnit"
37STATISTIC(NumGetCTUCalled, "The # of getCTUDefinition function called");
38STATISTIC(
39 NumNotInOtherTU,
40 "The # of getCTUDefinition called but the function is not in any other TU");
41STATISTIC(NumGetCTUSuccess,
42 "The # of getCTUDefinition successfully returned the "
43 "requested function's body");
44
Gabor Horvathe350b0a2017-09-22 11:11:01 +000045// FIXME: This class is will be removed after the transition to llvm::Error.
46class IndexErrorCategory : public std::error_category {
47public:
48 const char *name() const noexcept override { return "clang.index"; }
49
50 std::string message(int Condition) const override {
51 switch (static_cast<index_error_code>(Condition)) {
52 case index_error_code::unspecified:
53 return "An unknown error has occurred.";
54 case index_error_code::missing_index_file:
55 return "The index file is missing.";
56 case index_error_code::invalid_index_format:
57 return "Invalid index file format.";
58 case index_error_code::multiple_definitions:
59 return "Multiple definitions in the index file.";
60 case index_error_code::missing_definition:
61 return "Missing definition from the index file.";
62 case index_error_code::failed_import:
63 return "Failed to import the definition.";
64 case index_error_code::failed_to_get_external_ast:
65 return "Failed to load external AST source.";
66 case index_error_code::failed_to_generate_usr:
67 return "Failed to generate USR.";
68 }
69 llvm_unreachable("Unrecognized index_error_code.");
70 }
71};
72
73static llvm::ManagedStatic<IndexErrorCategory> Category;
74} // end anonymous namespace
75
76char IndexError::ID;
77
78void IndexError::log(raw_ostream &OS) const {
79 OS << Category->message(static_cast<int>(Code)) << '\n';
80}
81
82std::error_code IndexError::convertToErrorCode() const {
83 return std::error_code(static_cast<int>(Code), *Category);
84}
85
86llvm::Expected<llvm::StringMap<std::string>>
87parseCrossTUIndex(StringRef IndexPath, StringRef CrossTUDir) {
88 std::ifstream ExternalFnMapFile(IndexPath);
89 if (!ExternalFnMapFile)
90 return llvm::make_error<IndexError>(index_error_code::missing_index_file,
91 IndexPath.str());
92
93 llvm::StringMap<std::string> Result;
94 std::string Line;
95 unsigned LineNo = 1;
96 while (std::getline(ExternalFnMapFile, Line)) {
97 const size_t Pos = Line.find(" ");
98 if (Pos > 0 && Pos != std::string::npos) {
99 StringRef LineRef{Line};
100 StringRef FunctionLookupName = LineRef.substr(0, Pos);
101 if (Result.count(FunctionLookupName))
102 return llvm::make_error<IndexError>(
103 index_error_code::multiple_definitions, IndexPath.str(), LineNo);
104 StringRef FileName = LineRef.substr(Pos + 1);
105 SmallString<256> FilePath = CrossTUDir;
Gabor Horvath724beac2017-10-27 12:53:37 +0000106 llvm::sys::path::append(FilePath, FileName);
Gabor Horvathe350b0a2017-09-22 11:11:01 +0000107 Result[FunctionLookupName] = FilePath.str().str();
108 } else
109 return llvm::make_error<IndexError>(
110 index_error_code::invalid_index_format, IndexPath.str(), LineNo);
111 LineNo++;
112 }
113 return Result;
114}
115
116std::string
117createCrossTUIndexString(const llvm::StringMap<std::string> &Index) {
118 std::ostringstream Result;
119 for (const auto &E : Index)
120 Result << E.getKey().str() << " " << E.getValue() << '\n';
121 return Result.str();
122}
123
124CrossTranslationUnitContext::CrossTranslationUnitContext(CompilerInstance &CI)
125 : CI(CI), Context(CI.getASTContext()) {}
126
127CrossTranslationUnitContext::~CrossTranslationUnitContext() {}
128
129std::string CrossTranslationUnitContext::getLookupName(const NamedDecl *ND) {
130 SmallString<128> DeclUSR;
Richard Trieu8bd58bf2017-09-22 22:16:13 +0000131 bool Ret = index::generateUSRForDecl(ND, DeclUSR); (void)Ret;
Gabor Horvathe350b0a2017-09-22 11:11:01 +0000132 assert(!Ret && "Unable to generate USR");
133 return DeclUSR.str();
134}
135
136/// Recursively visits the function decls of a DeclContext, and looks up a
137/// function based on USRs.
138const FunctionDecl *
139CrossTranslationUnitContext::findFunctionInDeclContext(const DeclContext *DC,
140 StringRef LookupFnName) {
141 assert(DC && "Declaration Context must not be null");
142 for (const Decl *D : DC->decls()) {
143 const auto *SubDC = dyn_cast<DeclContext>(D);
144 if (SubDC)
145 if (const auto *FD = findFunctionInDeclContext(SubDC, LookupFnName))
146 return FD;
147
148 const auto *ND = dyn_cast<FunctionDecl>(D);
149 const FunctionDecl *ResultDecl;
150 if (!ND || !ND->hasBody(ResultDecl))
151 continue;
152 if (getLookupName(ResultDecl) != LookupFnName)
153 continue;
154 return ResultDecl;
155 }
156 return nullptr;
157}
158
159llvm::Expected<const FunctionDecl *>
160CrossTranslationUnitContext::getCrossTUDefinition(const FunctionDecl *FD,
161 StringRef CrossTUDir,
Gabor Marton9419eb42018-12-07 14:56:02 +0000162 StringRef IndexName,
163 bool DisplayCTUProgress) {
Gabor Martonb7f30dd2018-12-07 12:21:43 +0000164 assert(FD && "FD is missing, bad call to this function!");
Gabor Horvathe350b0a2017-09-22 11:11:01 +0000165 assert(!FD->hasBody() && "FD has a definition in current translation unit!");
Gabor Marton700a29a2018-12-07 11:55:22 +0000166 ++NumGetCTUCalled;
Gabor Horvathe350b0a2017-09-22 11:11:01 +0000167 const std::string LookupFnName = getLookupName(FD);
168 if (LookupFnName.empty())
169 return llvm::make_error<IndexError>(
170 index_error_code::failed_to_generate_usr);
171 llvm::Expected<ASTUnit *> ASTUnitOrError =
Gabor Marton9419eb42018-12-07 14:56:02 +0000172 loadExternalAST(LookupFnName, CrossTUDir, IndexName, DisplayCTUProgress);
Gabor Horvathe350b0a2017-09-22 11:11:01 +0000173 if (!ASTUnitOrError)
174 return ASTUnitOrError.takeError();
175 ASTUnit *Unit = *ASTUnitOrError;
176 if (!Unit)
177 return llvm::make_error<IndexError>(
178 index_error_code::failed_to_get_external_ast);
179 assert(&Unit->getFileManager() ==
180 &Unit->getASTContext().getSourceManager().getFileManager());
181
182 TranslationUnitDecl *TU = Unit->getASTContext().getTranslationUnitDecl();
183 if (const FunctionDecl *ResultDecl =
184 findFunctionInDeclContext(TU, LookupFnName))
185 return importDefinition(ResultDecl);
186 return llvm::make_error<IndexError>(index_error_code::failed_import);
187}
188
189void CrossTranslationUnitContext::emitCrossTUDiagnostics(const IndexError &IE) {
190 switch (IE.getCode()) {
191 case index_error_code::missing_index_file:
192 Context.getDiagnostics().Report(diag::err_fe_error_opening)
193 << IE.getFileName() << "required by the CrossTU functionality";
194 break;
195 case index_error_code::invalid_index_format:
196 Context.getDiagnostics().Report(diag::err_fnmap_parsing)
197 << IE.getFileName() << IE.getLineNum();
Simon Pilgrim554ab532017-09-24 15:17:46 +0000198 break;
Gabor Horvathe350b0a2017-09-22 11:11:01 +0000199 case index_error_code::multiple_definitions:
200 Context.getDiagnostics().Report(diag::err_multiple_def_index)
201 << IE.getLineNum();
202 break;
203 default:
204 break;
205 }
206}
207
208llvm::Expected<ASTUnit *> CrossTranslationUnitContext::loadExternalAST(
Gabor Marton9419eb42018-12-07 14:56:02 +0000209 StringRef LookupName, StringRef CrossTUDir, StringRef IndexName,
210 bool DisplayCTUProgress) {
Gabor Horvathe350b0a2017-09-22 11:11:01 +0000211 // FIXME: The current implementation only supports loading functions with
212 // a lookup name from a single translation unit. If multiple
213 // translation units contains functions with the same lookup name an
214 // error will be returned.
215 ASTUnit *Unit = nullptr;
216 auto FnUnitCacheEntry = FunctionASTUnitMap.find(LookupName);
217 if (FnUnitCacheEntry == FunctionASTUnitMap.end()) {
218 if (FunctionFileMap.empty()) {
219 SmallString<256> IndexFile = CrossTUDir;
220 if (llvm::sys::path::is_absolute(IndexName))
221 IndexFile = IndexName;
222 else
223 llvm::sys::path::append(IndexFile, IndexName);
224 llvm::Expected<llvm::StringMap<std::string>> IndexOrErr =
225 parseCrossTUIndex(IndexFile, CrossTUDir);
226 if (IndexOrErr)
227 FunctionFileMap = *IndexOrErr;
228 else
229 return IndexOrErr.takeError();
230 }
231
232 auto It = FunctionFileMap.find(LookupName);
Gabor Marton700a29a2018-12-07 11:55:22 +0000233 if (It == FunctionFileMap.end()) {
234 ++NumNotInOtherTU;
Gabor Horvathe350b0a2017-09-22 11:11:01 +0000235 return llvm::make_error<IndexError>(index_error_code::missing_definition);
Gabor Marton700a29a2018-12-07 11:55:22 +0000236 }
Gabor Horvathe350b0a2017-09-22 11:11:01 +0000237 StringRef ASTFileName = It->second;
238 auto ASTCacheEntry = FileASTUnitMap.find(ASTFileName);
239 if (ASTCacheEntry == FileASTUnitMap.end()) {
240 IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts = new DiagnosticOptions();
241 TextDiagnosticPrinter *DiagClient =
242 new TextDiagnosticPrinter(llvm::errs(), &*DiagOpts);
243 IntrusiveRefCntPtr<DiagnosticIDs> DiagID(new DiagnosticIDs());
244 IntrusiveRefCntPtr<DiagnosticsEngine> Diags(
245 new DiagnosticsEngine(DiagID, &*DiagOpts, DiagClient));
246
247 std::unique_ptr<ASTUnit> LoadedUnit(ASTUnit::LoadFromASTFile(
248 ASTFileName, CI.getPCHContainerOperations()->getRawReader(),
249 ASTUnit::LoadEverything, Diags, CI.getFileSystemOpts()));
250 Unit = LoadedUnit.get();
251 FileASTUnitMap[ASTFileName] = std::move(LoadedUnit);
Gabor Marton9419eb42018-12-07 14:56:02 +0000252 if (DisplayCTUProgress) {
253 llvm::errs() << "CTU loaded AST file: "
254 << ASTFileName << "\n";
255 }
Gabor Horvathe350b0a2017-09-22 11:11:01 +0000256 } else {
257 Unit = ASTCacheEntry->second.get();
258 }
259 FunctionASTUnitMap[LookupName] = Unit;
260 } else {
261 Unit = FnUnitCacheEntry->second;
262 }
263 return Unit;
264}
265
266llvm::Expected<const FunctionDecl *>
267CrossTranslationUnitContext::importDefinition(const FunctionDecl *FD) {
Gabor Martonb7f30dd2018-12-07 12:21:43 +0000268 assert(FD->hasBody() && "Functions to be imported should have body.");
269
Gabor Horvathe350b0a2017-09-22 11:11:01 +0000270 ASTImporter &Importer = getOrCreateASTImporter(FD->getASTContext());
271 auto *ToDecl =
Gabor Martonb87251d2018-12-07 16:05:58 +0000272 cast_or_null<FunctionDecl>(Importer.Import(const_cast<FunctionDecl *>(FD)));
273 if (!ToDecl)
274 return llvm::make_error<IndexError>(index_error_code::failed_import);
Gabor Horvathe350b0a2017-09-22 11:11:01 +0000275 assert(ToDecl->hasBody());
276 assert(FD->hasBody() && "Functions already imported should have body.");
Gabor Marton700a29a2018-12-07 11:55:22 +0000277 ++NumGetCTUSuccess;
Gabor Horvathe350b0a2017-09-22 11:11:01 +0000278 return ToDecl;
279}
280
281ASTImporter &
282CrossTranslationUnitContext::getOrCreateASTImporter(ASTContext &From) {
283 auto I = ASTUnitImporterMap.find(From.getTranslationUnitDecl());
284 if (I != ASTUnitImporterMap.end())
285 return *I->second;
286 ASTImporter *NewImporter =
287 new ASTImporter(Context, Context.getSourceManager().getFileManager(),
288 From, From.getSourceManager().getFileManager(), false);
289 ASTUnitImporterMap[From.getTranslationUnitDecl()].reset(NewImporter);
290 return *NewImporter;
291}
292
293} // namespace cross_tu
294} // namespace clang