diff --git a/clang-tools-extra/clangd/CompileCommands.h b/clang-tools-extra/clangd/CompileCommands.h index c9f2d668c36545..84c4c2a26a872c 100644 --- a/clang-tools-extra/clangd/CompileCommands.h +++ b/clang-tools-extra/clangd/CompileCommands.h @@ -59,6 +59,12 @@ struct CommandMangler { // The table-building strategy may not make sense outside clangd. class ArgStripper { public: + ArgStripper() = default; + ArgStripper(ArgStripper &&) = default; + ArgStripper(const ArgStripper &) = delete; + ArgStripper &operator=(ArgStripper &&) = default; + ArgStripper &operator=(const ArgStripper &) = delete; + // Adds the arg to the set which should be removed. // // Recognized clang flags are stripped semantically. When "-I" is stripped: diff --git a/clang-tools-extra/clangd/ConfigCompile.cpp b/clang-tools-extra/clangd/ConfigCompile.cpp index 04c0df88bbf762..568e029b5c0a1b 100644 --- a/clang-tools-extra/clangd/ConfigCompile.cpp +++ b/clang-tools-extra/clangd/ConfigCompile.cpp @@ -23,6 +23,7 @@ // //===----------------------------------------------------------------------===// +#include "CompileCommands.h" #include "Config.h" #include "ConfigFragment.h" #include "support/Logger.h" @@ -122,6 +123,19 @@ struct FragmentCompiler { } void compile(Fragment::CompileFlagsBlock &&F) { + if (!F.Remove.empty()) { + auto Remove = std::make_shared(); + for (auto &A : F.Remove) + Remove->strip(*A); + Out.Apply.push_back([Remove(std::shared_ptr( + std::move(Remove)))](Config &C) { + C.CompileFlags.Edits.push_back( + [Remove](std::vector &Args) { + Remove->process(Args); + }); + }); + } + if (!F.Add.empty()) { std::vector Add; for (auto &A : F.Add) diff --git a/clang-tools-extra/clangd/ConfigFragment.h b/clang-tools-extra/clangd/ConfigFragment.h index 42f9ec2edc7243..5cc8749c5efa11 100644 --- a/clang-tools-extra/clangd/ConfigFragment.h +++ b/clang-tools-extra/clangd/ConfigFragment.h @@ -117,9 +117,39 @@ struct Fragment { }; IfBlock If; + /// Conditions in the CompileFlags block affect how a file is parsed. + /// + /// clangd emulates how clang would interpret a file. + /// By default, it behaves roughly like `clang $FILENAME`, but real projects + /// usually require setting the include path (with the `-I` flag), defining + /// preprocessor symbols, configuring warnings etc. + /// Often, a compilation database specifies these compile commands. clangd + /// searches for compile_commands.json in parents of the source file. + /// + /// This section modifies how the compile command is constructed. struct CompileFlagsBlock { + /// List of flags to append to the compile command. std::vector> Add; - } CompileFlags; + /// List of flags to remove from the compile command. + /// + /// - If the value is a recognized clang flag (like "-I") then it will be + /// removed along with any arguments. Synonyms like --include-dir= will + /// also be removed. + /// - Otherwise, if the value ends in * (like "-DFOO=*") then any argument + /// with the prefix will be removed. + /// - Otherwise any argument exactly matching the value is removed. + /// + /// In all cases, -Xclang is also removed where needed. + /// + /// Example: + /// Command: clang++ --include-directory=/usr/include -DFOO=42 foo.cc + /// Remove: [-I, -DFOO=*] + /// Result: clang++ foo.cc + /// + /// Flags added by the same CompileFlags entry will not be removed. + std::vector> Remove; + }; + CompileFlagsBlock CompileFlags; }; } // namespace config diff --git a/clang-tools-extra/clangd/ConfigYAML.cpp b/clang-tools-extra/clangd/ConfigYAML.cpp index ef6003b0243922..7e86e372124831 100644 --- a/clang-tools-extra/clangd/ConfigYAML.cpp +++ b/clang-tools-extra/clangd/ConfigYAML.cpp @@ -63,6 +63,10 @@ class Parser { if (auto Values = scalarValues(N)) F.Add = std::move(*Values); }); + Dict.handle("Remove", [&](Node &N) { + if (auto Values = scalarValues(N)) + F.Remove = std::move(*Values); + }); Dict.parse(N); } diff --git a/clang-tools-extra/clangd/unittests/ConfigCompileTests.cpp b/clang-tools-extra/clangd/unittests/ConfigCompileTests.cpp index 825d6878727d9c..033734789bedd6 100644 --- a/clang-tools-extra/clangd/unittests/ConfigCompileTests.cpp +++ b/clang-tools-extra/clangd/unittests/ConfigCompileTests.cpp @@ -91,10 +91,12 @@ TEST_F(ConfigCompileTests, Condition) { TEST_F(ConfigCompileTests, CompileCommands) { Frag.CompileFlags.Add.emplace_back("-foo"); - std::vector Argv = {"clang", "a.cc"}; + Frag.CompileFlags.Remove.emplace_back("--include-directory="); + std::vector Argv = {"clang", "-I", "bar/", "a.cc"}; EXPECT_TRUE(compileAndApply()); - EXPECT_THAT(Conf.CompileFlags.Edits, SizeIs(1)); - Conf.CompileFlags.Edits.front()(Argv); + EXPECT_THAT(Conf.CompileFlags.Edits, SizeIs(2)); + for (auto &Edit : Conf.CompileFlags.Edits) + Edit(Argv); EXPECT_THAT(Argv, ElementsAre("clang", "a.cc", "-foo")); }