diff --git a/clang-tools-extra/clangd/Compiler.cpp b/clang-tools-extra/clangd/Compiler.cpp --- a/clang-tools-extra/clangd/Compiler.cpp +++ b/clang-tools-extra/clangd/Compiler.cpp @@ -39,6 +39,7 @@ void IgnoreDiagnostics::HandleDiagnostic(DiagnosticsEngine::Level DiagLevel, const clang::Diagnostic &Info) { + DiagnosticConsumer::HandleDiagnostic(DiagLevel, Info); IgnoreDiagnostics::log(DiagLevel, Info); } diff --git a/clang-tools-extra/clangd/unittests/ClangdTests.cpp b/clang-tools-extra/clangd/unittests/ClangdTests.cpp --- a/clang-tools-extra/clangd/unittests/ClangdTests.cpp +++ b/clang-tools-extra/clangd/unittests/ClangdTests.cpp @@ -624,10 +624,8 @@ ClangdServer Server(CDB, FS, ClangdServer::optsForTest(), &DiagConsumer); auto FooCpp = testPath("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); + // clang cannot create CompilerInvocation in this case. + CDB.ExtraClangFlags.push_back("-###"); // Clang can't parse command args in that case, but we shouldn't crash. runAddDocument(Server, FooCpp, "int main() {}"); @@ -1080,7 +1078,7 @@ Opts.RunParser = CodeCompleteOptions::ParseIfReady; // This will make compile command broken and preamble absent. - CDB.ExtraClangFlags = {"yolo.cc"}; + CDB.ExtraClangFlags = {"-###"}; Server.addDocument(FooCpp, Code.code()); ASSERT_TRUE(Server.blockUntilIdleForTest()); auto Res = cantFail(runCodeComplete(Server, FooCpp, Code.point(), Opts)); diff --git a/clang/lib/Frontend/CreateInvocationFromCommandLine.cpp b/clang/lib/Frontend/CreateInvocationFromCommandLine.cpp --- a/clang/lib/Frontend/CreateInvocationFromCommandLine.cpp +++ b/clang/lib/Frontend/CreateInvocationFromCommandLine.cpp @@ -79,22 +79,24 @@ } } } - if (Jobs.size() == 0 || !isa(*Jobs.begin()) || - (Jobs.size() > 1 && !OffloadCompilation)) { + + bool PickFirstOfMany = OffloadCompilation || ShouldRecoverOnErorrs; + if (Jobs.size() == 0 || (Jobs.size() > 1 && !PickFirstOfMany)) { SmallString<256> Msg; llvm::raw_svector_ostream OS(Msg); Jobs.Print(OS, "; ", true); Diags->Report(diag::err_fe_expected_compiler_job) << OS.str(); return nullptr; } - - const driver::Command &Cmd = cast(*Jobs.begin()); - if (StringRef(Cmd.getCreator().getName()) != "clang") { + auto Cmd = llvm::find_if(Jobs, [](const driver::Command &Cmd) { + return StringRef(Cmd.getCreator().getName()) == "clang"; + }); + if (Cmd == Jobs.end()) { Diags->Report(diag::err_fe_expected_clang_command); return nullptr; } - const ArgStringList &CCArgs = Cmd.getArguments(); + const ArgStringList &CCArgs = Cmd->getArguments(); if (CC1Args) *CC1Args = {CCArgs.begin(), CCArgs.end()}; auto CI = std::make_unique(); diff --git a/clang/unittests/Frontend/CMakeLists.txt b/clang/unittests/Frontend/CMakeLists.txt --- a/clang/unittests/Frontend/CMakeLists.txt +++ b/clang/unittests/Frontend/CMakeLists.txt @@ -13,6 +13,7 @@ PCHPreambleTest.cpp OutputStreamTest.cpp TextDiagnosticTest.cpp + UtilsTest.cpp ) clang_target_link_libraries(FrontendTests PRIVATE diff --git a/clang/unittests/Frontend/UtilsTest.cpp b/clang/unittests/Frontend/UtilsTest.cpp new file mode 100644 --- /dev/null +++ b/clang/unittests/Frontend/UtilsTest.cpp @@ -0,0 +1,37 @@ +//===- unittests/Frontend/UtilsTest.cpp -----------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#include "clang/Frontend/Utils.h" +#include "clang/Basic/Diagnostic.h" +#include "clang/Basic/TargetOptions.h" +#include "clang/Frontend/CompilerInstance.h" +#include "clang/Frontend/CompilerInvocation.h" +#include "llvm/Support/VirtualFileSystem.h" +#include "gmock/gmock.h" +#include "gtest/gtest.h" + +namespace clang { +namespace { + +TEST(BuildCompilerInvocationTest, RecoverMultipleJobs) { + // This generates multiple jobs and we recover by using the first. + std::vector Args = {"clang", "--target=macho", "-arch", "i386", + "-arch", "x86_64", "foo.cpp"}; + clang::IgnoringDiagConsumer D; + llvm::IntrusiveRefCntPtr CommandLineDiagsEngine = + clang::CompilerInstance::createDiagnostics(new DiagnosticOptions, &D, + false); + std::unique_ptr CI = createInvocationFromCommandLine( + Args, CommandLineDiagsEngine, new llvm::vfs::InMemoryFileSystem(), + /*ShouldRecoverOnErrors=*/true); + ASSERT_TRUE(CI); + EXPECT_THAT(CI->TargetOpts->Triple, testing::StartsWith("i386-")); +} + +} // namespace +} // namespace clang