Skip to content

Commit

Permalink
[Tooling] Handle compilation databases containing commands with doubl…
Browse files Browse the repository at this point in the history
…e dashes

As of CMake commit https://gitlab.kitware.com/cmake/cmake/-/commit/d993ebd4,
which first appeared in CMake 3.19.x series, in the compile commands for
clang-cl, CMake puts `--` before the input file. When operating on such a
database, the `InterpolatingCompilationDatabase` - specifically, the
`TransferableCommand` constructor - does not recognize that pattern and so, does
not strip the input, or the double dash when 'transferring' the compile command.
This results in a incorrect compile command - with the double dash and old input
file left in, and the language options and new input file appended after them,
where they're all treated as inputs, including the language version option.

Test files for some tests have names similar enough to be matched to commands
from the database, e.g.:

`.../path-mappings.test.tmp/server/bar.cpp`

can be matched to:

`.../Driver/ToolChains/BareMetal.cpp`

etc. When that happens, the tool being tested tries to use the matched, and
incorrectly 'transferred' compile command, and fails, reporting errors similar
to:

`error: no such file or directory: '/std:c++14'; did you mean '/std:c++14'? [clang-diagnostic-error]`

This happens in at least 4 tests:

  Clang Tools :: clang-tidy/checkers/performance-trivially-destructible.cpp
  Clangd :: check-fail.test
  Clangd :: check.test
  Clangd :: path-mappings.test

The fix for `TransferableCommand` removes the `--` and everything after it when
determining the arguments that apply to the new file. `--` is inserted in the
'transferred' command if the new file name starts with `-` and when operating in
clang-cl mode, also `/`. Additionally, other places in the code known to do
argument adjustment without accounting for the `--` and causing the tests to
fail are fixed as well.

Differential Revision: https://reviews.llvm.org/D98824
  • Loading branch information
Janusz Nykiel authored and aganea committed Mar 24, 2021
1 parent e29bb07 commit e030ce3
Show file tree
Hide file tree
Showing 4 changed files with 42 additions and 10 deletions.
5 changes: 3 additions & 2 deletions clang/lib/Tooling/ArgumentsAdjusters.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,8 @@ ArgumentsAdjuster getClangSyntaxOnlyAdjuster() {
HasSyntaxOnly = true;
}
if (!HasSyntaxOnly)
AdjustedArgs.push_back("-fsyntax-only");
AdjustedArgs =
getInsertArgumentAdjuster("-fsyntax-only")(AdjustedArgs, "");
return AdjustedArgs;
};
}
Expand Down Expand Up @@ -137,7 +138,7 @@ ArgumentsAdjuster getInsertArgumentAdjuster(const CommandLineArguments &Extra,

CommandLineArguments::iterator I;
if (Pos == ArgumentInsertPosition::END) {
I = Return.end();
I = std::find(Return.begin(), Return.end(), "--");
} else {
I = Return.begin();
++I; // To leave the program name in place
Expand Down
6 changes: 6 additions & 0 deletions clang/lib/Tooling/InterpolatingCompilationDatabase.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,10 @@ struct TransferableCommand {
Opt.matches(OPT__SLASH_Fo))))
continue;

// ...including when the inputs are passed after --.
if (Opt.matches(OPT__DASH_DASH))
break;

// Strip -x, but record the overridden language.
if (const auto GivenType = tryParseTypeArg(*Arg)) {
Type = *GivenType;
Expand Down Expand Up @@ -235,6 +239,8 @@ struct TransferableCommand {
llvm::Twine(ClangCLMode ? "/std:" : "-std=") +
LangStandard::getLangStandardForKind(Std).getName()).str());
}
if (Filename.startswith("-") || (ClangCLMode && Filename.startswith("/")))
Result.CommandLine.push_back("--");
Result.CommandLine.push_back(std::string(Filename));
return Result;
}
Expand Down
5 changes: 3 additions & 2 deletions clang/lib/Tooling/Tooling.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -440,8 +440,9 @@ static void injectResourceDir(CommandLineArguments &Args, const char *Argv0,
return;

// If there's no override in place add our resource dir.
Args.push_back("-resource-dir=" +
CompilerInvocation::GetResourcesPath(Argv0, MainAddr));
Args = getInsertArgumentAdjuster(
("-resource-dir=" + CompilerInvocation::GetResourcesPath(Argv0, MainAddr))
.c_str())(Args, "");
}

int ClangTool::run(ToolAction *Action) {
Expand Down
36 changes: 30 additions & 6 deletions clang/unittests/Tooling/CompilationDatabaseTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -725,14 +725,14 @@ class InterpolateTest : public MemDBTest {
protected:
// Look up the command from a relative path, and return it in string form.
// The input file is not included in the returned command.
std::string getCommand(llvm::StringRef F) {
std::string getCommand(llvm::StringRef F, bool MakeNative = true) {
auto Results =
inferMissingCompileCommands(std::make_unique<MemCDB>(Entries))
->getCompileCommands(path(F));
->getCompileCommands(MakeNative ? path(F) : F);
if (Results.empty())
return "none";
// drop the input file argument, so tests don't have to deal with path().
EXPECT_EQ(Results[0].CommandLine.back(), path(F))
EXPECT_EQ(Results[0].CommandLine.back(), MakeNative ? path(F) : F)
<< "Last arg should be the file";
Results[0].CommandLine.pop_back();
return llvm::join(Results[0].CommandLine, " ");
Expand Down Expand Up @@ -812,6 +812,28 @@ TEST_F(InterpolateTest, Strip) {
EXPECT_EQ(getCommand("dir/bar.cpp"), "clang -D dir/foo.cpp -Wall");
}

TEST_F(InterpolateTest, StripDoubleDash) {
add("dir/foo.cpp", "-o foo.o -std=c++14 -Wall -- dir/foo.cpp");
// input file and output option are removed
// -Wall flag isn't
// -std option gets re-added as the last argument before the input file
// -- is removed as it's not necessary - the new input file doesn't start with
// a dash
EXPECT_EQ(getCommand("dir/bar.cpp"), "clang -D dir/foo.cpp -Wall -std=c++14");
}

TEST_F(InterpolateTest, InsertDoubleDash) {
add("dir/foo.cpp", "-o foo.o -std=c++14 -Wall");
EXPECT_EQ(getCommand("-dir/bar.cpp", false),
"clang -D dir/foo.cpp -Wall -std=c++14 --");
}

TEST_F(InterpolateTest, InsertDoubleDashForClangCL) {
add("dir/foo.cpp", "clang-cl", "/std:c++14 /W4");
EXPECT_EQ(getCommand("/dir/bar.cpp", false),
"clang-cl -D dir/foo.cpp /W4 /std:c++14 --");
}

TEST_F(InterpolateTest, Case) {
add("FOO/BAR/BAZ/SHOUT.cc");
add("foo/bar/baz/quiet.cc");
Expand All @@ -831,16 +853,18 @@ TEST_F(InterpolateTest, ClangCL) {
add("foo.cpp", "clang-cl", "/W4");

// Language flags should be added with CL syntax.
EXPECT_EQ(getCommand("foo.h"), "clang-cl -D foo.cpp /W4 /TP");
EXPECT_EQ(getCommand("foo.h", false), "clang-cl -D foo.cpp /W4 /TP");
}

TEST_F(InterpolateTest, DriverModes) {
add("foo.cpp", "clang-cl", "--driver-mode=gcc");
add("bar.cpp", "clang", "--driver-mode=cl");

// --driver-mode overrides should be respected.
EXPECT_EQ(getCommand("foo.h"), "clang-cl -D foo.cpp --driver-mode=gcc -x c++-header");
EXPECT_EQ(getCommand("bar.h"), "clang -D bar.cpp --driver-mode=cl /TP");
EXPECT_EQ(getCommand("foo.h"),
"clang-cl -D foo.cpp --driver-mode=gcc -x c++-header");
EXPECT_EQ(getCommand("bar.h", false),
"clang -D bar.cpp --driver-mode=cl /TP");
}

TEST(TransferCompileCommandTest, Smoke) {
Expand Down

0 comments on commit e030ce3

Please sign in to comment.