Author: ibiryukov Date: Fri Feb 9 05:51:57 2018 New Revision: 324732 URL: http://llvm.org/viewvc/llvm-project?rev=324732&view=rev Log: [clangd] Fix crash when CompilerInvocation can't be created.
Summary: This can happen if the CompileCommand provided by compilation database is malformed. Reviewers: hokein, ioeric, sammccall Reviewed By: sammccall Subscribers: klimek, jkorous-apple, cfe-commits Differential Revision: https://reviews.llvm.org/D43122 Modified: clang-tools-extra/trunk/clangd/ClangdUnit.cpp clang-tools-extra/trunk/clangd/CodeComplete.cpp clang-tools-extra/trunk/unittests/clangd/ClangdTests.cpp clang-tools-extra/trunk/unittests/clangd/Matchers.h Modified: clang-tools-extra/trunk/clangd/ClangdUnit.cpp URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clangd/ClangdUnit.cpp?rev=324732&r1=324731&r2=324732&view=diff ============================================================================== --- clang-tools-extra/trunk/clangd/ClangdUnit.cpp (original) +++ clang-tools-extra/trunk/clangd/ClangdUnit.cpp Fri Feb 9 05:51:57 2018 @@ -405,10 +405,15 @@ CppFile::rebuild(ParseInputs &&Inputs) { &IgnoreDiagnostics, false); CI = createInvocationFromCommandLine(ArgStrs, CommandLineDiagsEngine, Inputs.FS); + if (!CI) { + log("Could not build CompilerInvocation for file " + FileName); + AST = llvm::None; + Preamble = nullptr; + return llvm::None; + } // createInvocationFromCommandLine sets DisableFree. CI->getFrontendOpts().DisableFree = false; } - assert(CI && "Couldn't create CompilerInvocation"); std::unique_ptr<llvm::MemoryBuffer> ContentsBuffer = llvm::MemoryBuffer::getMemBufferCopy(Inputs.Contents, FileName); Modified: clang-tools-extra/trunk/clangd/CodeComplete.cpp URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clangd/CodeComplete.cpp?rev=324732&r1=324731&r2=324732&view=diff ============================================================================== --- clang-tools-extra/trunk/clangd/CodeComplete.cpp (original) +++ clang-tools-extra/trunk/clangd/CodeComplete.cpp Fri Feb 9 05:51:57 2018 @@ -650,7 +650,10 @@ bool semaCodeComplete(std::unique_ptr<Co CompilerInstance::createDiagnostics(new DiagnosticOptions, &DummyDiagsConsumer, false), Input.VFS); - assert(CI && "Couldn't create CompilerInvocation"); + if (!CI) { + log("Couldn't create CompilerInvocation");; + return false; + } CI->getFrontendOpts().DisableFree = false; std::unique_ptr<llvm::MemoryBuffer> ContentsBuffer = Modified: clang-tools-extra/trunk/unittests/clangd/ClangdTests.cpp URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/unittests/clangd/ClangdTests.cpp?rev=324732&r1=324731&r2=324732&view=diff ============================================================================== --- clang-tools-extra/trunk/unittests/clangd/ClangdTests.cpp (original) +++ clang-tools-extra/trunk/unittests/clangd/ClangdTests.cpp Fri Feb 9 05:51:57 2018 @@ -9,6 +9,7 @@ #include "ClangdLSPServer.h" #include "ClangdServer.h" +#include "Matchers.h" #include "TestFS.h" #include "clang/Config/config.h" #include "llvm/ADT/SmallVector.h" @@ -453,6 +454,40 @@ struct Something { EXPECT_THAT(Server.getUsedBytesPerFile(), IsEmpty()); } +TEST_F(ClangdVFSTest, InvalidCompileCommand) { + MockFSProvider FS; + ErrorCheckingDiagConsumer DiagConsumer; + MockCompilationDatabase CDB; + + ClangdServer Server(CDB, DiagConsumer, FS, + /*AsyncThreadsCount=*/0, + /*StorePreamblesInMemory=*/true); + + auto FooCpp = getVirtualTestFilePath("foo.cpp"); + // clang cannot create CompilerInvocation if we pass two files in the + // CompileCommand. We pass the file in ExtraFlags once and CDB adds another + // one in getCompileCommand(). + CDB.ExtraClangFlags.push_back(FooCpp.str()); + + // Clang can't parse command args in that case, but we shouldn't crash. + Server.addDocument(FooCpp, "int main() {}"); + + EXPECT_EQ(Server.dumpAST(FooCpp), "<no-ast>"); + EXPECT_ERROR(Server.findDefinitions(FooCpp, Position{0, 0})); + EXPECT_ERROR(Server.findDocumentHighlights(FooCpp, Position{0, 0})); + EXPECT_ERROR(Server.rename(FooCpp, Position{0, 0}, "new_name")); + // FIXME: codeComplete and signatureHelp should also return errors when they + // can't parse the file. + EXPECT_THAT( + Server.codeComplete(FooCpp, Position{0, 0}, clangd::CodeCompleteOptions()) + .get() + .Value.items, + IsEmpty()); + EXPECT_THAT( + Server.signatureHelp(FooCpp, Position{0, 0}).get().Value.signatures, + IsEmpty()); +} + class ClangdThreadingTest : public ClangdVFSTest {}; TEST_F(ClangdThreadingTest, StressTest) { Modified: clang-tools-extra/trunk/unittests/clangd/Matchers.h URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/unittests/clangd/Matchers.h?rev=324732&r1=324731&r2=324732&view=diff ============================================================================== --- clang-tools-extra/trunk/unittests/clangd/Matchers.h (original) +++ clang-tools-extra/trunk/unittests/clangd/Matchers.h Fri Feb 9 05:51:57 2018 @@ -108,6 +108,28 @@ PolySubsequenceMatcher<Args...> HasSubse return PolySubsequenceMatcher<Args...>(std::forward<Args>(M)...); } +// EXPECT_ERROR seems like a pretty generic name, make sure it's not defined +// already. +#ifdef EXPECT_ERROR +#error "Refusing to redefine EXPECT_ERROR" +#endif + +// Consumes llvm::Expected<T>, checks it contains an error and marks it as +// handled. +#define EXPECT_ERROR(expectedValue) \ + do { \ + auto &&ComputedValue = (expectedValue); \ + if (ComputedValue) { \ + ADD_FAILURE() << "expected an error from " << #expectedValue \ + << " but got " \ + << ::testing::PrintToString(*ComputedValue); \ + break; \ + } \ + handleAllErrors(ComputedValue.takeError(), \ + [](const llvm::ErrorInfoBase &) {}); \ + \ + } while (false) + } // namespace clangd } // namespace clang #endif _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits