aboutsummaryrefslogtreecommitdiff
path: root/clang/lib/Tooling
diff options
context:
space:
mode:
authorDimitry Andric <dim@FreeBSD.org>2021-11-19 20:06:13 +0000
committerDimitry Andric <dim@FreeBSD.org>2021-11-19 20:06:13 +0000
commitc0981da47d5696fe36474fcf86b4ce03ae3ff818 (patch)
treef42add1021b9f2ac6a69ac7cf6c4499962739a45 /clang/lib/Tooling
parent344a3780b2e33f6ca763666c380202b18aab72a3 (diff)
Diffstat (limited to 'clang/lib/Tooling')
-rw-r--r--clang/lib/Tooling/CommonOptionsParser.cpp2
-rw-r--r--clang/lib/Tooling/DependencyScanning/DependencyScanningService.cpp4
-rw-r--r--clang/lib/Tooling/DependencyScanning/DependencyScanningTool.cpp41
-rw-r--r--clang/lib/Tooling/DependencyScanning/DependencyScanningWorker.cpp167
-rw-r--r--clang/lib/Tooling/DependencyScanning/ModuleDepCollector.cpp111
-rw-r--r--clang/lib/Tooling/DumpTool/ClangSrcLocDump.cpp8
-rw-r--r--clang/lib/Tooling/JSONCompilationDatabase.cpp13
-rw-r--r--clang/lib/Tooling/Syntax/BuildTree.cpp5
-rw-r--r--clang/lib/Tooling/Tooling.cpp34
-rw-r--r--clang/lib/Tooling/Transformer/Parsing.cpp2
-rw-r--r--clang/lib/Tooling/Transformer/Stencil.cpp79
11 files changed, 303 insertions, 163 deletions
diff --git a/clang/lib/Tooling/CommonOptionsParser.cpp b/clang/lib/Tooling/CommonOptionsParser.cpp
index 6301544dbb28..7d48dd505464 100644
--- a/clang/lib/Tooling/CommonOptionsParser.cpp
+++ b/clang/lib/Tooling/CommonOptionsParser.cpp
@@ -170,7 +170,7 @@ CommonOptionsParser::CommonOptionsParser(
llvm::Error Err = init(argc, argv, Category, OccurrencesFlag, Overview);
if (Err) {
llvm::report_fatal_error(
- "CommonOptionsParser: failed to parse command-line arguments. " +
+ Twine("CommonOptionsParser: failed to parse command-line arguments. ") +
llvm::toString(std::move(Err)));
}
}
diff --git a/clang/lib/Tooling/DependencyScanning/DependencyScanningService.cpp b/clang/lib/Tooling/DependencyScanning/DependencyScanningService.cpp
index 4f3e574719d2..4b6c87aba62f 100644
--- a/clang/lib/Tooling/DependencyScanning/DependencyScanningService.cpp
+++ b/clang/lib/Tooling/DependencyScanning/DependencyScanningService.cpp
@@ -15,9 +15,9 @@ using namespace dependencies;
DependencyScanningService::DependencyScanningService(
ScanningMode Mode, ScanningOutputFormat Format, bool ReuseFileManager,
- bool SkipExcludedPPRanges)
+ bool SkipExcludedPPRanges, bool OptimizeArgs)
: Mode(Mode), Format(Format), ReuseFileManager(ReuseFileManager),
- SkipExcludedPPRanges(SkipExcludedPPRanges) {
+ SkipExcludedPPRanges(SkipExcludedPPRanges), OptimizeArgs(OptimizeArgs) {
// Initialize targets for object file support.
llvm::InitializeAllTargets();
llvm::InitializeAllTargetMCs();
diff --git a/clang/lib/Tooling/DependencyScanning/DependencyScanningTool.cpp b/clang/lib/Tooling/DependencyScanning/DependencyScanningTool.cpp
index 2fd12f7e12b1..739712baadd0 100644
--- a/clang/lib/Tooling/DependencyScanning/DependencyScanningTool.cpp
+++ b/clang/lib/Tooling/DependencyScanning/DependencyScanningTool.cpp
@@ -24,8 +24,6 @@ std::vector<std::string> FullDependencies::getAdditionalArgs(
ClangModuleDeps, LookupPCMPath, LookupModuleDeps, PCMPaths, ModMapPaths);
for (const std::string &PCMPath : PCMPaths)
Ret.push_back("-fmodule-file=" + PCMPath);
- for (const std::string &ModMapPath : ModMapPaths)
- Ret.push_back("-fmodule-map-file=" + ModMapPath);
return Ret;
}
@@ -37,10 +35,8 @@ FullDependencies::getAdditionalArgsWithoutModulePaths() const {
"-fno-implicit-module-maps",
};
- for (const PrebuiltModuleDep &PMD : PrebuiltModuleDeps) {
- Args.push_back("-fmodule-file=" + PMD.ModuleName + "=" + PMD.PCMFile);
- Args.push_back("-fmodule-map-file=" + PMD.ModuleMapFile);
- }
+ for (const PrebuiltModuleDep &PMD : PrebuiltModuleDeps)
+ Args.push_back("-fmodule-file=" + PMD.PCMFile);
return Args;
}
@@ -50,7 +46,8 @@ DependencyScanningTool::DependencyScanningTool(
: Worker(Service) {}
llvm::Expected<std::string> DependencyScanningTool::getDependencyFile(
- const tooling::CompilationDatabase &Compilations, StringRef CWD) {
+ const std::vector<std::string> &CommandLine, StringRef CWD,
+ llvm::Optional<StringRef> ModuleName) {
/// Prints out all of the gathered dependencies into a string.
class MakeDependencyPrinterConsumer : public DependencyConsumer {
public:
@@ -102,17 +99,9 @@ llvm::Expected<std::string> DependencyScanningTool::getDependencyFile(
std::vector<std::string> Dependencies;
};
- // We expect a single command here because if a source file occurs multiple
- // times in the original CDB, then `computeDependencies` would run the
- // `DependencyScanningAction` once for every time the input occured in the
- // CDB. Instead we split up the CDB into single command chunks to avoid this
- // behavior.
- assert(Compilations.getAllCompileCommands().size() == 1 &&
- "Expected a compilation database with a single command!");
- std::string Input = Compilations.getAllCompileCommands().front().Filename;
-
MakeDependencyPrinterConsumer Consumer;
- auto Result = Worker.computeDependencies(Input, CWD, Compilations, Consumer);
+ auto Result =
+ Worker.computeDependencies(CWD, CommandLine, Consumer, ModuleName);
if (Result)
return std::move(Result);
std::string Output;
@@ -122,8 +111,9 @@ llvm::Expected<std::string> DependencyScanningTool::getDependencyFile(
llvm::Expected<FullDependenciesResult>
DependencyScanningTool::getFullDependencies(
- const tooling::CompilationDatabase &Compilations, StringRef CWD,
- const llvm::StringSet<> &AlreadySeen) {
+ const std::vector<std::string> &CommandLine, StringRef CWD,
+ const llvm::StringSet<> &AlreadySeen,
+ llvm::Optional<StringRef> ModuleName) {
class FullDependencyPrinterConsumer : public DependencyConsumer {
public:
FullDependencyPrinterConsumer(const llvm::StringSet<> &AlreadySeen)
@@ -180,24 +170,15 @@ DependencyScanningTool::getFullDependencies(
private:
std::vector<std::string> Dependencies;
std::vector<PrebuiltModuleDep> PrebuiltModuleDeps;
- std::unordered_map<std::string, ModuleDeps> ClangModuleDeps;
+ std::map<std::string, ModuleDeps> ClangModuleDeps;
std::string ContextHash;
std::vector<std::string> OutputPaths;
const llvm::StringSet<> &AlreadySeen;
};
- // We expect a single command here because if a source file occurs multiple
- // times in the original CDB, then `computeDependencies` would run the
- // `DependencyScanningAction` once for every time the input occured in the
- // CDB. Instead we split up the CDB into single command chunks to avoid this
- // behavior.
- assert(Compilations.getAllCompileCommands().size() == 1 &&
- "Expected a compilation database with a single command!");
- std::string Input = Compilations.getAllCompileCommands().front().Filename;
-
FullDependencyPrinterConsumer Consumer(AlreadySeen);
llvm::Error Result =
- Worker.computeDependencies(Input, CWD, Compilations, Consumer);
+ Worker.computeDependencies(CWD, CommandLine, Consumer, ModuleName);
if (Result)
return std::move(Result);
return Consumer.getFullDependencies();
diff --git a/clang/lib/Tooling/DependencyScanning/DependencyScanningWorker.cpp b/clang/lib/Tooling/DependencyScanning/DependencyScanningWorker.cpp
index d651ff23b387..7fdc49271791 100644
--- a/clang/lib/Tooling/DependencyScanning/DependencyScanningWorker.cpp
+++ b/clang/lib/Tooling/DependencyScanning/DependencyScanningWorker.cpp
@@ -133,6 +133,16 @@ deduceDepTarget(const std::string &OutputFile,
return makeObjFileName(InputFiles.front().getFile());
}
+/// Sanitize diagnostic options for dependency scan.
+static void sanitizeDiagOpts(DiagnosticOptions &DiagOpts) {
+ // Don't print 'X warnings and Y errors generated'.
+ DiagOpts.ShowCarets = false;
+ // Don't write out diagnostic file.
+ DiagOpts.DiagnosticSerializationFile.clear();
+ // Don't treat warnings as errors.
+ DiagOpts.Warnings.push_back("no-error");
+}
+
/// A clang tool that runs the preprocessor in a mode that's optimized for
/// dependency scanning for the given compiler invocation.
class DependencyScanningAction : public tooling::ToolAction {
@@ -141,10 +151,11 @@ public:
StringRef WorkingDirectory, DependencyConsumer &Consumer,
llvm::IntrusiveRefCntPtr<DependencyScanningWorkerFilesystem> DepFS,
ExcludedPreprocessorDirectiveSkipMapping *PPSkipMappings,
- ScanningOutputFormat Format)
+ ScanningOutputFormat Format, bool OptimizeArgs,
+ llvm::Optional<StringRef> ModuleName = None)
: WorkingDirectory(WorkingDirectory), Consumer(Consumer),
- DepFS(std::move(DepFS)), PPSkipMappings(PPSkipMappings),
- Format(Format) {}
+ DepFS(std::move(DepFS)), PPSkipMappings(PPSkipMappings), Format(Format),
+ OptimizeArgs(OptimizeArgs), ModuleName(ModuleName) {}
bool runInvocation(std::shared_ptr<CompilerInvocation> Invocation,
FileManager *FileMgr,
@@ -154,39 +165,34 @@ public:
CompilerInvocation OriginalInvocation(*Invocation);
// Create a compiler instance to handle the actual work.
- CompilerInstance Compiler(std::move(PCHContainerOps));
- Compiler.setInvocation(std::move(Invocation));
+ CompilerInstance ScanInstance(std::move(PCHContainerOps));
+ ScanInstance.setInvocation(std::move(Invocation));
- // Don't print 'X warnings and Y errors generated'.
- Compiler.getDiagnosticOpts().ShowCarets = false;
- // Don't write out diagnostic file.
- Compiler.getDiagnosticOpts().DiagnosticSerializationFile.clear();
- // Don't treat warnings as errors.
- Compiler.getDiagnosticOpts().Warnings.push_back("no-error");
// Create the compiler's actual diagnostics engine.
- Compiler.createDiagnostics(DiagConsumer, /*ShouldOwnClient=*/false);
- if (!Compiler.hasDiagnostics())
+ sanitizeDiagOpts(ScanInstance.getDiagnosticOpts());
+ ScanInstance.createDiagnostics(DiagConsumer, /*ShouldOwnClient=*/false);
+ if (!ScanInstance.hasDiagnostics())
return false;
- Compiler.getPreprocessorOpts().AllowPCHWithDifferentModulesCachePath = true;
+ ScanInstance.getPreprocessorOpts().AllowPCHWithDifferentModulesCachePath =
+ true;
FileMgr->getFileSystemOpts().WorkingDir = std::string(WorkingDirectory);
- Compiler.setFileManager(FileMgr);
- Compiler.createSourceManager(*FileMgr);
+ ScanInstance.setFileManager(FileMgr);
+ ScanInstance.createSourceManager(*FileMgr);
llvm::StringSet<> PrebuiltModulesInputFiles;
// Store the list of prebuilt module files into header search options. This
// will prevent the implicit build to create duplicate modules and will
// force reuse of the existing prebuilt module files instead.
- if (!Compiler.getPreprocessorOpts().ImplicitPCHInclude.empty())
+ if (!ScanInstance.getPreprocessorOpts().ImplicitPCHInclude.empty())
visitPrebuiltModule(
- Compiler.getPreprocessorOpts().ImplicitPCHInclude, Compiler,
- Compiler.getHeaderSearchOpts().PrebuiltModuleFiles,
+ ScanInstance.getPreprocessorOpts().ImplicitPCHInclude, ScanInstance,
+ ScanInstance.getHeaderSearchOpts().PrebuiltModuleFiles,
PrebuiltModulesInputFiles, /*VisitInputFiles=*/DepFS != nullptr);
// Use the dependency scanning optimized file system if requested to do so.
if (DepFS) {
- const CompilerInvocation &CI = Compiler.getInvocation();
DepFS->clearIgnoredFiles();
// Ignore any files that contributed to prebuilt modules. The implicit
// build validates the modules by comparing the reported sizes of their
@@ -197,20 +203,20 @@ public:
// Add any filenames that were explicity passed in the build settings and
// that might be opened, as we want to ensure we don't run source
// minimization on them.
- for (const auto &Entry : CI.getHeaderSearchOpts().UserEntries)
- DepFS->ignoreFile(Entry.Path);
- for (const auto &Entry : CI.getHeaderSearchOpts().VFSOverlayFiles)
- DepFS->ignoreFile(Entry);
+ for (const auto &E : ScanInstance.getHeaderSearchOpts().UserEntries)
+ DepFS->ignoreFile(E.Path);
+ for (const auto &F : ScanInstance.getHeaderSearchOpts().VFSOverlayFiles)
+ DepFS->ignoreFile(F);
// Support for virtual file system overlays on top of the caching
// filesystem.
FileMgr->setVirtualFileSystem(createVFSFromCompilerInvocation(
- CI, Compiler.getDiagnostics(), DepFS));
+ ScanInstance.getInvocation(), ScanInstance.getDiagnostics(), DepFS));
// Pass the skip mappings which should speed up excluded conditional block
// skipping in the preprocessor.
if (PPSkipMappings)
- Compiler.getPreprocessorOpts()
+ ScanInstance.getPreprocessorOpts()
.ExcludedConditionalDirectiveSkipMappings = PPSkipMappings;
}
@@ -222,35 +228,43 @@ public:
// which ensures that the compiler won't create new dependency collectors,
// and thus won't write out the extra '.d' files to disk.
auto Opts = std::make_unique<DependencyOutputOptions>();
- std::swap(*Opts, Compiler.getInvocation().getDependencyOutputOpts());
+ std::swap(*Opts, ScanInstance.getInvocation().getDependencyOutputOpts());
// We need at least one -MT equivalent for the generator of make dependency
// files to work.
if (Opts->Targets.empty())
- Opts->Targets = {deduceDepTarget(Compiler.getFrontendOpts().OutputFile,
- Compiler.getFrontendOpts().Inputs)};
+ Opts->Targets = {
+ deduceDepTarget(ScanInstance.getFrontendOpts().OutputFile,
+ ScanInstance.getFrontendOpts().Inputs)};
Opts->IncludeSystemHeaders = true;
switch (Format) {
case ScanningOutputFormat::Make:
- Compiler.addDependencyCollector(
+ ScanInstance.addDependencyCollector(
std::make_shared<DependencyConsumerForwarder>(std::move(Opts),
Consumer));
break;
case ScanningOutputFormat::Full:
- Compiler.addDependencyCollector(std::make_shared<ModuleDepCollector>(
- std::move(Opts), Compiler, Consumer, std::move(OriginalInvocation)));
+ ScanInstance.addDependencyCollector(std::make_shared<ModuleDepCollector>(
+ std::move(Opts), ScanInstance, Consumer,
+ std::move(OriginalInvocation), OptimizeArgs));
break;
}
// Consider different header search and diagnostic options to create
// different modules. This avoids the unsound aliasing of module PCMs.
//
- // TODO: Implement diagnostic bucketing and header search pruning to reduce
- // the impact of strict context hashing.
- Compiler.getHeaderSearchOpts().ModulesStrictContextHash = true;
+ // TODO: Implement diagnostic bucketing to reduce the impact of strict
+ // context hashing.
+ ScanInstance.getHeaderSearchOpts().ModulesStrictContextHash = true;
+
+ std::unique_ptr<FrontendAction> Action;
+
+ if (ModuleName.hasValue())
+ Action = std::make_unique<GetDependenciesByModuleNameAction>(*ModuleName);
+ else
+ Action = std::make_unique<ReadPCHAndPreprocessAction>();
- auto Action = std::make_unique<ReadPCHAndPreprocessAction>();
- const bool Result = Compiler.ExecuteAction(*Action);
+ const bool Result = ScanInstance.ExecuteAction(*Action);
if (!DepFS)
FileMgr->clearStatCache();
return Result;
@@ -262,15 +276,15 @@ private:
llvm::IntrusiveRefCntPtr<DependencyScanningWorkerFilesystem> DepFS;
ExcludedPreprocessorDirectiveSkipMapping *PPSkipMappings;
ScanningOutputFormat Format;
+ bool OptimizeArgs;
+ llvm::Optional<StringRef> ModuleName;
};
} // end anonymous namespace
DependencyScanningWorker::DependencyScanningWorker(
DependencyScanningService &Service)
- : Format(Service.getFormat()) {
- DiagOpts = new DiagnosticOptions();
-
+ : Format(Service.getFormat()), OptimizeArgs(Service.canOptimizeArgs()) {
PCHContainerOps = std::make_shared<PCHContainerOperations>();
PCHContainerOps->registerReader(
std::make_unique<ObjectFilePCHContainerReader>());
@@ -279,7 +293,12 @@ DependencyScanningWorker::DependencyScanningWorker(
PCHContainerOps->registerWriter(
std::make_unique<ObjectFilePCHContainerWriter>());
- RealFS = llvm::vfs::createPhysicalFileSystem();
+ auto OverlayFS = llvm::makeIntrusiveRefCnt<llvm::vfs::OverlayFileSystem>(
+ llvm::vfs::createPhysicalFileSystem());
+ InMemoryFS = llvm::makeIntrusiveRefCnt<llvm::vfs::InMemoryFileSystem>();
+ OverlayFS->pushOverlay(InMemoryFS);
+ RealFS = OverlayFS;
+
if (Service.canSkipExcludedPPRanges())
PPSkipMappings =
std::make_unique<ExcludedPreprocessorDirectiveSkipMapping>();
@@ -290,36 +309,64 @@ DependencyScanningWorker::DependencyScanningWorker(
Files = new FileManager(FileSystemOptions(), RealFS);
}
-static llvm::Error runWithDiags(
- DiagnosticOptions *DiagOpts,
- llvm::function_ref<bool(DiagnosticConsumer &DC)> BodyShouldSucceed) {
+static llvm::Error
+runWithDiags(DiagnosticOptions *DiagOpts,
+ llvm::function_ref<bool(DiagnosticConsumer &, DiagnosticOptions &)>
+ BodyShouldSucceed) {
+ sanitizeDiagOpts(*DiagOpts);
+
// Capture the emitted diagnostics and report them to the client
// in the case of a failure.
std::string DiagnosticOutput;
llvm::raw_string_ostream DiagnosticsOS(DiagnosticOutput);
TextDiagnosticPrinter DiagPrinter(DiagnosticsOS, DiagOpts);
- if (BodyShouldSucceed(DiagPrinter))
+ if (BodyShouldSucceed(DiagPrinter, *DiagOpts))
return llvm::Error::success();
return llvm::make_error<llvm::StringError>(DiagnosticsOS.str(),
llvm::inconvertibleErrorCode());
}
llvm::Error DependencyScanningWorker::computeDependencies(
- const std::string &Input, StringRef WorkingDirectory,
- const CompilationDatabase &CDB, DependencyConsumer &Consumer) {
+ StringRef WorkingDirectory, const std::vector<std::string> &CommandLine,
+ DependencyConsumer &Consumer, llvm::Optional<StringRef> ModuleName) {
+ // Reset what might have been modified in the previous worker invocation.
RealFS->setCurrentWorkingDirectory(WorkingDirectory);
- return runWithDiags(DiagOpts.get(), [&](DiagnosticConsumer &DC) {
- /// Create the tool that uses the underlying file system to ensure that any
- /// file system requests that are made by the driver do not go through the
- /// dependency scanning filesystem.
- tooling::ClangTool Tool(CDB, Input, PCHContainerOps, RealFS, Files);
- Tool.clearArgumentsAdjusters();
- Tool.setRestoreWorkingDir(false);
- Tool.setPrintErrorMessage(false);
- Tool.setDiagnosticConsumer(&DC);
- DependencyScanningAction Action(WorkingDirectory, Consumer, DepFS,
- PPSkipMappings.get(), Format);
- return !Tool.run(&Action);
- });
+ if (Files)
+ Files->setVirtualFileSystem(RealFS);
+
+ llvm::IntrusiveRefCntPtr<FileManager> CurrentFiles =
+ Files ? Files : new FileManager(FileSystemOptions(), RealFS);
+
+ Optional<std::vector<std::string>> ModifiedCommandLine;
+ if (ModuleName.hasValue()) {
+ ModifiedCommandLine = CommandLine;
+ InMemoryFS->addFile(*ModuleName, 0, llvm::MemoryBuffer::getMemBuffer(""));
+ ModifiedCommandLine->emplace_back(*ModuleName);
+ }
+
+ const std::vector<std::string> &FinalCommandLine =
+ ModifiedCommandLine ? *ModifiedCommandLine : CommandLine;
+
+ std::vector<const char *> FinalCCommandLine(CommandLine.size(), nullptr);
+ llvm::transform(CommandLine, FinalCCommandLine.begin(),
+ [](const std::string &Str) { return Str.c_str(); });
+
+ return runWithDiags(CreateAndPopulateDiagOpts(FinalCCommandLine).release(),
+ [&](DiagnosticConsumer &DC, DiagnosticOptions &DiagOpts) {
+ DependencyScanningAction Action(
+ WorkingDirectory, Consumer, DepFS,
+ PPSkipMappings.get(), Format, OptimizeArgs,
+ ModuleName);
+ // Create an invocation that uses the underlying file
+ // system to ensure that any file system requests that
+ // are made by the driver do not go through the
+ // dependency scanning filesystem.
+ ToolInvocation Invocation(FinalCommandLine, &Action,
+ CurrentFiles.get(),
+ PCHContainerOps);
+ Invocation.setDiagnosticConsumer(&DC);
+ Invocation.setDiagnosticOptions(&DiagOpts);
+ return Invocation.run();
+ });
}
diff --git a/clang/lib/Tooling/DependencyScanning/ModuleDepCollector.cpp b/clang/lib/Tooling/DependencyScanning/ModuleDepCollector.cpp
index 88cee63c98aa..383a850301a1 100644
--- a/clang/lib/Tooling/DependencyScanning/ModuleDepCollector.cpp
+++ b/clang/lib/Tooling/DependencyScanning/ModuleDepCollector.cpp
@@ -1,9 +1,8 @@
//===- ModuleDepCollector.cpp - Callbacks to collect deps -------*- C++ -*-===//
//
-// The LLVM Compiler Infrastructure
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
@@ -18,11 +17,26 @@ using namespace clang;
using namespace tooling;
using namespace dependencies;
+static void optimizeHeaderSearchOpts(HeaderSearchOptions &Opts,
+ ASTReader &Reader,
+ const serialization::ModuleFile &MF) {
+ // Only preserve search paths that were used during the dependency scan.
+ std::vector<HeaderSearchOptions::Entry> Entries = Opts.UserEntries;
+ Opts.UserEntries.clear();
+ for (unsigned I = 0; I < Entries.size(); ++I)
+ if (MF.SearchPathUsage[I])
+ Opts.UserEntries.push_back(Entries[I]);
+}
+
CompilerInvocation ModuleDepCollector::makeInvocationForModuleBuildWithoutPaths(
- const ModuleDeps &Deps) const {
+ const ModuleDeps &Deps,
+ llvm::function_ref<void(CompilerInvocation &)> Optimize) const {
// Make a deep copy of the original Clang invocation.
CompilerInvocation CI(OriginalInvocation);
+ CI.getLangOpts()->resetNonModularOptions();
+ CI.getPreprocessorOpts().resetNonModularOptions();
+
// Remove options incompatible with explicit module build.
CI.getFrontendOpts().Inputs.clear();
CI.getFrontendOpts().OutputFile.clear();
@@ -34,12 +48,18 @@ CompilerInvocation ModuleDepCollector::makeInvocationForModuleBuildWithoutPaths(
CI.getLangOpts()->ImplicitModules = false;
// Report the prebuilt modules this module uses.
- for (const auto &PrebuiltModule : Deps.PrebuiltModuleDeps) {
+ for (const auto &PrebuiltModule : Deps.PrebuiltModuleDeps)
CI.getFrontendOpts().ModuleFiles.push_back(PrebuiltModule.PCMFile);
- CI.getFrontendOpts().ModuleMapFiles.push_back(PrebuiltModule.ModuleMapFile);
- }
- CI.getPreprocessorOpts().ImplicitPCHInclude.clear();
+ Optimize(CI);
+
+ // The original invocation probably didn't have strict context hash enabled.
+ // We will use the context hash of this invocation to distinguish between
+ // multiple incompatible versions of the same module and will use it when
+ // reporting dependencies to the clients. Let's make sure we're using
+ // **strict** context hash in order to prevent accidental sharing of
+ // incompatible modules (e.g. with differences in search paths).
+ CI.getHeaderSearchOpts().ModulesStrictContextHash = true;
return CI;
}
@@ -62,7 +82,7 @@ serializeCompilerInvocation(const CompilerInvocation &CI) {
std::vector<std::string> ModuleDeps::getCanonicalCommandLine(
std::function<StringRef(ModuleID)> LookupPCMPath,
std::function<const ModuleDeps &(ModuleID)> LookupModuleDeps) const {
- CompilerInvocation CI(Invocation);
+ CompilerInvocation CI(BuildInvocation);
FrontendOptions &FrontendOpts = CI.getFrontendOpts();
InputKind ModuleMapInputKind(FrontendOpts.DashX.getLanguage(),
@@ -79,7 +99,7 @@ std::vector<std::string> ModuleDeps::getCanonicalCommandLine(
std::vector<std::string>
ModuleDeps::getCanonicalCommandLineWithoutModulePaths() const {
- return serializeCompilerInvocation(Invocation);
+ return serializeCompilerInvocation(BuildInvocation);
}
void dependencies::detail::collectPCMAndModuleMapPaths(
@@ -112,15 +132,15 @@ void ModuleDepCollectorPP::FileChanged(SourceLocation Loc,
FileID PrevFID) {
if (Reason != PPCallbacks::EnterFile)
return;
-
+
// This has to be delayed as the context hash can change at the start of
// `CompilerInstance::ExecuteAction`.
if (MDC.ContextHash.empty()) {
- MDC.ContextHash = Instance.getInvocation().getModuleHash();
+ MDC.ContextHash = MDC.ScanInstance.getInvocation().getModuleHash();
MDC.Consumer.handleContextHash(MDC.ContextHash);
}
- SourceManager &SM = Instance.getSourceManager();
+ SourceManager &SM = MDC.ScanInstance.getSourceManager();
// Dependency generation really does want to go all the way to the
// file entry for a source location to find out what is depended on.
@@ -163,12 +183,14 @@ void ModuleDepCollectorPP::handleImport(const Module *Imported) {
}
void ModuleDepCollectorPP::EndOfMainFile() {
- FileID MainFileID = Instance.getSourceManager().getMainFileID();
- MDC.MainFile = std::string(
- Instance.getSourceManager().getFileEntryForID(MainFileID)->getName());
+ FileID MainFileID = MDC.ScanInstance.getSourceManager().getMainFileID();
+ MDC.MainFile = std::string(MDC.ScanInstance.getSourceManager()
+ .getFileEntryForID(MainFileID)
+ ->getName());
- if (!Instance.getPreprocessorOpts().ImplicitPCHInclude.empty())
- MDC.FileDeps.push_back(Instance.getPreprocessorOpts().ImplicitPCHInclude);
+ if (!MDC.ScanInstance.getPreprocessorOpts().ImplicitPCHInclude.empty())
+ MDC.FileDeps.push_back(
+ MDC.ScanInstance.getPreprocessorOpts().ImplicitPCHInclude);
for (const Module *M : DirectModularDeps) {
// A top-level module might not be actually imported as a module when
@@ -207,15 +229,16 @@ ModuleID ModuleDepCollectorPP::handleTopLevelModule(const Module *M) {
MD.ImplicitModulePCMPath = std::string(M->getASTFile()->getName());
MD.IsSystem = M->IsSystem;
- const FileEntry *ModuleMap = Instance.getPreprocessor()
+ const FileEntry *ModuleMap = MDC.ScanInstance.getPreprocessor()
.getHeaderSearchInfo()
.getModuleMap()
.getModuleMapFileForUniquing(M);
MD.ClangModuleMapFile = std::string(ModuleMap ? ModuleMap->getName() : "");
serialization::ModuleFile *MF =
- MDC.Instance.getASTReader()->getModuleManager().lookup(M->getASTFile());
- MDC.Instance.getASTReader()->visitInputFiles(
+ MDC.ScanInstance.getASTReader()->getModuleManager().lookup(
+ M->getASTFile());
+ MDC.ScanInstance.getASTReader()->visitInputFiles(
*MF, true, true, [&](const serialization::InputFile &IF, bool isSystem) {
// __inferred_module.map is the result of the way in which an implicit
// module build handles inferred modules. It adds an overlay VFS with
@@ -232,10 +255,16 @@ ModuleID ModuleDepCollectorPP::handleTopLevelModule(const Module *M) {
// Add direct prebuilt module dependencies now, so that we can use them when
// creating a CompilerInvocation and computing context hash for this
// ModuleDeps instance.
- addDirectPrebuiltModuleDeps(M, MD);
+ llvm::DenseSet<const Module *> SeenModules;
+ addAllSubmodulePrebuiltDeps(M, MD, SeenModules);
- MD.Invocation = MDC.makeInvocationForModuleBuildWithoutPaths(MD);
- MD.ID.ContextHash = MD.Invocation.getModuleHash();
+ MD.BuildInvocation = MDC.makeInvocationForModuleBuildWithoutPaths(
+ MD, [&](CompilerInvocation &BuildInvocation) {
+ if (MDC.OptimizeArgs)
+ optimizeHeaderSearchOpts(BuildInvocation.getHeaderSearchOpts(),
+ *MDC.ScanInstance.getASTReader(), *MF);
+ });
+ MD.ID.ContextHash = MD.BuildInvocation.getModuleHash();
llvm::DenseSet<const Module *> AddedModules;
addAllSubmoduleDeps(M, MD, AddedModules);
@@ -243,12 +272,23 @@ ModuleID ModuleDepCollectorPP::handleTopLevelModule(const Module *M) {
return MD.ID;
}
-void ModuleDepCollectorPP::addDirectPrebuiltModuleDeps(const Module *M,
- ModuleDeps &MD) {
+void ModuleDepCollectorPP::addAllSubmodulePrebuiltDeps(
+ const Module *M, ModuleDeps &MD,
+ llvm::DenseSet<const Module *> &SeenSubmodules) {
+ addModulePrebuiltDeps(M, MD, SeenSubmodules);
+
+ for (const Module *SubM : M->submodules())
+ addAllSubmodulePrebuiltDeps(SubM, MD, SeenSubmodules);
+}
+
+void ModuleDepCollectorPP::addModulePrebuiltDeps(
+ const Module *M, ModuleDeps &MD,
+ llvm::DenseSet<const Module *> &SeenSubmodules) {
for (const Module *Import : M->Imports)
if (Import->getTopLevelModule() != M->getTopLevelModule())
- if (MDC.isPrebuiltModule(Import))
- MD.PrebuiltModuleDeps.emplace_back(Import);
+ if (MDC.isPrebuiltModule(Import->getTopLevelModule()))
+ if (SeenSubmodules.insert(Import->getTopLevelModule()).second)
+ MD.PrebuiltModuleDeps.emplace_back(Import->getTopLevelModule());
}
void ModuleDepCollectorPP::addAllSubmoduleDeps(
@@ -274,13 +314,14 @@ void ModuleDepCollectorPP::addModuleDep(
}
ModuleDepCollector::ModuleDepCollector(
- std::unique_ptr<DependencyOutputOptions> Opts, CompilerInstance &I,
- DependencyConsumer &C, CompilerInvocation &&OriginalCI)
- : Instance(I), Consumer(C), Opts(std::move(Opts)),
- OriginalInvocation(std::move(OriginalCI)) {}
+ std::unique_ptr<DependencyOutputOptions> Opts,
+ CompilerInstance &ScanInstance, DependencyConsumer &C,
+ CompilerInvocation &&OriginalCI, bool OptimizeArgs)
+ : ScanInstance(ScanInstance), Consumer(C), Opts(std::move(Opts)),
+ OriginalInvocation(std::move(OriginalCI)), OptimizeArgs(OptimizeArgs) {}
void ModuleDepCollector::attachToPreprocessor(Preprocessor &PP) {
- PP.addPPCallbacks(std::make_unique<ModuleDepCollectorPP>(Instance, *this));
+ PP.addPPCallbacks(std::make_unique<ModuleDepCollectorPP>(*this));
}
void ModuleDepCollector::attachToASTReader(ASTReader &R) {}
@@ -288,7 +329,7 @@ void ModuleDepCollector::attachToASTReader(ASTReader &R) {}
bool ModuleDepCollector::isPrebuiltModule(const Module *M) {
std::string Name(M->getTopLevelModuleName());
const auto &PrebuiltModuleFiles =
- Instance.getHeaderSearchOpts().PrebuiltModuleFiles;
+ ScanInstance.getHeaderSearchOpts().PrebuiltModuleFiles;
auto PrebuiltModuleFileIt = PrebuiltModuleFiles.find(Name);
if (PrebuiltModuleFileIt == PrebuiltModuleFiles.end())
return false;
diff --git a/clang/lib/Tooling/DumpTool/ClangSrcLocDump.cpp b/clang/lib/Tooling/DumpTool/ClangSrcLocDump.cpp
index 8091a467d056..9c825428f2ea 100644
--- a/clang/lib/Tooling/DumpTool/ClangSrcLocDump.cpp
+++ b/clang/lib/Tooling/DumpTool/ClangSrcLocDump.cpp
@@ -91,12 +91,8 @@ int main(int argc, const char **argv) {
llvm::transform(Args, Argv.begin(),
[](const std::string &Arg) { return Arg.c_str(); });
- IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts = new DiagnosticOptions();
- unsigned MissingArgIndex, MissingArgCount;
- auto Opts = driver::getDriverOptTable();
- auto ParsedArgs = Opts.ParseArgs(llvm::makeArrayRef(Argv).slice(1),
- MissingArgIndex, MissingArgCount);
- ParseDiagnosticArgs(*DiagOpts, ParsedArgs);
+ IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts =
+ CreateAndPopulateDiagOpts(Argv);
// Don't output diagnostics, because common scenarios such as
// cross-compiling fail with diagnostics. This is not fatal, but
diff --git a/clang/lib/Tooling/JSONCompilationDatabase.cpp b/clang/lib/Tooling/JSONCompilationDatabase.cpp
index 97ba7e411fbb..5e18d7a576c0 100644
--- a/clang/lib/Tooling/JSONCompilationDatabase.cpp
+++ b/clang/lib/Tooling/JSONCompilationDatabase.cpp
@@ -135,15 +135,12 @@ class CommandLineArgumentParser {
std::vector<std::string> unescapeCommandLine(JSONCommandLineSyntax Syntax,
StringRef EscapedCommandLine) {
if (Syntax == JSONCommandLineSyntax::AutoDetect) {
+#ifdef _WIN32
+ // Assume Windows command line parsing on Win32
+ Syntax = JSONCommandLineSyntax::Windows;
+#else
Syntax = JSONCommandLineSyntax::Gnu;
- llvm::Triple Triple(llvm::sys::getProcessTriple());
- if (Triple.getOS() == llvm::Triple::OSType::Win32) {
- // Assume Windows command line parsing on Win32 unless the triple
- // explicitly tells us otherwise.
- if (!Triple.hasEnvironment() ||
- Triple.getEnvironment() == llvm::Triple::EnvironmentType::MSVC)
- Syntax = JSONCommandLineSyntax::Windows;
- }
+#endif
}
if (Syntax == JSONCommandLineSyntax::Windows) {
diff --git a/clang/lib/Tooling/Syntax/BuildTree.cpp b/clang/lib/Tooling/Syntax/BuildTree.cpp
index 07888b5c32fa..fcac2250dd96 100644
--- a/clang/lib/Tooling/Syntax/BuildTree.cpp
+++ b/clang/lib/Tooling/Syntax/BuildTree.cpp
@@ -155,9 +155,8 @@ private:
} // namespace
static CallExpr::arg_range dropDefaultArgs(CallExpr::arg_range Args) {
- auto FirstDefaultArg = std::find_if(Args.begin(), Args.end(), [](auto It) {
- return isa<CXXDefaultArgExpr>(It);
- });
+ auto FirstDefaultArg =
+ llvm::find_if(Args, [](auto It) { return isa<CXXDefaultArgExpr>(It); });
return llvm::make_range(Args.begin(), FirstDefaultArg);
}
diff --git a/clang/lib/Tooling/Tooling.cpp b/clang/lib/Tooling/Tooling.cpp
index 5242134097da..6314615f83c8 100644
--- a/clang/lib/Tooling/Tooling.cpp
+++ b/clang/lib/Tooling/Tooling.cpp
@@ -343,23 +343,27 @@ bool ToolInvocation::run() {
for (const std::string &Str : CommandLine)
Argv.push_back(Str.c_str());
const char *const BinaryName = Argv[0];
- IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts = new DiagnosticOptions();
- unsigned MissingArgIndex, MissingArgCount;
- llvm::opt::InputArgList ParsedArgs = driver::getDriverOptTable().ParseArgs(
- ArrayRef<const char *>(Argv).slice(1), MissingArgIndex, MissingArgCount);
- ParseDiagnosticArgs(*DiagOpts, ParsedArgs);
- TextDiagnosticPrinter DiagnosticPrinter(
- llvm::errs(), &*DiagOpts);
- DiagnosticsEngine Diagnostics(
- IntrusiveRefCntPtr<DiagnosticIDs>(new DiagnosticIDs()), &*DiagOpts,
- DiagConsumer ? DiagConsumer : &DiagnosticPrinter, false);
+
+ // Parse diagnostic options from the driver command-line only if none were
+ // explicitly set.
+ IntrusiveRefCntPtr<DiagnosticOptions> ParsedDiagOpts;
+ DiagnosticOptions *DiagOpts = this->DiagOpts;
+ if (!DiagOpts) {
+ ParsedDiagOpts = CreateAndPopulateDiagOpts(Argv);
+ DiagOpts = &*ParsedDiagOpts;
+ }
+
+ TextDiagnosticPrinter DiagnosticPrinter(llvm::errs(), DiagOpts);
+ IntrusiveRefCntPtr<DiagnosticsEngine> Diagnostics =
+ CompilerInstance::createDiagnostics(
+ &*DiagOpts, DiagConsumer ? DiagConsumer : &DiagnosticPrinter, false);
// Although `Diagnostics` are used only for command-line parsing, the custom
// `DiagConsumer` might expect a `SourceManager` to be present.
- SourceManager SrcMgr(Diagnostics, *Files);
- Diagnostics.setSourceManager(&SrcMgr);
+ SourceManager SrcMgr(*Diagnostics, *Files);
+ Diagnostics->setSourceManager(&SrcMgr);
const std::unique_ptr<driver::Driver> Driver(
- newDriver(&Diagnostics, BinaryName, &Files->getVirtualFileSystem()));
+ newDriver(&*Diagnostics, BinaryName, &Files->getVirtualFileSystem()));
// The "input file not found" diagnostics from the driver are useful.
// The driver is only aware of the VFS working directory, but some clients
// change this at the FileManager level instead.
@@ -371,11 +375,11 @@ bool ToolInvocation::run() {
if (!Compilation)
return false;
const llvm::opt::ArgStringList *const CC1Args = getCC1Arguments(
- &Diagnostics, Compilation.get());
+ &*Diagnostics, Compilation.get());
if (!CC1Args)
return false;
std::unique_ptr<CompilerInvocation> Invocation(
- newInvocation(&Diagnostics, *CC1Args, BinaryName));
+ newInvocation(&*Diagnostics, *CC1Args, BinaryName));
return runInvocation(BinaryName, Compilation.get(), std::move(Invocation),
std::move(PCHContainerOps));
}
diff --git a/clang/lib/Tooling/Transformer/Parsing.cpp b/clang/lib/Tooling/Transformer/Parsing.cpp
index 66fa04a15594..242db2a16b43 100644
--- a/clang/lib/Tooling/Transformer/Parsing.cpp
+++ b/clang/lib/Tooling/Transformer/Parsing.cpp
@@ -165,7 +165,7 @@ static ExpectedProgress<llvm::NoneType> parseChar(char c, ParseState State) {
static ExpectedProgress<std::string> parseId(ParseState State) {
State.Input = consumeWhitespace(State.Input);
auto Id = State.Input.take_while(
- [](char c) { return isASCII(c) && isIdentifierBody(c); });
+ [](char c) { return isASCII(c) && isAsciiIdentifierContinue(c); });
if (Id.empty())
return makeParseError(State, "failed to parse name");
return makeParseProgress(advance(State, Id.size()), Id.str());
diff --git a/clang/lib/Tooling/Transformer/Stencil.cpp b/clang/lib/Tooling/Transformer/Stencil.cpp
index 4dc3544bb06d..8b20ef34c3ff 100644
--- a/clang/lib/Tooling/Transformer/Stencil.cpp
+++ b/clang/lib/Tooling/Transformer/Stencil.cpp
@@ -27,14 +27,15 @@
using namespace clang;
using namespace transformer;
+using ast_matchers::BoundNodes;
using ast_matchers::MatchFinder;
using llvm::errc;
using llvm::Error;
using llvm::Expected;
using llvm::StringError;
-static llvm::Expected<DynTypedNode>
-getNode(const ast_matchers::BoundNodes &Nodes, StringRef Id) {
+static llvm::Expected<DynTypedNode> getNode(const BoundNodes &Nodes,
+ StringRef Id) {
auto &NodesMap = Nodes.getMap();
auto It = NodesMap.find(Id);
if (It == NodesMap.end())
@@ -366,6 +367,73 @@ public:
}
};
+class SelectBoundStencil : public clang::transformer::StencilInterface {
+ static bool containsNoNullStencils(
+ const std::vector<std::pair<std::string, Stencil>> &Cases) {
+ for (const auto &S : Cases)
+ if (S.second == nullptr)
+ return false;
+ return true;
+ }
+
+public:
+ SelectBoundStencil(std::vector<std::pair<std::string, Stencil>> Cases,
+ Stencil Default)
+ : CaseStencils(std::move(Cases)), DefaultStencil(std::move(Default)) {
+ assert(containsNoNullStencils(CaseStencils) &&
+ "cases of selectBound may not be null");
+ }
+ ~SelectBoundStencil() override{};
+
+ llvm::Error eval(const MatchFinder::MatchResult &match,
+ std::string *result) const override {
+ const BoundNodes::IDToNodeMap &NodeMap = match.Nodes.getMap();
+ for (const auto &S : CaseStencils) {
+ if (NodeMap.count(S.first) > 0) {
+ return S.second->eval(match, result);
+ }
+ }
+
+ if (DefaultStencil != nullptr) {
+ return DefaultStencil->eval(match, result);
+ }
+
+ llvm::SmallVector<llvm::StringRef, 2> CaseIDs;
+ CaseIDs.reserve(CaseStencils.size());
+ for (const auto &S : CaseStencils)
+ CaseIDs.emplace_back(S.first);
+
+ return llvm::createStringError(
+ errc::result_out_of_range,
+ llvm::Twine("selectBound failed: no cases bound and no default: {") +
+ llvm::join(CaseIDs, ", ") + "}");
+ }
+
+ std::string toString() const override {
+ std::string Buffer;
+ llvm::raw_string_ostream Stream(Buffer);
+ Stream << "selectBound({";
+ bool First = true;
+ for (const auto &S : CaseStencils) {
+ if (First)
+ First = false;
+ else
+ Stream << "}, ";
+ Stream << "{\"" << S.first << "\", " << S.second->toString();
+ }
+ Stream << "}}";
+ if (DefaultStencil != nullptr) {
+ Stream << ", " << DefaultStencil->toString();
+ }
+ Stream << ")";
+ return Stream.str();
+ }
+
+private:
+ std::vector<std::pair<std::string, Stencil>> CaseStencils;
+ Stencil DefaultStencil;
+};
+
class SequenceStencil : public StencilInterface {
std::vector<Stencil> Stencils;
@@ -462,6 +530,13 @@ Stencil transformer::ifBound(StringRef Id, Stencil TrueStencil,
std::move(FalseStencil));
}
+Stencil transformer::selectBound(
+ std::vector<std::pair<std::string, Stencil>> CaseStencils,
+ Stencil DefaultStencil) {
+ return std::make_shared<SelectBoundStencil>(std::move(CaseStencils),
+ std::move(DefaultStencil));
+}
+
Stencil transformer::run(MatchConsumer<std::string> Fn) {
return std::make_shared<RunStencil>(std::move(Fn));
}