diff --git a/clang-tools-extra/clangd/CodeComplete.cpp b/clang-tools-extra/clangd/CodeComplete.cpp --- a/clang-tools-extra/clangd/CodeComplete.cpp +++ b/clang-tools-extra/clangd/CodeComplete.cpp @@ -21,6 +21,7 @@ #include "AST.h" #include "CodeCompletionStrings.h" #include "Compiler.h" +#include "Config.h" #include "ExpectedTypes.h" #include "Feature.h" #include "FileDistance.h" @@ -1554,7 +1555,8 @@ Inserter.emplace( SemaCCInput.FileName, SemaCCInput.ParseInput.Contents, Style, SemaCCInput.ParseInput.CompileCommand.Directory, - &Recorder->CCSema->getPreprocessor().getHeaderSearchInfo()); + &Recorder->CCSema->getPreprocessor().getHeaderSearchInfo(), + Config::current().Style.AlwaysBracketedInclude); for (const auto &Inc : Includes.MainFileIncludes) Inserter->addExisting(Inc); @@ -1637,7 +1639,8 @@ auto Style = getFormatStyleForFile(FileName, Content, TFS); // This will only insert verbatim headers. Inserter.emplace(FileName, Content, Style, - /*BuildDir=*/"", /*HeaderSearchInfo=*/nullptr); + /*BuildDir=*/"", /*HeaderSearchInfo=*/nullptr, + Config::current().Style.AlwaysBracketedInclude); auto Identifiers = collectIdentifiers(Content, Style); std::vector IdentifierResults; diff --git a/clang-tools-extra/clangd/Config.h b/clang-tools-extra/clangd/Config.h --- a/clang-tools-extra/clangd/Config.h +++ b/clang-tools-extra/clangd/Config.h @@ -124,6 +124,9 @@ // declarations, always spell out the whole name (with or without leading // ::). All nested namespaces are affected as well. std::vector FullyQualifiedNamespaces; + + // Inserted includes should always use <> over "", regardless of heuristics + bool AlwaysBracketedInclude = false; } Style; /// Configures code completion feature. diff --git a/clang-tools-extra/clangd/ConfigCompile.cpp b/clang-tools-extra/clangd/ConfigCompile.cpp --- a/clang-tools-extra/clangd/ConfigCompile.cpp +++ b/clang-tools-extra/clangd/ConfigCompile.cpp @@ -488,6 +488,12 @@ FullyQualifiedNamespaces.begin(), FullyQualifiedNamespaces.end()); }); } + if (F.AlwaysBracketedInclude) { + if (auto Val = F.AlwaysBracketedInclude) + Out.Apply.push_back([Val](const Params &, Config &C) { + C.Style.AlwaysBracketedInclude = **Val; + }); + } } void appendTidyCheckSpec(std::string &CurSpec, diff --git a/clang-tools-extra/clangd/ConfigFragment.h b/clang-tools-extra/clangd/ConfigFragment.h --- a/clang-tools-extra/clangd/ConfigFragment.h +++ b/clang-tools-extra/clangd/ConfigFragment.h @@ -293,6 +293,9 @@ // ::). All nested namespaces are affected as well. // Affects availability of the AddUsing tweak. std::vector> FullyQualifiedNamespaces; + + // Inserted includes should always use <> over "", regardless of heuristics + std::optional> AlwaysBracketedInclude; }; StyleBlock Style; diff --git a/clang-tools-extra/clangd/ConfigYAML.cpp b/clang-tools-extra/clangd/ConfigYAML.cpp --- a/clang-tools-extra/clangd/ConfigYAML.cpp +++ b/clang-tools-extra/clangd/ConfigYAML.cpp @@ -116,6 +116,10 @@ if (auto Values = scalarValues(N)) F.FullyQualifiedNamespaces = std::move(*Values); }); + Dict.handle("AlwaysBracketedInclude", [&](Node &N) { + if (auto AlwaysBracketedInclude = boolValue(N, "AlwaysBracketedInclude")) + F.AlwaysBracketedInclude = *AlwaysBracketedInclude; + }); Dict.parse(N); } diff --git a/clang-tools-extra/clangd/Headers.h b/clang-tools-extra/clangd/Headers.h --- a/clang-tools-extra/clangd/Headers.h +++ b/clang-tools-extra/clangd/Headers.h @@ -219,10 +219,11 @@ // include path of non-verbatim header will not be shortened. IncludeInserter(StringRef FileName, StringRef Code, const format::FormatStyle &Style, StringRef BuildDir, - HeaderSearch *HeaderSearchInfo) + HeaderSearch *HeaderSearchInfo, bool AlwaysBracketedInclude) : FileName(FileName), Code(Code), BuildDir(BuildDir), HeaderSearchInfo(HeaderSearchInfo), - Inserter(FileName, Code, Style.IncludeStyle) {} + Inserter(FileName, Code, Style.IncludeStyle), + AlwaysBracketedInclude(AlwaysBracketedInclude) {} void addExisting(const Inclusion &Inc); @@ -266,6 +267,7 @@ HeaderSearch *HeaderSearchInfo = nullptr; llvm::StringSet<> IncludedHeaders; // Both written and resolved. tooling::HeaderIncludes Inserter; // Computers insertion replacement. + bool AlwaysBracketedInclude = false; }; } // namespace clangd diff --git a/clang-tools-extra/clangd/Headers.cpp b/clang-tools-extra/clangd/Headers.cpp --- a/clang-tools-extra/clangd/Headers.cpp +++ b/clang-tools-extra/clangd/Headers.cpp @@ -349,7 +349,7 @@ // FIXME: should we allow (some limited number of) "../header.h"? if (llvm::sys::path::is_absolute(Suggested)) return std::nullopt; - if (IsSystem) + if (AlwaysBracketedInclude || IsSystem) Suggested = "<" + Suggested + ">"; else Suggested = "\"" + Suggested + "\""; diff --git a/clang-tools-extra/clangd/ParsedAST.cpp b/clang-tools-extra/clangd/ParsedAST.cpp --- a/clang-tools-extra/clangd/ParsedAST.cpp +++ b/clang-tools-extra/clangd/ParsedAST.cpp @@ -565,7 +565,8 @@ getFormatStyleForFile(Filename, Inputs.Contents, *Inputs.TFS); auto Inserter = std::make_shared( Filename, Inputs.Contents, Style, BuildDir.get(), - &Clang->getPreprocessor().getHeaderSearchInfo()); + &Clang->getPreprocessor().getHeaderSearchInfo(), + Cfg.Style.AlwaysBracketedInclude); ArrayRef MainFileIncludes; if (Preamble) { MainFileIncludes = Preamble->Includes.MainFileIncludes; diff --git a/clang-tools-extra/clangd/unittests/ConfigCompileTests.cpp b/clang-tools-extra/clangd/unittests/ConfigCompileTests.cpp --- a/clang-tools-extra/clangd/unittests/ConfigCompileTests.cpp +++ b/clang-tools-extra/clangd/unittests/ConfigCompileTests.cpp @@ -542,6 +542,19 @@ Frag.Style.FullyQualifiedNamespaces.push_back(std::string("bar")); EXPECT_TRUE(compileAndApply()); EXPECT_THAT(Conf.Style.FullyQualifiedNamespaces, ElementsAre("foo", "bar")); + + Frag = {}; + EXPECT_TRUE(compileAndApply()); + // Off by default. + EXPECT_EQ(Conf.Style.AlwaysBracketedInclude, false); + + Frag.Style.AlwaysBracketedInclude.emplace(true); + EXPECT_TRUE(compileAndApply()); + EXPECT_EQ(Conf.Style.AlwaysBracketedInclude, true); + + Frag.Style.AlwaysBracketedInclude.emplace(false); + EXPECT_TRUE(compileAndApply()); + EXPECT_EQ(Conf.Style.AlwaysBracketedInclude, false); } TEST_F(ConfigCompileTests, AllowDiagsFromStalePreamble) { diff --git a/clang-tools-extra/clangd/unittests/ConfigYAMLTests.cpp b/clang-tools-extra/clangd/unittests/ConfigYAMLTests.cpp --- a/clang-tools-extra/clangd/unittests/ConfigYAMLTests.cpp +++ b/clang-tools-extra/clangd/unittests/ConfigYAMLTests.cpp @@ -265,13 +265,16 @@ CapturedDiags Diags; Annotations YAML(R"yaml( Style: - FullyQualifiedNamespaces: [foo, bar])yaml"); + FullyQualifiedNamespaces: [foo, bar] + AlwaysBracketedInclude: Yes)yaml"); auto Results = Fragment::parseYAML(YAML.code(), "config.yaml", Diags.callback()); ASSERT_THAT(Diags.Diagnostics, IsEmpty()); ASSERT_EQ(Results.size(), 1u); EXPECT_THAT(Results[0].Style.FullyQualifiedNamespaces, ElementsAre(val("foo"), val("bar"))); + EXPECT_THAT(Results[0].Style.AlwaysBracketedInclude, + llvm::ValueIs(val(true))); } TEST(ParseYAML, DiagnosticsMode) { diff --git a/clang-tools-extra/clangd/unittests/HeadersTests.cpp b/clang-tools-extra/clangd/unittests/HeadersTests.cpp --- a/clang-tools-extra/clangd/unittests/HeadersTests.cpp +++ b/clang-tools-extra/clangd/unittests/HeadersTests.cpp @@ -108,7 +108,8 @@ IncludeInserter Inserter(MainFile, /*Code=*/"", format::getLLVMStyle(), CDB.getCompileCommand(MainFile)->Directory, - &Clang->getPreprocessor().getHeaderSearchInfo()); + &Clang->getPreprocessor().getHeaderSearchInfo(), + AlwaysBracketedInclude); for (const auto &Inc : Inclusions) Inserter.addExisting(Inc); auto Inserted = ToHeaderFile(Preferred); @@ -128,7 +129,8 @@ IncludeInserter Inserter(MainFile, /*Code=*/"", format::getLLVMStyle(), CDB.getCompileCommand(MainFile)->Directory, - &Clang->getPreprocessor().getHeaderSearchInfo()); + &Clang->getPreprocessor().getHeaderSearchInfo(), + AlwaysBracketedInclude); auto Edit = Inserter.insert(VerbatimHeader, Directive); Action.EndSourceFile(); return Edit; @@ -139,6 +141,7 @@ std::string MainFile = testPath("main.cpp"); std::string Subdir = testPath("sub"); std::string SearchDirArg = (llvm::Twine("-I") + Subdir).str(); + bool AlwaysBracketedInclude = false; IgnoringDiagConsumer IgnoreDiags; std::unique_ptr Clang; }; @@ -309,6 +312,9 @@ std::string Path = testPath("sub/bar.h"); FS.Files[Path] = ""; EXPECT_EQ(calculate(Path), "\"bar.h\""); + + AlwaysBracketedInclude = true; + EXPECT_EQ(calculate(Path), ""); } TEST_F(HeadersTest, DoNotInsertIfInSameFile) { @@ -331,6 +337,17 @@ EXPECT_EQ(calculate(BarHeader), "\"sub/bar.h\""); } +TEST_F(HeadersTest, ShortenIncludesInSearchPathBracketed) { + AlwaysBracketedInclude = true; + std::string BarHeader = testPath("sub/bar.h"); + EXPECT_EQ(calculate(BarHeader), ""); + + SearchDirArg = (llvm::Twine("-I") + Subdir + "/..").str(); + CDB.ExtraClangFlags = {SearchDirArg.c_str()}; + BarHeader = testPath("sub/bar.h"); + EXPECT_EQ(calculate(BarHeader), ""); +} + TEST_F(HeadersTest, ShortenedIncludeNotInSearchPath) { std::string BarHeader = llvm::sys::path::convert_to_slash(testPath("sub-2/bar.h")); @@ -343,6 +360,10 @@ std::string BazHeader = testPath("sub/baz.h"); EXPECT_EQ(calculate(BarHeader, BazHeader), "\"baz.h\""); + + AlwaysBracketedInclude = true; + std::string BiffHeader = testPath("sub/biff.h"); + EXPECT_EQ(calculate(BarHeader, BiffHeader), ""); } TEST_F(HeadersTest, DontInsertDuplicatePreferred) { @@ -375,7 +396,8 @@ TEST(Headers, NoHeaderSearchInfo) { std::string MainFile = testPath("main.cpp"); IncludeInserter Inserter(MainFile, /*Code=*/"", format::getLLVMStyle(), - /*BuildDir=*/"", /*HeaderSearchInfo=*/nullptr); + /*BuildDir=*/"", /*HeaderSearchInfo=*/nullptr, + /*AlwaysBracketedInclude*/ false); auto HeaderPath = testPath("sub/bar.h"); auto Inserting = HeaderFile{HeaderPath, /*Verbatim=*/false};