Skip to content

Commit 9ae21b0

Browse files
[clangd] fix extract-to-function for overloaded operators (#81640)
When selecting code that contains the use of overloaded operators, the SelectionTree will attribute the operator to the operator declaration, not to the `CXXOperatorCallExpr`. To allow extract-to-function to work with these operators, make unselected `CXXOperatorCallExpr`s valid root statements, just like `DeclStmt`s. Partially fixes clangd/clangd#1254 --------- Co-authored-by: Nathan Ridge <[email protected]>
1 parent 1b23ebe commit 9ae21b0

File tree

3 files changed

+73
-8
lines changed

3 files changed

+73
-8
lines changed

clang-tools-extra/clangd/refactor/tweaks/ExtractFunction.cpp

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@
5656
#include "clang/AST/ASTContext.h"
5757
#include "clang/AST/Decl.h"
5858
#include "clang/AST/DeclBase.h"
59+
#include "clang/AST/ExprCXX.h"
5960
#include "clang/AST/NestedNameSpecifier.h"
6061
#include "clang/AST/RecursiveASTVisitor.h"
6162
#include "clang/AST/Stmt.h"
@@ -70,7 +71,6 @@
7071
#include "llvm/ADT/StringRef.h"
7172
#include "llvm/Support/Casting.h"
7273
#include "llvm/Support/Error.h"
73-
#include "llvm/Support/raw_os_ostream.h"
7474
#include <optional>
7575

7676
namespace clang {
@@ -95,18 +95,21 @@ enum FunctionDeclKind {
9595
OutOfLineDefinition
9696
};
9797

98-
// A RootStmt is a statement that's fully selected including all it's children
99-
// and it's parent is unselected.
98+
// A RootStmt is a statement that's fully selected including all its children
99+
// and its parent is unselected.
100100
// Check if a node is a root statement.
101101
bool isRootStmt(const Node *N) {
102102
if (!N->ASTNode.get<Stmt>())
103103
return false;
104104
// Root statement cannot be partially selected.
105105
if (N->Selected == SelectionTree::Partial)
106106
return false;
107-
// Only DeclStmt can be an unselected RootStmt since VarDecls claim the entire
108-
// selection range in selectionTree.
109-
if (N->Selected == SelectionTree::Unselected && !N->ASTNode.get<DeclStmt>())
107+
// A DeclStmt can be an unselected RootStmt since VarDecls claim the entire
108+
// selection range in selectionTree. Additionally, a CXXOperatorCallExpr of a
109+
// binary operation can be unselected because its children claim the entire
110+
// selection range in the selection tree (e.g. <<).
111+
if (N->Selected == SelectionTree::Unselected && !N->ASTNode.get<DeclStmt>() &&
112+
!N->ASTNode.get<CXXOperatorCallExpr>())
110113
return false;
111114
return true;
112115
}
@@ -913,8 +916,8 @@ Expected<Tweak::Effect> ExtractFunction::apply(const Selection &Inputs) {
913916

914917
tooling::Replacements OtherEdit(
915918
createForwardDeclaration(*ExtractedFunc, SM));
916-
if (auto PathAndEdit = Tweak::Effect::fileEdit(SM, SM.getFileID(*FwdLoc),
917-
OtherEdit))
919+
if (auto PathAndEdit =
920+
Tweak::Effect::fileEdit(SM, SM.getFileID(*FwdLoc), OtherEdit))
918921
MultiFileEffect->ApplyEdits.try_emplace(PathAndEdit->first,
919922
PathAndEdit->second);
920923
else

clang-tools-extra/clangd/unittests/tweaks/ExtractFunctionTests.cpp

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -190,6 +190,18 @@ F (extracted();)
190190
}]]
191191
)cpp";
192192
EXPECT_EQ(apply(CompoundFailInput), "unavailable");
193+
194+
ExtraArgs.push_back("-std=c++14");
195+
// FIXME: Expressions are currently not extracted
196+
EXPECT_EQ(apply(R"cpp(
197+
void call() { [[1+1]]; }
198+
)cpp"),
199+
"unavailable");
200+
// FIXME: Single expression statements are currently not extracted
201+
EXPECT_EQ(apply(R"cpp(
202+
void call() { [[1+1;]] }
203+
)cpp"),
204+
"unavailable");
193205
}
194206

195207
TEST_F(ExtractFunctionTest, DifferentHeaderSourceTest) {
@@ -571,6 +583,53 @@ int getNum(bool Superstitious, int Min, int Max) {
571583
EXPECT_EQ(apply(Before), After);
572584
}
573585

586+
TEST_F(ExtractFunctionTest, OverloadedOperators) {
587+
Context = File;
588+
std::string Before = R"cpp(struct A {
589+
int operator+(int x) { return x; }
590+
};
591+
A &operator<<(A &, int);
592+
A &operator|(A &, int);
593+
594+
A stream{};
595+
596+
void foo(int, int);
597+
598+
int main() {
599+
[[foo(1, 2);
600+
foo(3, 4);
601+
stream << 42;
602+
stream + 42;
603+
stream | 42;
604+
foo(1, 2);
605+
foo(3, 4);]]
606+
})cpp";
607+
std::string After =
608+
R"cpp(struct A {
609+
int operator+(int x) { return x; }
610+
};
611+
A &operator<<(A &, int);
612+
A &operator|(A &, int);
613+
614+
A stream{};
615+
616+
void foo(int, int);
617+
618+
void extracted() {
619+
foo(1, 2);
620+
foo(3, 4);
621+
stream << 42;
622+
stream + 42;
623+
stream | 42;
624+
foo(1, 2);
625+
foo(3, 4);
626+
}
627+
int main() {
628+
extracted();
629+
})cpp";
630+
EXPECT_EQ(apply(Before), After);
631+
}
632+
574633
} // namespace
575634
} // namespace clangd
576635
} // namespace clang

clang-tools-extra/docs/ReleaseNotes.rst

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,9 @@ Code actions
7878

7979
- Added `Swap operands` tweak for certain binary operators.
8080

81+
- Improved the extract-to-function code action to allow extracting statements
82+
with overloaded operators like ``<<`` of ``std::ostream``.
83+
8184
Signature help
8285
^^^^^^^^^^^^^^
8386

0 commit comments

Comments
 (0)