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
  • [PATCH] D137794: [clangd] Ena... Nathan James via Phabricator via cfe-commits

Reply via email to