diff --git a/clang/include/clang/Tooling/Tooling.h b/clang/include/clang/Tooling/Tooling.h --- a/clang/include/clang/Tooling/Tooling.h +++ b/clang/include/clang/Tooling/Tooling.h @@ -268,11 +268,17 @@ ~ToolInvocation(); - /// Set a \c DiagnosticConsumer to use during parsing. + /// Set a \c DiagnosticConsumer to use during driver command-line parsing and + /// the action invocation itself. void setDiagnosticConsumer(DiagnosticConsumer *DiagConsumer) { this->DiagConsumer = DiagConsumer; } + /// Set a \c DiagnosticOptions to use during driver command-line parsing. + void setDiagnosticOptions(DiagnosticOptions *DiagOpts) { + this->DiagOpts = DiagOpts; + } + /// Run the clang invocation. /// /// \returns True if there were no errors during execution. @@ -290,6 +296,7 @@ FileManager *Files; std::shared_ptr PCHContainerOps; DiagnosticConsumer *DiagConsumer = nullptr; + DiagnosticOptions *DiagOpts = nullptr; }; /// Utility to run a FrontendAction over a set of files. diff --git a/clang/lib/Tooling/Tooling.cpp b/clang/lib/Tooling/Tooling.cpp --- a/clang/lib/Tooling/Tooling.cpp +++ b/clang/lib/Tooling/Tooling.cpp @@ -343,10 +343,17 @@ for (const std::string &Str : CommandLine) Argv.push_back(Str.c_str()); const char *const BinaryName = Argv[0]; - IntrusiveRefCntPtr DiagOpts = - CreateAndPopulateDiagOpts(Argv); - TextDiagnosticPrinter DiagnosticPrinter( - llvm::errs(), &*DiagOpts); + + // Parse diagnostic options from the driver command-line only if none were + // explicitly set. + IntrusiveRefCntPtr ParsedDiagOpts; + DiagnosticOptions *DiagOpts = this->DiagOpts; + if (!DiagOpts) { + ParsedDiagOpts = CreateAndPopulateDiagOpts(Argv); + DiagOpts = &*ParsedDiagOpts; + } + + TextDiagnosticPrinter DiagnosticPrinter(llvm::errs(), DiagOpts); IntrusiveRefCntPtr Diagnostics = CompilerInstance::createDiagnostics( &*DiagOpts, DiagConsumer ? DiagConsumer : &DiagnosticPrinter, false); diff --git a/clang/unittests/Tooling/ToolingTest.cpp b/clang/unittests/Tooling/ToolingTest.cpp --- a/clang/unittests/Tooling/ToolingTest.cpp +++ b/clang/unittests/Tooling/ToolingTest.cpp @@ -265,6 +265,41 @@ EXPECT_EQ(Consumer.NumErrorsSeen, 0u); } +TEST(ToolInvocation, CustomDiagnosticOptionsOverwriteParsedOnes) { + llvm::IntrusiveRefCntPtr OverlayFileSystem( + new llvm::vfs::OverlayFileSystem(llvm::vfs::getRealFileSystem())); + llvm::IntrusiveRefCntPtr InMemoryFileSystem( + new llvm::vfs::InMemoryFileSystem); + OverlayFileSystem->pushOverlay(InMemoryFileSystem); + llvm::IntrusiveRefCntPtr Files( + new FileManager(FileSystemOptions(), OverlayFileSystem)); + + std::vector Args; + Args.push_back("tool-executable"); + Args.push_back("-target"); + // Invalid argument that by default results in an error diagnostic: + Args.push_back("i386-apple-ios14.0-simulator"); + // Argument that downgrades the error into a warning: + Args.push_back("-Wno-error=invalid-ios-deployment-target"); + Args.push_back("-fsyntax-only"); + Args.push_back("test.cpp"); + + clang::tooling::ToolInvocation Invocation( + Args, std::make_unique(), Files.get()); + InMemoryFileSystem->addFile( + "test.cpp", 0, llvm::MemoryBuffer::getMemBuffer("int a() {}\n")); + ErrorCountingDiagnosticConsumer Consumer; + Invocation.setDiagnosticConsumer(&Consumer); + + auto DiagOpts = llvm::makeIntrusiveRefCnt(); + Invocation.setDiagnosticOptions(&*DiagOpts); + + EXPECT_TRUE(Invocation.run()); + // Check that `-Wno-error=invalid-ios-deployment-target` didn't downgrade the + // error into a warning due to being overwritten by custom diagnostic options. + EXPECT_EQ(Consumer.NumErrorsSeen, 1u); +} + struct DiagnosticConsumerExpectingSourceManager : public DiagnosticConsumer { bool SawSourceManager;