[clang-tools-extra] 0ee7acc - [clangd] Support `=default` in DefineOutline to find insertion point (#175618)
via cfe-commits
cfe-commits at lists.llvm.org
Fri Jan 16 10:53:08 PST 2026
Author: Adrian Vogelsgesang
Date: 2026-01-16T19:53:03+01:00
New Revision: 0ee7accf6610325c94cec5270b42f4160a4116fc
URL: https://github.com/llvm/llvm-project/commit/0ee7accf6610325c94cec5270b42f4160a4116fc
DIFF: https://github.com/llvm/llvm-project/commit/0ee7accf6610325c94cec5270b42f4160a4116fc.diff
LOG: [clangd] Support `=default` in DefineOutline to find insertion point (#175618)
Since #128164, the DefineOutline tweak is looking for a good insertion
point by looking at where neighboring functions are defined. That
heuristic didn't yet handle `= default`. This commit adds support for
it.
Added:
Modified:
clang-tools-extra/clangd/refactor/tweaks/DefineOutline.cpp
clang-tools-extra/clangd/unittests/tweaks/DefineOutlineTests.cpp
Removed:
################################################################################
diff --git a/clang-tools-extra/clangd/refactor/tweaks/DefineOutline.cpp b/clang-tools-extra/clangd/refactor/tweaks/DefineOutline.cpp
index 45e7adeeefcd9..761ab013374ef 100644
--- a/clang-tools-extra/clangd/refactor/tweaks/DefineOutline.cpp
+++ b/clang-tools-extra/clangd/refactor/tweaks/DefineOutline.cpp
@@ -671,6 +671,27 @@ class DefineOutline : public Tweak {
return {};
}
+ // Helper function to skip over a matching pair of tokens (e.g., parentheses
+ // or braces). Returns an iterator to the matching closing token, or the end
+ // iterator if no matching pair is found.
+ template <typename Iterator>
+ Iterator skipTokenPair(Iterator Begin, Iterator End, tok::TokenKind StartKind,
+ tok::TokenKind EndKind) {
+ int Count = 0;
+ for (auto It = Begin; It != End; ++It) {
+ if (It->kind() == StartKind) {
+ ++Count;
+ } else if (It->kind() == EndKind) {
+ if (--Count == 0)
+ return It;
+ if (Count < 0)
+ // We encountered a closing token without a matching opening token.
+ return End;
+ }
+ }
+ return End;
+ }
+
// We don't know the actual start or end of the definition, only the position
// of the name. Therefore, we heuristically try to locate the last token
// before or in this function, respectively. Adapt as required by user code.
@@ -718,34 +739,41 @@ class DefineOutline : public Tweak {
InsertBefore();
} else {
// Skip over one top-level pair of parentheses (for the parameter list)
- // and one pair of curly braces (for the code block).
+ // and one pair of curly braces (for the code block), or `= default`.
// If that fails, insert before the function instead.
auto Tokens =
syntax::tokenize(syntax::FileRange(SM.getMainFileID(), *StartOffset,
Buffer.getBuffer().size()),
SM, AST->getLangOpts());
- bool SkippedParams = false;
- int OpenParens = 0;
- int OpenBraces = 0;
std::optional<syntax::Token> Tok;
- for (const auto &T : Tokens) {
- tok::TokenKind StartKind = SkippedParams ? tok::l_brace : tok::l_paren;
- tok::TokenKind EndKind = SkippedParams ? tok::r_brace : tok::r_paren;
- int &Count = SkippedParams ? OpenBraces : OpenParens;
- if (T.kind() == StartKind) {
- ++Count;
- } else if (T.kind() == EndKind) {
- if (--Count == 0) {
- if (SkippedParams) {
- Tok = T;
- break;
+
+ // Skip parameter list (parentheses)
+ auto ClosingParen = skipTokenPair(Tokens.begin(), Tokens.end(),
+ tok::l_paren, tok::r_paren);
+ if (ClosingParen != Tokens.end()) {
+ // After the closing paren, check for `= default`
+ auto AfterParams = std::next(ClosingParen);
+ if (AfterParams != Tokens.end() && AfterParams->kind() == tok::equal) {
+ auto NextIt = std::next(AfterParams);
+ if (NextIt != Tokens.end() && NextIt->kind() == tok::kw_default) {
+ // Find the semicolon after `default`.
+ auto SemiIt = std::next(NextIt);
+ if (SemiIt != Tokens.end() && SemiIt->kind() == tok::semi) {
+ Tok = *SemiIt;
}
- SkippedParams = true;
- } else if (Count < 0) {
- break;
+ }
+ }
+
+ // If not `= default`, skip function body (braces)
+ if (!Tok && AfterParams != Tokens.end()) {
+ auto ClosingBrace = skipTokenPair(AfterParams, Tokens.end(),
+ tok::l_brace, tok::r_brace);
+ if (ClosingBrace != Tokens.end()) {
+ Tok = *ClosingBrace;
}
}
}
+
if (Tok)
InsertionLoc = Tok->endLocation();
else
diff --git a/clang-tools-extra/clangd/unittests/tweaks/DefineOutlineTests.cpp b/clang-tools-extra/clangd/unittests/tweaks/DefineOutlineTests.cpp
index 7689bf3181a5f..df7d251afc3e4 100644
--- a/clang-tools-extra/clangd/unittests/tweaks/DefineOutlineTests.cpp
+++ b/clang-tools-extra/clangd/unittests/tweaks/DefineOutlineTests.cpp
@@ -950,6 +950,37 @@ inline void Foo::neighbor() {}
)cpp",
{}},
+ // Adjacent definition with `= default`
+ {
+ R"cpp(
+struct Foo {
+ void ignored1();
+ Foo();
+ void fun^c() {}
+ void ignored2();
+};
+)cpp",
+ R"cpp(
+#include "a.hpp"
+void Foo::ignored1() {}
+Foo::Foo() = default;
+void Foo::ignored2() {}
+)cpp",
+ R"cpp(
+struct Foo {
+ void ignored1();
+ Foo();
+ void func() ;
+ void ignored2();
+};
+)cpp",
+ R"cpp(
+#include "a.hpp"
+void Foo::ignored1() {}
+Foo::Foo() = default;void Foo::func() {}
+
+void Foo::ignored2() {}
+)cpp"},
};
for (const auto &Case : Cases) {
More information about the cfe-commits
mailing list