From 999e4c4793fb8983011fbba3858c6c48b8c89248 Mon Sep 17 00:00:00 2001 From: Ilya Biryukov Date: Tue, 27 Aug 2019 10:02:18 +0000 Subject: [PATCH] [Driver] Add an option for createInvocationFromCommandLine to recover on errors Summary: Previously, it would always return nullptr on any error. This change adds a parameter, controlling whether the function should attempt to return a non-null result even if unknown arguments (or other errors were encountered). The new behavior is only used in clangd. Considered an alternative of changing the return value instead of adding a new parameter, but that would require updating all callsites. Settled with the parameter to minimize the code changes. Reviewers: gribozavr Reviewed By: gribozavr Subscribers: nridge, jkorous, arphaman, kadircet, cfe-commits Tags: #clang Differential Revision: https://reviews.llvm.org/D66731 llvm-svn: 370033 --- clang-tools-extra/clangd/Compiler.cpp | 3 ++- .../clangd/unittests/ClangdUnitTests.cpp | 16 ++++++++++++++++ .../include/clang/Frontend/CompilerInvocation.h | 5 +++++ clang/include/clang/Frontend/Utils.h | 7 ++++++- .../Frontend/CreateInvocationFromCommandLine.cpp | 16 +++++----------- 5 files changed, 34 insertions(+), 13 deletions(-) diff --git a/clang-tools-extra/clangd/Compiler.cpp b/clang-tools-extra/clangd/Compiler.cpp index 22d1fc956bff..7080e20e879e 100644 --- a/clang-tools-extra/clangd/Compiler.cpp +++ b/clang-tools-extra/clangd/Compiler.cpp @@ -59,7 +59,8 @@ buildCompilerInvocation(const ParseInputs &Inputs) { CompilerInstance::createDiagnostics(new DiagnosticOptions, &IgnoreDiagnostics, false); std::unique_ptr CI = createInvocationFromCommandLine( - ArgStrs, CommandLineDiagsEngine, Inputs.FS); + ArgStrs, CommandLineDiagsEngine, Inputs.FS, + /*ShouldRecoverOnErrors=*/true); if (!CI) return nullptr; // createInvocationFromCommandLine sets DisableFree. diff --git a/clang-tools-extra/clangd/unittests/ClangdUnitTests.cpp b/clang-tools-extra/clangd/unittests/ClangdUnitTests.cpp index ad013e24a16d..7fe57025dc77 100644 --- a/clang-tools-extra/clangd/unittests/ClangdUnitTests.cpp +++ b/clang-tools-extra/clangd/unittests/ClangdUnitTests.cpp @@ -9,7 +9,9 @@ #include "AST.h" #include "Annotations.h" #include "ClangdUnit.h" +#include "Compiler.h" #include "SourceCode.h" +#include "TestFS.h" #include "TestTU.h" #include "clang/AST/DeclTemplate.h" #include "clang/Basic/TokenKinds.h" @@ -244,6 +246,20 @@ TEST(ClangdUnitTest, NoCrashOnTokensWithTidyCheck) { EXPECT_EQ(T.expandedTokens().drop_back().back().text(SM), "}"); } +TEST(ClangdUnitTest, CanBuildInvocationWithUnknownArgs) { + // Unknown flags should not prevent a build of compiler invocation. + ParseInputs Inputs; + Inputs.FS = buildTestFS({{testPath("foo.cpp"), "void test() {}"}}); + Inputs.CompileCommand.CommandLine = {"clang", "-fsome-unknown-flag", + testPath("foo.cpp")}; + EXPECT_NE(buildCompilerInvocation(Inputs), nullptr); + + // Unknown forwarded to -cc1 should not a failure either. + Inputs.CompileCommand.CommandLine = { + "clang", "-Xclang", "-fsome-unknown-flag", testPath("foo.cpp")}; + EXPECT_NE(buildCompilerInvocation(Inputs), nullptr); +} + } // namespace } // namespace clangd } // namespace clang diff --git a/clang/include/clang/Frontend/CompilerInvocation.h b/clang/include/clang/Frontend/CompilerInvocation.h index 191d2ae9c708..f0280232af0d 100644 --- a/clang/include/clang/Frontend/CompilerInvocation.h +++ b/clang/include/clang/Frontend/CompilerInvocation.h @@ -147,6 +147,11 @@ public: /// Create a compiler invocation from a list of input options. /// \returns true on success. /// + /// \returns false if an error was encountered while parsing the arguments + /// and attempts to recover and continue parsing the rest of the arguments. + /// The recovery is best-effort and only guarantees that \p Res will end up in + /// one of the vaild-to-access (albeit arbitrary) states. + /// /// \param [out] Res - The resulting invocation. /// \param ArgBegin - The first element in the argument vector. /// \param ArgEnd - The last element in the argument vector. diff --git a/clang/include/clang/Frontend/Utils.h b/clang/include/clang/Frontend/Utils.h index 04e786f82ec7..0f9b17ee5089 100644 --- a/clang/include/clang/Frontend/Utils.h +++ b/clang/include/clang/Frontend/Utils.h @@ -213,13 +213,18 @@ createChainedIncludesSource(CompilerInstance &CI, /// createInvocationFromCommandLine - Construct a compiler invocation object for /// a command line argument vector. /// +/// \param ShouldRecoverOnErrors - whether we should attempt to return a +/// non-null (and possibly incorrect) CompilerInvocation if any errors were +/// encountered. When this flag is false, always return null on errors. +/// /// \return A CompilerInvocation, or 0 if none was built for the given /// argument vector. std::unique_ptr createInvocationFromCommandLine( ArrayRef Args, IntrusiveRefCntPtr Diags = IntrusiveRefCntPtr(), - IntrusiveRefCntPtr VFS = nullptr); + IntrusiveRefCntPtr VFS = nullptr, + bool ShouldRecoverOnErrors = false); /// Return the value of the last argument as an integer, or a default. If Diags /// is non-null, emits an error if the argument is given, but non-integral. diff --git a/clang/lib/Frontend/CreateInvocationFromCommandLine.cpp b/clang/lib/Frontend/CreateInvocationFromCommandLine.cpp index d70a3b9a42d7..ea7de7a41112 100644 --- a/clang/lib/Frontend/CreateInvocationFromCommandLine.cpp +++ b/clang/lib/Frontend/CreateInvocationFromCommandLine.cpp @@ -24,14 +24,9 @@ using namespace clang; using namespace llvm::opt; -/// createInvocationFromCommandLine - Construct a compiler invocation object for -/// a command line argument vector. -/// -/// \return A CompilerInvocation, or 0 if none was built for the given -/// argument vector. std::unique_ptr clang::createInvocationFromCommandLine( ArrayRef ArgList, IntrusiveRefCntPtr Diags, - IntrusiveRefCntPtr VFS) { + IntrusiveRefCntPtr VFS, bool ShouldRecoverOnErorrs) { if (!Diags.get()) { // No diagnostics engine was provided, so create our own diagnostics object // with the default options. @@ -95,11 +90,10 @@ std::unique_ptr clang::createInvocationFromCommandLine( const ArgStringList &CCArgs = Cmd.getArguments(); auto CI = std::make_unique(); - if (!CompilerInvocation::CreateFromArgs(*CI, - const_cast(CCArgs.data()), - const_cast(CCArgs.data()) + - CCArgs.size(), - *Diags)) + if (!CompilerInvocation::CreateFromArgs( + *CI, const_cast(CCArgs.data()), + const_cast(CCArgs.data()) + CCArgs.size(), *Diags) && + !ShouldRecoverOnErorrs) return nullptr; return CI; }