[clangd] Avoid "expected one compiler job" by picking the first eligible job.

This happens in createInvocationWithCommandLine but only clangd currently passes
ShouldRecoverOnErorrs (sic).

One cause of this (with correct command) is several -arch arguments for mac
multi-arch support.

Fixes https://github.com/clangd/clangd/issues/827

Differential Revision: https://reviews.llvm.org/D107632
This commit is contained in:
Sam McCall 2021-08-06 12:07:13 +02:00
parent 6b28835b37
commit 2ff7ca98a9
4 changed files with 49 additions and 11 deletions

View file

@ -624,10 +624,8 @@ TEST(ClangdServerTest, InvalidCompileCommand) {
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 @@ TEST(ClangdServerTest, FallbackWhenPreambleIsNotReady) {
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));

View file

@ -79,22 +79,24 @@ std::unique_ptr<CompilerInvocation> clang::createInvocationFromCommandLine(
}
}
}
if (Jobs.size() == 0 || !isa<driver::Command>(*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<driver::Command>(*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<CompilerInvocation>();

View file

@ -13,6 +13,7 @@ add_clang_unittest(FrontendTests
PCHPreambleTest.cpp
OutputStreamTest.cpp
TextDiagnosticTest.cpp
UtilsTest.cpp
)
clang_target_link_libraries(FrontendTests
PRIVATE

View file

@ -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<const char *> Args = {"clang", "--target=macho", "-arch", "i386",
"-arch", "x86_64", "foo.cpp"};
clang::IgnoringDiagConsumer D;
llvm::IntrusiveRefCntPtr<DiagnosticsEngine> CommandLineDiagsEngine =
clang::CompilerInstance::createDiagnostics(new DiagnosticOptions, &D,
false);
std::unique_ptr<CompilerInvocation> CI = createInvocationFromCommandLine(
Args, CommandLineDiagsEngine, new llvm::vfs::InMemoryFileSystem(),
/*ShouldRecoverOnErrors=*/true);
ASSERT_TRUE(CI);
EXPECT_THAT(CI->TargetOpts->Triple, testing::StartsWith("i386-"));
}
} // namespace
} // namespace clang