Skip to content

Conversation

@MythreyaK
Copy link
Contributor

@MythreyaK MythreyaK commented Nov 28, 2025

Adds MacroFilterPolicy

Completion:
  MacroFilter: ExactPrefix (default), FuzzyMatch

Fixes clangd/clangd#1480

Allow results from macros that do not have leading
or trailing underscores.
@MythreyaK MythreyaK marked this pull request as ready for review November 28, 2025 08:51
@llvmbot
Copy link
Member

llvmbot commented Nov 28, 2025

@llvm/pr-subscribers-clangd

@llvm/pr-subscribers-clang-tools-extra

Author: Mythreya Kuricheti (MythreyaK)

Changes

Adds MacroFilterPolicy

Completion:
  MacroFilter: ExactPrefix (default), FuzzyMatch

Fix for clangd/clangd#1480


Full diff: https://github.com/llvm/llvm-project/pull/169880.diff

9 Files Affected:

  • (modified) clang-tools-extra/clangd/ClangdServer.cpp (+1)
  • (modified) clang-tools-extra/clangd/CodeComplete.cpp (+28-7)
  • (modified) clang-tools-extra/clangd/CodeComplete.h (+5)
  • (modified) clang-tools-extra/clangd/Config.h (+8)
  • (modified) clang-tools-extra/clangd/ConfigCompile.cpp (+16-4)
  • (modified) clang-tools-extra/clangd/ConfigFragment.h (+6)
  • (modified) clang-tools-extra/clangd/ConfigYAML.cpp (+4)
  • (modified) clang-tools-extra/clangd/unittests/CodeCompleteTests.cpp (+29)
  • (modified) clang-tools-extra/clangd/unittests/ConfigYAMLTests.cpp (+14)
diff --git a/clang-tools-extra/clangd/ClangdServer.cpp b/clang-tools-extra/clangd/ClangdServer.cpp
index ac1e9aa5f0ff1..f1a87dd12d905 100644
--- a/clang-tools-extra/clangd/ClangdServer.cpp
+++ b/clang-tools-extra/clangd/ClangdServer.cpp
@@ -458,6 +458,7 @@ void ClangdServer::codeComplete(PathRef File, Position Pos,
     CodeCompleteOpts.InsertIncludes =
         Config::current().Completion.HeaderInsertion;
     CodeCompleteOpts.CodePatterns = Config::current().Completion.CodePatterns;
+    CodeCompleteOpts.MacroFilter = Config::current().Completion.MacroFilter;
     // 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(
diff --git a/clang-tools-extra/clangd/CodeComplete.cpp b/clang-tools-extra/clangd/CodeComplete.cpp
index e4df7581f1315..0358455ff1f75 100644
--- a/clang-tools-extra/clangd/CodeComplete.cpp
+++ b/clang-tools-extra/clangd/CodeComplete.cpp
@@ -1435,7 +1435,8 @@ bool semaCodeComplete(std::unique_ptr<CodeCompleteConsumer> Consumer,
   Clang->setCodeCompletionConsumer(Consumer.release());
 
   if (Input.Preamble.RequiredModules)
-    Input.Preamble.RequiredModules->adjustHeaderSearchOptions(Clang->getHeaderSearchOpts());
+    Input.Preamble.RequiredModules->adjustHeaderSearchOptions(
+        Clang->getHeaderSearchOpts());
 
   SyntaxOnlyAction Action;
   if (!Action.BeginSourceFile(*Clang, Clang->getFrontendOpts().Inputs[0])) {
@@ -2037,14 +2038,34 @@ class CodeCompleteFlow {
   }
 
   std::optional<float> fuzzyScore(const CompletionCandidate &C) {
-    // Macros can be very spammy, so we only support prefix completion.
-    if (((C.SemaResult &&
+    using MacroFilterPolicy = Config::MacroFilterPolicy;
+
+    const auto IsMacroResult =
+        ((C.SemaResult &&
           C.SemaResult->Kind == CodeCompletionResult::RK_Macro) ||
          (C.IndexResult &&
-          C.IndexResult->SymInfo.Kind == index::SymbolKind::Macro)) &&
-        !C.Name.starts_with_insensitive(Filter->pattern()))
-      return std::nullopt;
-    return Filter->match(C.Name);
+          C.IndexResult->SymInfo.Kind == index::SymbolKind::Macro));
+
+    if (!IsMacroResult)
+      return Filter->match(C.Name);
+
+    // macros with leading and trailing underscore are probably spammy
+    switch (Opts.MacroFilter) {
+    case MacroFilterPolicy::ExactPrefix:
+      if (C.Name.starts_with_insensitive(Filter->pattern()))
+        return Filter->match(C.Name);
+      else
+        return std::nullopt;
+    case MacroFilterPolicy::FuzzyMatch:
+      if (!C.Name.starts_with_insensitive("_") &&
+          !C.Name.ends_with_insensitive("_"))
+        return Filter->match(C.Name);
+      else
+        return std::nullopt;
+    }
+    llvm_unreachable("Unhandled MacroFilter option in fuzzyScore.");
+
+    return std::nullopt;
   }
 
   CodeCompletion::Scores
diff --git a/clang-tools-extra/clangd/CodeComplete.h b/clang-tools-extra/clangd/CodeComplete.h
index 1cf3b41119043..cde22a8212e6a 100644
--- a/clang-tools-extra/clangd/CodeComplete.h
+++ b/clang-tools-extra/clangd/CodeComplete.h
@@ -114,6 +114,11 @@ struct CodeCompleteOptions {
   /// Whether to suggest code patterns & snippets or not in completion
   Config::CodePatternsPolicy CodePatterns = Config::CodePatternsPolicy::All;
 
+  /// Filter macros using an exact prefix, or with a fuzzy match. In both cases,
+  /// macros with leading or trailing underscores are strictly filtered
+  Config::MacroFilterPolicy MacroFilter =
+      Config::MacroFilterPolicy::ExactPrefix;
+
   /// Whether to use the clang parser, or fallback to text-based completion
   /// (using identifiers in the current file and symbol indexes).
   enum CodeCompletionParse {
diff --git a/clang-tools-extra/clangd/Config.h b/clang-tools-extra/clangd/Config.h
index 01997cee08515..bc58c831e4e89 100644
--- a/clang-tools-extra/clangd/Config.h
+++ b/clang-tools-extra/clangd/Config.h
@@ -157,6 +157,12 @@ struct Config {
     None // Suggest none of the code patterns and snippets
   };
 
+  enum class MacroFilterPolicy {
+    ExactPrefix, // Suggest macros if the prefix matches exactly
+    FuzzyMatch,  // Fuzzy-match macros if they do not have "_" as prefix or
+                 // suffix
+  };
+
   /// Configures code completion feature.
   struct {
     /// Whether code completion includes results that are not visible in current
@@ -168,6 +174,8 @@ struct Config {
     HeaderInsertionPolicy HeaderInsertion = HeaderInsertionPolicy::IWYU;
     /// Enables code patterns & snippets suggestions
     CodePatternsPolicy CodePatterns = CodePatternsPolicy::All;
+    /// Controls how macros are filtered
+    MacroFilterPolicy MacroFilter = MacroFilterPolicy::ExactPrefix;
   } Completion;
 
   /// Configures hover feature.
diff --git a/clang-tools-extra/clangd/ConfigCompile.cpp b/clang-tools-extra/clangd/ConfigCompile.cpp
index 18e31809aa7c7..2b41949d6d05c 100644
--- a/clang-tools-extra/clangd/ConfigCompile.cpp
+++ b/clang-tools-extra/clangd/ConfigCompile.cpp
@@ -564,10 +564,10 @@ struct FragmentCompiler {
       auto Fast = isFastTidyCheck(Str);
       if (!Fast.has_value()) {
         diag(Warning,
-             llvm::formatv(
-                 "Latency of clang-tidy check '{0}' is not known. "
-                 "It will only run if ClangTidy.FastCheckFilter is Loose or None",
-                 Str)
+             llvm::formatv("Latency of clang-tidy check '{0}' is not known. "
+                           "It will only run if ClangTidy.FastCheckFilter is "
+                           "Loose or None",
+                           Str)
                  .str(),
              Arg.Range);
       } else if (!*Fast) {
@@ -719,6 +719,18 @@ struct FragmentCompiler {
           C.Completion.CodePatterns = *Val;
         });
     }
+
+    if (F.MacroFilter) {
+      if (auto Val =
+              compileEnum<Config::MacroFilterPolicy>("MacroFilter",
+                                                     *F.MacroFilter)
+                  .map("ExactPrefix", Config::MacroFilterPolicy::ExactPrefix)
+                  .map("FuzzyMatch", Config::MacroFilterPolicy::FuzzyMatch)
+                  .value())
+        Out.Apply.push_back([Val](const Params &, Config &C) {
+          C.Completion.MacroFilter = *Val;
+        });
+    }
   }
 
   void compile(Fragment::HoverBlock &&F) {
diff --git a/clang-tools-extra/clangd/ConfigFragment.h b/clang-tools-extra/clangd/ConfigFragment.h
index 2afeb36574b21..7604fe4e24c97 100644
--- a/clang-tools-extra/clangd/ConfigFragment.h
+++ b/clang-tools-extra/clangd/ConfigFragment.h
@@ -354,6 +354,12 @@ struct Fragment {
     ///   All  => enable all code patterns and snippets suggestion
     ///   None => disable all code patterns and snippets suggestion
     std::optional<Located<std::string>> CodePatterns;
+    /// How to filter macros before offering them as suggestions
+    /// Values are Config::MacroFilterPolicy:
+    ///   ExactPrefix:  Suggest macros if the prefix matches exactly
+    ///   FuzzyMatch:   Fuzzy-match macros if they do not have "_" as prefix or
+    ///   suffix
+    std::optional<Located<std::string>> MacroFilter;
   };
   CompletionBlock Completion;
 
diff --git a/clang-tools-extra/clangd/ConfigYAML.cpp b/clang-tools-extra/clangd/ConfigYAML.cpp
index 392cf19b05a55..7b6993620fb8c 100644
--- a/clang-tools-extra/clangd/ConfigYAML.cpp
+++ b/clang-tools-extra/clangd/ConfigYAML.cpp
@@ -255,6 +255,10 @@ class Parser {
       if (auto CodePatterns = scalarValue(N, "CodePatterns"))
         F.CodePatterns = *CodePatterns;
     });
+    Dict.handle("MacroFilter", [&](Node &N) {
+      if (auto MacroFilter = scalarValue(N, "MacroFilter"))
+        F.MacroFilter = *MacroFilter;
+    });
     Dict.parse(N);
   }
 
diff --git a/clang-tools-extra/clangd/unittests/CodeCompleteTests.cpp b/clang-tools-extra/clangd/unittests/CodeCompleteTests.cpp
index e2bdb0fe46e37..1bec601ef1d36 100644
--- a/clang-tools-extra/clangd/unittests/CodeCompleteTests.cpp
+++ b/clang-tools-extra/clangd/unittests/CodeCompleteTests.cpp
@@ -4692,6 +4692,35 @@ TEST(CompletionTest, ListExplicitObjectOverloads) {
   }
 }
 
+TEST(CompletionTest, FuzzyMatchMacro) {
+  const auto *const Code = R"cpp(
+  #define gl_foo() 42
+  #define _gl_foo() 42
+  int gl_frob();
+
+  int main() {
+    int x = glf^
+  }
+  )cpp";
+
+  {
+    CodeCompleteOptions Opts{};
+    EXPECT_EQ(Opts.MacroFilter, Config::MacroFilterPolicy::ExactPrefix);
+
+    auto Results = completions(Code, {}, Opts);
+    EXPECT_THAT(Results.Completions, ElementsAre(named("gl_frob")));
+  }
+
+  {
+    CodeCompleteOptions Opts{};
+    Opts.MacroFilter = Config::MacroFilterPolicy::FuzzyMatch;
+
+    auto Results = completions(Code, {}, Opts);
+    EXPECT_THAT(Results.Completions,
+                ElementsAre(named("gl_frob"), named("gl_foo")));
+  }
+}
+
 } // namespace
 } // namespace clangd
 } // namespace clang
diff --git a/clang-tools-extra/clangd/unittests/ConfigYAMLTests.cpp b/clang-tools-extra/clangd/unittests/ConfigYAMLTests.cpp
index c332dcc417fe1..264cb453b413c 100644
--- a/clang-tools-extra/clangd/unittests/ConfigYAMLTests.cpp
+++ b/clang-tools-extra/clangd/unittests/ConfigYAMLTests.cpp
@@ -228,6 +228,20 @@ TEST(ParseYAML, CodePatterns) {
   EXPECT_THAT(Results[0].Completion.CodePatterns, llvm::ValueIs(val("None")));
 }
 
+TEST(ParseYAML, MacroFilter) {
+  CapturedDiags Diags;
+  Annotations YAML(R"yaml(
+    Completion:
+      MacroFilter: FuzzyMatch
+  )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].Completion.MacroFilter,
+              llvm::ValueIs(val("FuzzyMatch")));
+}
+
 TEST(ParseYAML, Hover) {
   CapturedDiags Diags;
   Annotations YAML(R"yaml(

@zwuis
Copy link
Contributor

zwuis commented Nov 28, 2025

Please add a release note entry to "clang-tools-extra/docs/ReleaseNotes.rst". Thanks!

Fix for clangd/clangd#1480

You can use this syntax.

@MythreyaK
Copy link
Contributor Author

Fix for clangd/clangd#1480

You can use this syntax.

Thanks, I keep forgetting it 😅

Updated the release notes, please let me know if it can be improved.

@HighCommander4 HighCommander4 self-requested a review November 29, 2025 02:16
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add an option to use fuzzy match for macro completions too (they currently require an exact prefix match)

3 participants