19
19
#include " clang/Frontend/CompilerInstance.h"
20
20
#include " clang/Frontend/TextDiagnosticPrinter.h"
21
21
#include " clang/Index/USRGeneration.h"
22
- #include " llvm/ADT/Triple.h"
22
+ #include " clang/Tooling/JSONCompilationDatabase.h"
23
+ #include " clang/Tooling/Tooling.h"
23
24
#include " llvm/ADT/Statistic.h"
25
+ #include " llvm/ADT/Triple.h"
24
26
#include " llvm/Support/ErrorHandling.h"
25
27
#include " llvm/Support/ManagedStatic.h"
26
28
#include " llvm/Support/Path.h"
27
29
#include " llvm/Support/raw_ostream.h"
30
+ #include < algorithm>
28
31
#include < fstream>
29
32
#include < sstream>
30
33
@@ -101,6 +104,8 @@ class IndexErrorCategory : public std::error_category {
101
104
return " Failed to import the definition." ;
102
105
case index_error_code::failed_to_get_external_ast:
103
106
return " Failed to load external AST source." ;
107
+ case index_error_code::failed_to_load_compilation_database:
108
+ return " Failed to load compilation database." ;
104
109
case index_error_code::failed_to_generate_usr:
105
110
return " Failed to generate USR." ;
106
111
case index_error_code::triple_mismatch:
@@ -130,7 +135,8 @@ std::error_code IndexError::convertToErrorCode() const {
130
135
}
131
136
132
137
llvm::Expected<llvm::StringMap<std::string>>
133
- parseCrossTUIndex (StringRef IndexPath, StringRef CrossTUDir) {
138
+ parseCrossTUIndex (StringRef IndexPath, StringRef CrossTUDir,
139
+ StringRef CompilationDatabase) {
134
140
std::ifstream ExternalMapFile (IndexPath);
135
141
if (!ExternalMapFile)
136
142
return llvm::make_error<IndexError>(index_error_code::missing_index_file,
@@ -148,9 +154,14 @@ parseCrossTUIndex(StringRef IndexPath, StringRef CrossTUDir) {
148
154
return llvm::make_error<IndexError>(
149
155
index_error_code::multiple_definitions, IndexPath.str (), LineNo);
150
156
StringRef FileName = LineRef.substr (Pos + 1 );
151
- SmallString<256 > FilePath = CrossTUDir;
152
- llvm::sys::path::append (FilePath, FileName);
153
- Result[LookupName] = FilePath.str ().str ();
157
+ // AST-dump based analysis requires a prefixed path.
158
+ if (CompilationDatabase.empty ()) {
159
+ SmallString<256 > FilePath = CrossTUDir;
160
+ llvm::sys::path::append (FilePath, FileName);
161
+ Result[LookupName] = FilePath.str ().str ();
162
+ } else {
163
+ Result[LookupName] = FileName.str ();
164
+ }
154
165
} else
155
166
return llvm::make_error<IndexError>(
156
167
index_error_code::invalid_index_format, IndexPath.str (), LineNo);
@@ -203,11 +214,10 @@ CrossTranslationUnitContext::findFunctionInDeclContext(const DeclContext *DC,
203
214
}
204
215
205
216
llvm::Expected<const FunctionDecl *>
206
- CrossTranslationUnitContext::getCrossTUDefinition (const FunctionDecl *FD,
207
- StringRef CrossTUDir,
208
- StringRef IndexName,
209
- bool DisplayCTUProgress,
210
- unsigned CTULoadThreshold) {
217
+ CrossTranslationUnitContext::getCrossTUDefinition (
218
+ const FunctionDecl *FD, StringRef CrossTUDir, StringRef IndexName,
219
+ bool DisplayCTUProgress, unsigned CTULoadThreshold,
220
+ StringRef CompilationDatabase) {
211
221
assert (FD && " FD is missing, bad call to this function!" );
212
222
assert (!FD->hasBody () && " FD has a definition in current translation unit!" );
213
223
++NumGetCTUCalled;
@@ -217,7 +227,7 @@ CrossTranslationUnitContext::getCrossTUDefinition(const FunctionDecl *FD,
217
227
index_error_code::failed_to_generate_usr);
218
228
llvm::Expected<ASTUnit *> ASTUnitOrError =
219
229
loadExternalAST (LookupFnName, CrossTUDir, IndexName, DisplayCTUProgress,
220
- CTULoadThreshold);
230
+ CTULoadThreshold, CompilationDatabase );
221
231
if (!ASTUnitOrError)
222
232
return ASTUnitOrError.takeError ();
223
233
ASTUnit *Unit = *ASTUnitOrError;
@@ -302,9 +312,91 @@ void CrossTranslationUnitContext::emitCrossTUDiagnostics(const IndexError &IE) {
302
312
}
303
313
}
304
314
315
+ // / Load the AST from a source-file, which is supposed to be located inside the
316
+ // / compilation database \p CompileCommands. The compilation database can can
317
+ // / contain the path of the file under the key "file" as an absolute path, or as
318
+ // / a relative path. When emitting diagnostics, plist files may contain
319
+ // / references to a location in a TU, that is different from the main TU. In
320
+ // / such cases, the file path emitted by the DiagnosticEngine is based on how
321
+ // / the exact invocation is assembled inside the ClangTool, which performs the
322
+ // / building of the ASTs. In order ensure absolute paths inside the diagnostics,
323
+ // / we use the ArgumentsAdjuster API of ClangTool to make sure that the
324
+ // / invocation inside ClangTool is always made with an absolute path. \p
325
+ // / ASTSourcePath is assumed to be the lookup-name of the file, which comes from
326
+ // / the Index. The Index is built by the \p clang-extdef-mapping tool, which is
327
+ // / supposed to generate absolute paths.
328
+ // / Note that as the ClangTool is instantiated with a lookup-vector, which
329
+ // / contains a single entry; the the supposedly absolute path of the source
330
+ // / file. So the ArgumentAdjuster will only be used on the single corresponding
331
+ // / invocation. This garantees that even if two files match in name, but
332
+ // / differ in location, only the correct one's invocation will be handled. This
333
+ // / is due to the fact that the lookup is done correctly inside the
334
+ // / CompilationDatabase, so it works for already absolute paths given under the
335
+ // / "file" entry of the compilation database, but also if a relative path is
336
+ // / given. In such a case, the lookup uses the "directory" entry as well to
337
+ // / identify the correct file.
338
+ std::unique_ptr<ASTUnit>
339
+ CrossTranslationUnitContext::loadASTOnDemand (StringRef ASTSourcePath) const {
340
+
341
+ using namespace tooling ;
342
+
343
+ SmallVector<std::string, 1 > Files;
344
+ Files.push_back (ASTSourcePath);
345
+ ClangTool Tool (*CompileCommands, Files, CI.getPCHContainerOperations ());
346
+
347
+ // / Lambda filter designed to find the source file argument inside an
348
+ // / invocation used to build the ASTs, and replace it with its absolute path
349
+ // / equivalent.
350
+ auto SourcePathNormalizer = [ASTSourcePath](const CommandLineArguments &Args,
351
+ StringRef FileName) {
352
+ // / Match the argument to the absolute path by checking whether it is a
353
+ // / postfix.
354
+ auto IsPostfixOfLookup = [ASTSourcePath](const std::string &Arg) {
355
+ return ASTSourcePath.rfind (Arg) != llvm::StringRef::npos;
356
+ };
357
+
358
+ // / Commandline arguments are modified, and the API dictates the return a
359
+ // / new instance, so copy the original.
360
+ CommandLineArguments Result{Args};
361
+
362
+ // / Search for the source file argument. Start from the end as a heuristic,
363
+ // / as most invocations tend to contain the source file argument in their
364
+ // / the latter half. Only the first match is replaced.
365
+ auto SourceFilePath =
366
+ std::find_if (Result.rbegin (), Result.rend (), IsPostfixOfLookup);
367
+
368
+ // / If source file argument could not been found, return the original
369
+ // / CommandlineArgumentsInstance.
370
+ if (SourceFilePath == Result.rend ())
371
+ return Result;
372
+
373
+ llvm::errs () << " Matching argument: '" << *SourceFilePath
374
+ << " ', which matches original filename '" << ASTSourcePath
375
+ << " ', overwriting...\n " ;
376
+
377
+ // / Overwrite the argument with the \p ASTSourcePath, as it is assumed to be
378
+ // / the absolute path of the file.
379
+ *SourceFilePath = ASTSourcePath.str ();
380
+
381
+ return Result;
382
+ };
383
+
384
+ Tool.appendArgumentsAdjuster (std::move (SourcePathNormalizer));
385
+
386
+ std::vector<std::unique_ptr<ASTUnit>> ASTs;
387
+ Tool.buildASTs (ASTs);
388
+
389
+ if (ASTs.size () > 0 ) {
390
+ ASTs[0 ]->enableSourceFileDiagnostics ();
391
+ return std::move (ASTs[0 ]);
392
+ } else
393
+ return nullptr ;
394
+ }
395
+
305
396
llvm::Expected<ASTUnit *> CrossTranslationUnitContext::loadExternalAST (
306
397
StringRef LookupName, StringRef CrossTUDir, StringRef IndexName,
307
- bool DisplayCTUProgress, unsigned CTULoadThreshold) {
398
+ bool DisplayCTUProgress, unsigned CTULoadThreshold,
399
+ StringRef CompilationDatabase) {
308
400
// FIXME: The current implementation only supports loading functions with
309
401
// a lookup name from a single translation unit. If multiple
310
402
// translation units contains functions with the same lookup name an
@@ -326,7 +418,7 @@ llvm::Expected<ASTUnit *> CrossTranslationUnitContext::loadExternalAST(
326
418
else
327
419
llvm::sys::path::append (IndexFile, IndexName);
328
420
llvm::Expected<llvm::StringMap<std::string>> IndexOrErr =
329
- parseCrossTUIndex (IndexFile, CrossTUDir);
421
+ parseCrossTUIndex (IndexFile, CrossTUDir, CompilationDatabase );
330
422
if (IndexOrErr)
331
423
FunctionFileMap = *IndexOrErr;
332
424
else
@@ -338,33 +430,63 @@ llvm::Expected<ASTUnit *> CrossTranslationUnitContext::loadExternalAST(
338
430
++NumNotInOtherTU;
339
431
return llvm::make_error<IndexError>(index_error_code::missing_definition);
340
432
}
341
- StringRef ASTFileName = It->second ;
342
- auto ASTCacheEntry = FileASTUnitMap.find (ASTFileName );
433
+ StringRef ASTSource = It->second ;
434
+ auto ASTCacheEntry = FileASTUnitMap.find (ASTSource );
343
435
if (ASTCacheEntry == FileASTUnitMap.end ()) {
344
- IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts = new DiagnosticOptions ();
345
- TextDiagnosticPrinter *DiagClient =
346
- new TextDiagnosticPrinter (llvm::errs (), &*DiagOpts);
347
- IntrusiveRefCntPtr<DiagnosticIDs> DiagID (new DiagnosticIDs ());
348
- IntrusiveRefCntPtr<DiagnosticsEngine> Diags (
349
- new DiagnosticsEngine (DiagID, &*DiagOpts, DiagClient));
350
-
351
- std::unique_ptr<ASTUnit> LoadedUnit (ASTUnit::LoadFromASTFile (
352
- ASTFileName, CI.getPCHContainerOperations ()->getRawReader (),
353
- ASTUnit::LoadEverything, Diags, CI.getFileSystemOpts ()));
354
- Unit = LoadedUnit.get ();
355
- FileASTUnitMap[ASTFileName] = std::move (LoadedUnit);
436
+ if (CompilationDatabase.empty ()) {
437
+ // If no \p CompilationDatabase is given, try to load from AST dump
438
+ // file, as on-demand parsing is disabled.
439
+ IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts =
440
+ new DiagnosticOptions ();
441
+ TextDiagnosticPrinter *DiagClient =
442
+ new TextDiagnosticPrinter (llvm::errs (), &*DiagOpts);
443
+ IntrusiveRefCntPtr<DiagnosticIDs> DiagID (new DiagnosticIDs ());
444
+ IntrusiveRefCntPtr<DiagnosticsEngine> Diags (
445
+ new DiagnosticsEngine (DiagID, &*DiagOpts, DiagClient));
446
+
447
+ std::unique_ptr<ASTUnit> LoadedUnit (ASTUnit::LoadFromASTFile (
448
+ ASTSource, CI.getPCHContainerOperations ()->getRawReader (),
449
+ ASTUnit::LoadEverything, Diags, CI.getFileSystemOpts ()));
450
+ Unit = LoadedUnit.get ();
451
+
452
+ // Cache the resulting ASTUnit.
453
+ if (Unit)
454
+ FileASTUnitMap[ASTSource] = std::move (LoadedUnit);
455
+ } else {
456
+
457
+ // Lazily initialize the compilation database.
458
+ if (!CompileCommands) {
459
+ std::string LoadError;
460
+ CompileCommands = tooling::JSONCompilationDatabase::loadFromFile (
461
+ CompilationDatabase, LoadError,
462
+ tooling::JSONCommandLineSyntax::AutoDetect);
463
+ if (!CompileCommands)
464
+ return llvm::make_error<IndexError>(
465
+ index_error_code::failed_to_get_external_ast);
466
+ }
467
+
468
+ // Try loading on-demand.
469
+ std::unique_ptr<ASTUnit> LoadedUnit = loadASTOnDemand (ASTSource);
470
+ Unit = LoadedUnit.get ();
471
+
472
+ // Cache the sulting ASTUnit.
473
+ if (Unit)
474
+ FileASTUnitMap[ASTSource] = std::move (LoadedUnit);
475
+ }
356
476
++NumASTLoaded;
357
477
if (DisplayCTUProgress) {
358
- llvm::errs () << " CTU loaded AST file: "
359
- << ASTFileName << " \n " ;
478
+ llvm::errs () << " CTU loaded AST file: " << ASTSource << " \n " ;
360
479
}
361
480
} else {
362
481
Unit = ASTCacheEntry->second .get ();
363
482
}
364
- FunctionASTUnitMap[LookupName] = Unit;
483
+ // Fill the cache for the lookup name as well.
484
+ if (Unit)
485
+ FunctionASTUnitMap[LookupName] = Unit;
365
486
} else {
366
487
Unit = FnUnitCacheEntry->second ;
367
488
}
489
+
368
490
if (!Unit)
369
491
return llvm::make_error<IndexError>(
370
492
index_error_code::failed_to_get_external_ast);
@@ -397,7 +519,7 @@ CrossTranslationUnitContext::importDefinition(const FunctionDecl *FD,
397
519
});
398
520
return llvm::make_error<IndexError>(index_error_code::failed_import);
399
521
}
400
- auto *ToDecl = cast<FunctionDecl>(*ToDeclOrError);
522
+ auto *ToDecl = cast<FunctionDecl>(*ToDeclOrError);
401
523
assert (ToDecl->hasBody () && " Imported function should have body." );
402
524
++NumGetCTUSuccess;
403
525
0 commit comments