diff options
| author | Dimitry Andric <dim@FreeBSD.org> | 2021-11-19 20:06:13 +0000 |
|---|---|---|
| committer | Dimitry Andric <dim@FreeBSD.org> | 2021-11-19 20:06:13 +0000 |
| commit | c0981da47d5696fe36474fcf86b4ce03ae3ff818 (patch) | |
| tree | f42add1021b9f2ac6a69ac7cf6c4499962739a45 /clang/lib/Tooling | |
| parent | 344a3780b2e33f6ca763666c380202b18aab72a3 (diff) | |
Diffstat (limited to 'clang/lib/Tooling')
| -rw-r--r-- | clang/lib/Tooling/CommonOptionsParser.cpp | 2 | ||||
| -rw-r--r-- | clang/lib/Tooling/DependencyScanning/DependencyScanningService.cpp | 4 | ||||
| -rw-r--r-- | clang/lib/Tooling/DependencyScanning/DependencyScanningTool.cpp | 41 | ||||
| -rw-r--r-- | clang/lib/Tooling/DependencyScanning/DependencyScanningWorker.cpp | 167 | ||||
| -rw-r--r-- | clang/lib/Tooling/DependencyScanning/ModuleDepCollector.cpp | 111 | ||||
| -rw-r--r-- | clang/lib/Tooling/DumpTool/ClangSrcLocDump.cpp | 8 | ||||
| -rw-r--r-- | clang/lib/Tooling/JSONCompilationDatabase.cpp | 13 | ||||
| -rw-r--r-- | clang/lib/Tooling/Syntax/BuildTree.cpp | 5 | ||||
| -rw-r--r-- | clang/lib/Tooling/Tooling.cpp | 34 | ||||
| -rw-r--r-- | clang/lib/Tooling/Transformer/Parsing.cpp | 2 | ||||
| -rw-r--r-- | clang/lib/Tooling/Transformer/Stencil.cpp | 79 |
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)); } |
