njames93 created this revision. njames93 added reviewers: sammccall, kadircet, nridge. Herald added a subscriber: arphaman. Herald added a project: All. njames93 requested review of this revision. Herald added subscribers: cfe-commits, MaskRay, ilya-biryukov. Herald added a project: clang-tools-extra.
See https://github.com/clangd/clangd/issues/1367 Repository: rG LLVM Github Monorepo https://reviews.llvm.org/D137794 Files: clang-tools-extra/clangd/CMakeLists.txt clang-tools-extra/clangd/ClangdServer.cpp clang-tools-extra/clangd/CodeComplete.cpp clang-tools-extra/clangd/CodeComplete.h clang-tools-extra/clangd/Config.h clang-tools-extra/clangd/ConfigCompile.cpp clang-tools-extra/clangd/ConfigFragment.h clang-tools-extra/clangd/ConfigYAML.cpp clang-tools-extra/clangd/IncludeFixer.cpp clang-tools-extra/clangd/IncludeFixer.h clang-tools-extra/clangd/IncludeRenamer.cpp clang-tools-extra/clangd/IncludeRenamer.h clang-tools-extra/clangd/ParsedAST.cpp clang-tools-extra/clangd/unittests/CodeCompleteTests.cpp clang-tools-extra/clangd/unittests/ConfigCompileTests.cpp clang-tools-extra/clangd/unittests/ConfigYAMLTests.cpp clang-tools-extra/clangd/unittests/DiagnosticsTests.cpp
Index: clang-tools-extra/clangd/unittests/DiagnosticsTests.cpp =================================================================== --- clang-tools-extra/clangd/unittests/DiagnosticsTests.cpp +++ clang-tools-extra/clangd/unittests/DiagnosticsTests.cpp @@ -11,6 +11,7 @@ #include "Diagnostics.h" #include "Feature.h" #include "FeatureModule.h" +#include "IncludeRenamer.h" #include "ParsedAST.h" #include "Protocol.h" #include "TestFS.h" @@ -1294,6 +1295,35 @@ "Include \"b.h\" for symbol na::nb::X"))))); } +TEST(IncludeFixerTest, InsertIncludeRules) { + Annotations Test(R"cpp(// error-ok +$insert[[]]namespace na { +namespace nb { +void foo() { + $unqualified[[X]] x; +} +} +} + )cpp"); + auto TU = TestTU::withCode(Test.code()); + auto Index = buildIndexWithSymbol( + {SymbolWithHeader{"na::X", "unittest:///foo.h", "\"foo.h\""}, + SymbolWithHeader{"na::nb::X", "unittest:///bar.h", "\"bar.h\""}}); + TU.ExternalIndex = Index.get(); + Config Cfg; + Cfg.IncludeRules->addRule({std::make_shared<llvm::Regex>("^foo"), + IncludeRule::Type::Angled, std::string("my_foo")}); + Cfg.IncludeRules->addRule({std::make_shared<llvm::Regex>("^bar"), + IncludeRule::Type::Disabled, None}); + WithContextValue Ctx(Config::Key, std::move(Cfg)); + EXPECT_THAT(*TU.build().getDiagnostics(), + UnorderedElementsAre(AllOf( + Diag(Test.range("unqualified"), "unknown type name 'X'"), + diagName("unknown_typename"), + withFix(Fix(Test.range("insert"), "#include <my_foo.h>\n", + "Include <my_foo.h> for symbol na::X"))))); +} + TEST(IncludeFixerTest, NoCrashMemberAccess) { Annotations Test(R"cpp(// error-ok struct X { int xyz; }; Index: clang-tools-extra/clangd/unittests/ConfigYAMLTests.cpp =================================================================== --- clang-tools-extra/clangd/unittests/ConfigYAMLTests.cpp +++ clang-tools-extra/clangd/unittests/ConfigYAMLTests.cpp @@ -274,6 +274,31 @@ EXPECT_THAT(Results[0].Style.FullyQualifiedNamespaces, ElementsAre(val("foo"), val("bar"))); } + +TEST(ParseYAML, IncludeRules) { + CapturedDiags Diags; + Annotations YAML(R"yaml( +IncludeRules: + - PathMatch: 'Library/' + Type: Angled + - PathMatch: 'Library2/' + Type: Quoted + Replace: 'Libary/' +--- +IncludeRules: + $listinput^PathMatch: 'Library/' + Type: Angled + Replace: 'Library2/' +)yaml"); + auto Results = + Fragment::parseYAML(YAML.code(), "config.yaml", Diags.callback()); + ASSERT_EQ(Results.size(), 1u); + EXPECT_EQ(Results[0].IncludeRules.size(), 2U); + EXPECT_THAT(Diags.Diagnostics, + ElementsAre(AllOf(diagMessage("Expected list of Input Rules"), + diagKind(llvm::SourceMgr::DK_Error), + diagPos(YAML.point("listinput"))))); +} } // namespace } // namespace config } // namespace clangd Index: clang-tools-extra/clangd/unittests/ConfigCompileTests.cpp =================================================================== --- clang-tools-extra/clangd/unittests/ConfigCompileTests.cpp +++ clang-tools-extra/clangd/unittests/ConfigCompileTests.cpp @@ -544,6 +544,71 @@ EXPECT_TRUE(compileAndApply()); EXPECT_THAT(Conf.Style.FullyQualifiedNamespaces, ElementsAre("foo", "bar")); } + +TEST_F(ConfigCompileTests, IncludeRules) { + + auto Add = [&](llvm::StringRef PathMatch = "", llvm::StringRef Type = "", + llvm::StringRef Replace = "") { + Fragment::IncludeRuleBlock IRB; + if (!PathMatch.empty()) + IRB.PathMatch.emplace(PathMatch.str()); + if (!Type.empty()) + IRB.Type.emplace(Type.str()); + if (!Replace.empty()) + IRB.Replace.emplace(Replace.str()); + Frag.IncludeRules.push_back(std::move(IRB)); + }; + + { + Frag = {}; + Add(); + EXPECT_TRUE(compileAndApply()); + EXPECT_THAT( + Diags.Diagnostics, + Contains(AllOf(diagMessage("Include block must contain 'PathMatch'"), + diagKind(llvm::SourceMgr::DK_Error)))); + EXPECT_TRUE(Conf.IncludeRules->empty()); + } + { + Frag = {}; + Add("InvalidRegex("); + EXPECT_TRUE(compileAndApply()); + EXPECT_THAT( + Diags.Diagnostics, + Contains(AllOf(diagMessage("Invalid regular expression " + "'InvalidRegex(': parentheses not balanced"), + diagKind(llvm::SourceMgr::DK_Error)))); + EXPECT_TRUE(Conf.IncludeRules->empty()); + } + { + Frag = {}; + Add("^library-10/"); + EXPECT_TRUE(compileAndApply()); + EXPECT_THAT(Diags.Diagnostics, IsEmpty()); + EXPECT_EQ(Conf.IncludeRules->size(), 1U); + } + { + Frag = {}; + Add("^library-10/", "Invalid"); + EXPECT_TRUE(compileAndApply()); + EXPECT_THAT(Diags.Diagnostics, + Contains(AllOf( + diagMessage("Invalid Type value 'Invalid'. " + "Valid values are Quoted, Angled, Disabled."), + diagKind(llvm::SourceMgr::DK_Warning)))); + EXPECT_EQ(Conf.IncludeRules->size(), 1U); + } + + { + Frag = {}; + Add("^library-10/", "Quoted"); + Add("^library-12/", "Angled"); + Add("^library-13/", "", "library/"); + EXPECT_TRUE(compileAndApply()); + EXPECT_THAT(Diags.Diagnostics, IsEmpty()); + EXPECT_EQ(Conf.IncludeRules->size(), 3U); + } +} } // namespace } // namespace config } // namespace clangd Index: clang-tools-extra/clangd/unittests/CodeCompleteTests.cpp =================================================================== --- clang-tools-extra/clangd/unittests/CodeCompleteTests.cpp +++ clang-tools-extra/clangd/unittests/CodeCompleteTests.cpp @@ -11,6 +11,7 @@ #include "ClangdServer.h" #include "CodeComplete.h" #include "Compiler.h" +#include "IncludeRenamer.h" #include "Matchers.h" #include "Protocol.h" #include "Quality.h" @@ -2651,6 +2652,22 @@ EXPECT_EQ(Results[0].Includes.size(), 2u); } +TEST(CompletionTest, InsertIncludeRules) { + std::string DeclFile = URI::create(testPath("foo")).toString(); + Symbol Sym = func("Func"); + Sym.CanonicalDeclaration.FileURI = DeclFile.c_str(); + Sym.IncludeHeaders.emplace_back("\"foo.h\"", 2); + + CodeCompleteOptions Opts; + auto Renamer = std::make_shared<IncludeRenamer>(); + Renamer->addRule({std::make_shared<llvm::Regex>("foo"), + IncludeRule::Type::Angled, std::string("my_foo")}); + Opts.IncludeRenamer = Renamer; + auto Results = completions("Fun^", {Sym}, Opts).Completions; + assert(!Results.empty()); + EXPECT_THAT(Results[0], AllOf(insertInclude("<my_foo.h>"))); +} + TEST(CompletionTest, NoInsertIncludeIfOnePresent) { Annotations Test(R"cpp( #include "foo.h" Index: clang-tools-extra/clangd/ParsedAST.cpp =================================================================== --- clang-tools-extra/clangd/ParsedAST.cpp +++ clang-tools-extra/clangd/ParsedAST.cpp @@ -563,7 +563,7 @@ Inserter->addExisting(Inc); } FixIncludes.emplace(Filename, Inserter, *Inputs.Index, - /*IndexRequestLimit=*/5); + /*IndexRequestLimit=*/5, Cfg.IncludeRules); ASTDiags.contributeFixes([&FixIncludes](DiagnosticsEngine::Level DiagLevl, const clang::Diagnostic &Info) { return FixIncludes->fix(DiagLevl, Info); Index: clang-tools-extra/clangd/IncludeRenamer.h =================================================================== --- /dev/null +++ clang-tools-extra/clangd/IncludeRenamer.h @@ -0,0 +1,57 @@ +//===--- IncludeRenamed.h-----------------------------------------*- C++-*-===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANGD_INCLUDERENAMED_H +#define LLVM_CLANG_TOOLS_EXTRA_CLANGD_INCLUDERENAMED_H + +#include "clang/Basic/LLVM.h" +#include "llvm/ADT/Optional.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/Support/Regex.h" +#include <memory> +#include <mutex> +#include <vector> +namespace clang::clangd { + +class IncludeRule { + friend class IncludeRenamer; + +public: + enum class Type { Angled, Quoted, Disabled }; + + IncludeRule(std::shared_ptr<llvm::Regex> Match, Optional<Type> IncludeType, + Optional<std::string> Replace) + : Match(std::move(Match)), IncludeType(IncludeType), + Replace(std::move(Replace)) {} + +private: + std::shared_ptr<llvm::Regex> Match; + Optional<Type> IncludeType; + Optional<std::string> Replace; +}; + +class IncludeRenamer { +public: + Optional<std::string> processInclude(StringRef Include) const; + + void addRule(IncludeRule &&Rule); + + void addRules(ArrayRef<IncludeRule> Rules); + + bool empty() const; + + size_t size() const; + +private: + mutable std::mutex Guard; + std::vector<IncludeRule> Rules; +}; + +} // namespace clang::clangd + +#endif // LLVM_CLANG_TOOLS_EXTRA_CLANGD_INCLUDERENAMED_H \ No newline at end of file Index: clang-tools-extra/clangd/IncludeRenamer.cpp =================================================================== --- /dev/null +++ clang-tools-extra/clangd/IncludeRenamer.cpp @@ -0,0 +1,80 @@ +//===--- IncludeRenamed.cpp---------------------------------------*- C++-*-===// +// +// 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 "IncludeRenamer.h" +#include "support/Logger.h" +#include "llvm/ADT/None.h" +#include <mutex> + +namespace clang::clangd { + +llvm::Optional<std::string> +IncludeRenamer::processInclude(llvm::StringRef Include) const { + std::lock_guard<std::mutex> Lock(Guard); + if (Include.empty()) + return llvm::None; + if (Rules.empty()) + return Include.str(); + llvm::StringRef Hdr = Include; + bool IsQuote = Hdr.consume_front("\""); + if (IsQuote ? Hdr.consume_back("\"") + : (Hdr.consume_front("<") && Hdr.consume_back(">"))) { + for (auto &Rule : Rules) { + if (Rule.Match->match(Hdr)) { + if (Rule.IncludeType == IncludeRule::Type::Disabled) + return llvm::None; + bool NewQuotes = (Rule.IncludeType) + ? (*Rule.IncludeType == IncludeRule::Type::Quoted) + : IsQuote; + std::string NewHdr = NewQuotes ? "\"" : "<"; + std::string Err; + if (Rule.Replace) { + NewHdr += Rule.Match->sub(*Rule.Replace, Hdr, &Err); + if (!Err.empty()) { + log("Failed to regex replace include '{0}' with error: " + "'{1}')", + Hdr, Err); + break; + } + } else { + NewHdr += Hdr; + } + NewHdr += NewQuotes ? '"' : '>'; + return std::move(NewHdr); + } + } + } + return Include.str(); +} + +void IncludeRenamer::addRule(IncludeRule &&Rule) { + assert(Rule.Match && Rule.Match->isValid()); + std::lock_guard<std::mutex> Lock(Guard); + Rules.push_back(std::move(Rule)); +} + +void IncludeRenamer::addRules(ArrayRef<IncludeRule> Rules) { + assert(llvm::all_of(Rules, [](const IncludeRule &R) { + return R.Match && R.Match->isValid(); + })); + std::lock_guard<std::mutex> Lock(Guard); + // Insert from the beginning as higher priority configs are loaded later. + this->Rules.insert(this->Rules.begin(), Rules.begin(), Rules.end()); +} + +bool IncludeRenamer::empty() const { + std::lock_guard<std::mutex> Lock(Guard); + return Rules.empty(); +} + +size_t IncludeRenamer::size() const { + std::lock_guard<std::mutex> Lock(Guard); + return Rules.size(); +} + +} // namespace clang::clangd Index: clang-tools-extra/clangd/IncludeFixer.h =================================================================== --- clang-tools-extra/clangd/IncludeFixer.h +++ clang-tools-extra/clangd/IncludeFixer.h @@ -11,6 +11,7 @@ #include "Diagnostics.h" #include "Headers.h" +#include "IncludeRenamer.h" #include "index/Index.h" #include "index/Symbol.h" #include "clang/AST/Type.h" @@ -33,9 +34,10 @@ class IncludeFixer { public: IncludeFixer(llvm::StringRef File, std::shared_ptr<IncludeInserter> Inserter, - const SymbolIndex &Index, unsigned IndexRequestLimit) + const SymbolIndex &Index, unsigned IndexRequestLimit, + std::shared_ptr<const IncludeRenamer> Renamer) : File(File), Inserter(std::move(Inserter)), Index(Index), - IndexRequestLimit(IndexRequestLimit) {} + IndexRequestLimit(IndexRequestLimit), Renamer(Renamer) {} /// Returns include insertions that can potentially recover the diagnostic. /// If Info is a note and fixes are returned, they should *replace* the note. @@ -92,6 +94,8 @@ llvm::Optional<const SymbolSlab *> fuzzyFindCached(const FuzzyFindRequest &Req) const; llvm::Optional<const SymbolSlab *> lookupCached(const SymbolID &ID) const; + + std::shared_ptr<const IncludeRenamer> Renamer; }; } // namespace clangd Index: clang-tools-extra/clangd/IncludeFixer.cpp =================================================================== --- clang-tools-extra/clangd/IncludeFixer.cpp +++ clang-tools-extra/clangd/IncludeFixer.cpp @@ -319,11 +319,13 @@ for (const auto &Inc : getRankedIncludes(Sym)) { if (auto ToInclude = Inserted(Sym, Inc)) { if (ToInclude->second) { - if (!InsertedHeaders.try_emplace(ToInclude->first).second) - continue; - if (auto Fix = - insertHeader(ToInclude->first, (Sym.Scope + Sym.Name).str())) - Fixes.push_back(std::move(*Fix)); + if (auto MappedInclude = Renamer->processInclude(ToInclude->first)) { + if (!InsertedHeaders.try_emplace(*MappedInclude).second) + continue; + if (auto Fix = + insertHeader(*MappedInclude, (Sym.Scope + Sym.Name).str())) + Fixes.push_back(std::move(*Fix)); + } } } else { vlog("Failed to calculate include insertion for {0} into {1}: {2}", Inc, Index: clang-tools-extra/clangd/ConfigYAML.cpp =================================================================== --- clang-tools-extra/clangd/ConfigYAML.cpp +++ clang-tools-extra/clangd/ConfigYAML.cpp @@ -68,6 +68,7 @@ Dict.handle("Completion", [&](Node &N) { parse(F.Completion, N); }); Dict.handle("Hover", [&](Node &N) { parse(F.Hover, N); }); Dict.handle("InlayHints", [&](Node &N) { parse(F.InlayHints, N); }); + Dict.handle("IncludeRules", [&](Node &N) { parse(F.IncludeRules, N); }); Dict.parse(N); return !(N.failed() || HadError); } @@ -251,6 +252,58 @@ Dict.parse(N); } + llvm::Optional<Located<Fragment::IncludeRuleBlock>> + parseIncludeRule(Node &N) { + Fragment::IncludeRuleBlock Result; + DictParser Dict("IncludeRule", this); + Dict.handle("PathMatch", [&](Node &N) { + if (auto Value = scalarValue(N, "PathMatch")) + Result.PathMatch = *Value; + }); + Dict.handle("Type", [&](Node &N) { + if (auto Value = scalarValue(N, "Type")) + Result.Type = *Value; + }); + Dict.handle("Replace", [&](Node &N) { + if (auto Value = scalarValue(N, "Replace")) + Result.Replace = *Value; + }); + Dict.parse(N); + if (this->HadError) + return llvm::None; + return Located<Fragment::IncludeRuleBlock>(std::move(Result), + N.getSourceRange()); + } + + void parse(std::vector<Located<Fragment::IncludeRuleBlock>> &F, Node &N) { + if (auto *S = llvm::dyn_cast<SequenceNode>(&N)) { + llvm::Optional<Fragment::IncludeRuleBlock> Result; + DictParser Dict("IncludeRule", this); + Dict.handle("PathMatch", [&](Node &N) { + if (auto Value = scalarValue(N, "PathMatch")) + Result->PathMatch = *Value; + }); + Dict.handle("Type", [&](Node &N) { + if (auto Value = scalarValue(N, "Type")) + Result->Type = *Value; + }); + Dict.handle("Replace", [&](Node &N) { + if (auto Value = scalarValue(N, "Replace")) + Result->Replace = *Value; + }); + for (auto &Child : *S) { + Result.emplace(); + Dict.parse(Child); + if (!this->HadError) + F.emplace_back(std::move(*Result), Child.getSourceRange()); + Result.emplace(); + } + } else { + N.skip(); + error("Expected list of Input Rules", N); + } + } + // Helper for parsing mapping nodes (dictionaries). // We don't use YamlIO as we want to control over unknown keys. class DictParser { Index: clang-tools-extra/clangd/ConfigFragment.h =================================================================== --- clang-tools-extra/clangd/ConfigFragment.h +++ clang-tools-extra/clangd/ConfigFragment.h @@ -307,6 +307,37 @@ llvm::Optional<Located<bool>> Designators; }; InlayHintsBlock InlayHints; + + struct IncludeRuleBlock { + /// A regex that must be matched for this rule to take effect. + /// Only partial matching is needed so: + /// {PathMatch: '^foo/'} will match all includes under the foo directory + llvm::Optional<Located<std::string>> PathMatch; + + /// What type of include we should generate. + /// + /// Valid values are: + /// - Quoted: insert a "double_quoted" include. + /// - Angled: insert an <angle_bracket> include. + /// - Disabled: don't emit an include for this item + llvm::Optional<Located<std::string>> Type; + + /// A regex replacement string to transform the matched path. + /// For example with this config: + /// PathMatch: '^foo/' + /// Replace: 'my_foo/' + /// + /// The include: `<foo/impl.h>` would be changed to: + /// `<my_foo/impl.h>` + /// Note only the first regex match is replaced, therefor given a regex + /// 'foo/' and a replace 'my_foo', the include: `<foo/foo/impl.h>` would be + /// changed to: `<my_foo/foo/impl.h>` + llvm::Optional<Located<std::string>> Replace; + }; + + /// A list of @c IncludeRuleBlock`s. This list is traversed in order and stops + /// being processed once the first rule matches. + std::vector<Located<IncludeRuleBlock>> IncludeRules; }; } // namespace config Index: clang-tools-extra/clangd/ConfigCompile.cpp =================================================================== --- clang-tools-extra/clangd/ConfigCompile.cpp +++ clang-tools-extra/clangd/ConfigCompile.cpp @@ -29,6 +29,7 @@ #include "ConfigProvider.h" #include "Diagnostics.h" #include "Feature.h" +#include "IncludeRenamer.h" #include "TidyProvider.h" #include "support/Logger.h" #include "support/Path.h" @@ -198,6 +199,7 @@ compile(std::move(F.Hover)); compile(std::move(F.InlayHints)); compile(std::move(F.Style)); + compile(std::move(F.IncludeRules)); } void compile(Fragment::IfBlock &&F) { @@ -588,6 +590,55 @@ }); } + void compile(std::vector<Located<Fragment::IncludeRuleBlock>> &&F) { +#ifdef CLANGD_PATH_CASE_INSENSITIVE + static llvm::Regex::RegexFlags Flags = llvm::Regex::IgnoreCase; +#else + static llvm::Regex::RegexFlags Flags = llvm::Regex::NoFlags; +#endif + std::vector<IncludeRule> Rules; + for (auto &Item : F) { + std::shared_ptr<llvm::Regex> Regex; + if (!Item->PathMatch) { + diag(Error, "Include block must contain 'PathMatch'", Item.Range); + } else { + Regex = std::make_shared<llvm::Regex>(**Item->PathMatch, Flags); + // Don't anchor these regexes + std::string RegexError; + if (!Regex->isValid(RegexError)) { + diag(Error, + llvm::formatv("Invalid regular expression '{0}': {1}", + **Item->PathMatch, RegexError) + .str(), + Item->PathMatch->Range); + Regex.reset(); + } + } + llvm::Optional<IncludeRule::Type> Type; + if (Item->Type) { + if (auto Value = compileEnum<IncludeRule::Type>("Type", *Item->Type) + .map("Quoted", IncludeRule::Type::Quoted) + .map("Angled", IncludeRule::Type::Angled) + .map("Disabled", IncludeRule::Type::Disabled) + .value()) { + Type = Value; + } + } + if (Regex) { + llvm::Optional<std::string> Replace; + if (Item->Replace) + Replace = std::move(**Item->Replace); + Rules.emplace_back(std::move(Regex), std::move(Type), + std::move(Replace)); + } + } + if (!Rules.empty()) { + Out.Apply.push_back([Rules(std::move(Rules))](const Params &, Config &C) { + C.IncludeRules->addRules(Rules); + }); + } + } + constexpr static llvm::SourceMgr::DiagKind Error = llvm::SourceMgr::DK_Error; constexpr static llvm::SourceMgr::DiagKind Warning = llvm::SourceMgr::DK_Warning; Index: clang-tools-extra/clangd/Config.h =================================================================== --- clang-tools-extra/clangd/Config.h +++ clang-tools-extra/clangd/Config.h @@ -24,11 +24,13 @@ #ifndef LLVM_CLANG_TOOLS_EXTRA_CLANGD_CONFIG_H #define LLVM_CLANG_TOOLS_EXTRA_CLANGD_CONFIG_H +#include "IncludeRenamer.h" #include "support/Context.h" #include "llvm/ADT/FunctionExtras.h" #include "llvm/ADT/Optional.h" #include "llvm/ADT/StringMap.h" #include "llvm/ADT/StringSet.h" +#include "llvm/Support/Regex.h" #include <functional> #include <string> #include <vector> @@ -140,6 +142,10 @@ bool DeducedTypes = true; bool Designators = true; } InlayHints; + + /// Rules for configuring include insertions. + std::shared_ptr<IncludeRenamer> IncludeRules = + std::make_shared<IncludeRenamer>(); }; } // namespace clangd Index: clang-tools-extra/clangd/CodeComplete.h =================================================================== --- clang-tools-extra/clangd/CodeComplete.h +++ clang-tools-extra/clangd/CodeComplete.h @@ -17,6 +17,8 @@ #include "ASTSignals.h" #include "Compiler.h" +#include "Config.h" +#include "IncludeRenamer.h" #include "Protocol.h" #include "Quality.h" #include "index/Index.h" @@ -31,6 +33,7 @@ #include "llvm/ADT/StringRef.h" #include <functional> #include <future> +#include <memory> #include <utility> namespace clang { @@ -148,6 +151,8 @@ /// Semantics: E.g. For Base = 1.3, if the Prediction score reduces by 2.6 /// points then completion score reduces by 50% or 1.3^(-2.6). float DecisionForestBase = 1.3f; + + std::shared_ptr<const IncludeRenamer> IncludeRenamer; }; // Semi-structured representation of a code-complete suggestion for our C++ API. Index: clang-tools-extra/clangd/CodeComplete.cpp =================================================================== --- clang-tools-extra/clangd/CodeComplete.cpp +++ 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 "FileDistance.h" #include "FuzzyMatch.h" @@ -385,10 +386,15 @@ for (const auto &Inc : C.RankedIncludeHeaders) { if (auto ToInclude = Inserted(Inc)) { CodeCompletion::IncludeCandidate Include; - Include.Header = ToInclude->first; - if (ToInclude->second && ShouldInsert) - Include.Insertion = Includes.insert(ToInclude->first); - Completion.Includes.push_back(std::move(Include)); + if (Optional<std::string> MappedInclude = + Opts.IncludeRenamer + ? Opts.IncludeRenamer->processInclude(ToInclude->first) + : ToInclude->first) { + Include.Header = *MappedInclude; + if (ToInclude->second && ShouldInsert) + Include.Insertion = Includes.insert(*MappedInclude); + Completion.Includes.push_back(std::move(Include)); + } } else log("Failed to generate include insertion edits for adding header " "(FileURI='{0}', IncludeHeader='{1}') into {2}: {3}", Index: clang-tools-extra/clangd/ClangdServer.cpp =================================================================== --- clang-tools-extra/clangd/ClangdServer.cpp +++ clang-tools-extra/clangd/ClangdServer.cpp @@ -404,6 +404,7 @@ CodeCompleteOpts.MainFileSignals = IP->Signals; CodeCompleteOpts.AllScopes = Config::current().Completion.AllScopes; + CodeCompleteOpts.IncludeRenamer = Config::current().IncludeRules; // FIXME(ibiryukov): even if Preamble is non-null, we may want to check // both the old and the new version in case only one of them matches. CodeCompleteResult Result = clangd::codeComplete( Index: clang-tools-extra/clangd/CMakeLists.txt =================================================================== --- clang-tools-extra/clangd/CMakeLists.txt +++ clang-tools-extra/clangd/CMakeLists.txt @@ -85,6 +85,7 @@ Hover.cpp IncludeCleaner.cpp IncludeFixer.cpp + IncludeRenamer.cpp InlayHints.cpp JSONTransport.cpp PathMapping.cpp
_______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits