llvm-project
llvm-project copied to clipboard
[clangd] fix extract-to-function for overloaded operators
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
CXXOperatorCallExprs valid root statements, just like DeclStmts.
Fixes clangd/clangd#1254
@llvm/pr-subscribers-clang-tools-extra
@llvm/pr-subscribers-clangd
Author: Julian Schmidt (5chmidti)
Changes
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
CXXOperatorCallExprs valid root statements, just like DeclStmts.
Fixes clangd/clangd#1254
Full diff: https://github.com/llvm/llvm-project/pull/81640.diff
3 Files Affected:
- (modified) clang-tools-extra/clangd/refactor/tweaks/ExtractFunction.cpp (+9-6)
- (modified) clang-tools-extra/clangd/unittests/tweaks/ExtractFunctionTests.cpp (+47)
- (modified) clang-tools-extra/docs/ReleaseNotes.rst (+3)
diff --git a/clang-tools-extra/clangd/refactor/tweaks/ExtractFunction.cpp b/clang-tools-extra/clangd/refactor/tweaks/ExtractFunction.cpp
index 0302839c58252e..aae480175b33f6 100644
--- a/clang-tools-extra/clangd/refactor/tweaks/ExtractFunction.cpp
+++ b/clang-tools-extra/clangd/refactor/tweaks/ExtractFunction.cpp
@@ -56,6 +56,7 @@
#include "clang/AST/ASTContext.h"
#include "clang/AST/Decl.h"
#include "clang/AST/DeclBase.h"
+#include "clang/AST/ExprCXX.h"
#include "clang/AST/NestedNameSpecifier.h"
#include "clang/AST/RecursiveASTVisitor.h"
#include "clang/AST/Stmt.h"
@@ -70,7 +71,6 @@
#include "llvm/ADT/StringRef.h"
#include "llvm/Support/Casting.h"
#include "llvm/Support/Error.h"
-#include "llvm/Support/raw_os_ostream.h"
#include <optional>
namespace clang {
@@ -104,9 +104,12 @@ bool isRootStmt(const Node *N) {
// Root statement cannot be partially selected.
if (N->Selected == SelectionTree::Partial)
return false;
- // Only DeclStmt can be an unselected RootStmt since VarDecls claim the entire
- // selection range in selectionTree.
- if (N->Selected == SelectionTree::Unselected && !N->ASTNode.get<DeclStmt>())
+ // A DeclStmt can be an unselected RootStmt since VarDecls claim the entire
+ // selection range in selectionTree. Additionally, an CXXOperatorCallExpr of a
+ // binary operation can be unselected because it's children claim the entire
+ // selection range in the selection tree (e.g. <<).
+ if (N->Selected == SelectionTree::Unselected && !N->ASTNode.get<DeclStmt>() &&
+ !N->ASTNode.get<CXXOperatorCallExpr>())
return false;
return true;
}
@@ -913,8 +916,8 @@ Expected<Tweak::Effect> ExtractFunction::apply(const Selection &Inputs) {
tooling::Replacements OtherEdit(
createForwardDeclaration(*ExtractedFunc, SM));
- if (auto PathAndEdit = Tweak::Effect::fileEdit(SM, SM.getFileID(*FwdLoc),
- OtherEdit))
+ if (auto PathAndEdit =
+ Tweak::Effect::fileEdit(SM, SM.getFileID(*FwdLoc), OtherEdit))
MultiFileEffect->ApplyEdits.try_emplace(PathAndEdit->first,
PathAndEdit->second);
else
diff --git a/clang-tools-extra/clangd/unittests/tweaks/ExtractFunctionTests.cpp b/clang-tools-extra/clangd/unittests/tweaks/ExtractFunctionTests.cpp
index dec63d454d52c6..8e347b516c6ffe 100644
--- a/clang-tools-extra/clangd/unittests/tweaks/ExtractFunctionTests.cpp
+++ b/clang-tools-extra/clangd/unittests/tweaks/ExtractFunctionTests.cpp
@@ -571,6 +571,53 @@ int getNum(bool Superstitious, int Min, int Max) {
EXPECT_EQ(apply(Before), After);
}
+TEST_F(ExtractFunctionTest, OverloadedOperators) {
+ Context = File;
+ std::string Before = R"cpp(struct A {
+ int operator+(int x) { return x; }
+ };
+ A &operator<<(A &, int);
+ A &operator|(A &, int);
+
+ A stream{};
+
+ void foo(int, int);
+
+ int main() {
+ [[foo(1, 2);
+ foo(3, 4);
+ stream << 42;
+ stream + 42;
+ stream | 42;
+ foo(1, 2);
+ foo(3, 4);]]
+ })cpp";
+ std::string After =
+ R"cpp(struct A {
+ int operator+(int x) { return x; }
+ };
+ A &operator<<(A &, int);
+ A &operator|(A &, int);
+
+ A stream{};
+
+ void foo(int, int);
+
+ void extracted() {
+foo(1, 2);
+ foo(3, 4);
+ stream << 42;
+ stream + 42;
+ stream | 42;
+ foo(1, 2);
+ foo(3, 4);
+}
+int main() {
+ extracted();
+ })cpp";
+ EXPECT_EQ(apply(Before), After);
+}
+
} // namespace
} // namespace clangd
} // namespace clang
diff --git a/clang-tools-extra/docs/ReleaseNotes.rst b/clang-tools-extra/docs/ReleaseNotes.rst
index f2fba9aa1450d6..01cd2278c1fb18 100644
--- a/clang-tools-extra/docs/ReleaseNotes.rst
+++ b/clang-tools-extra/docs/ReleaseNotes.rst
@@ -69,6 +69,9 @@ Code completion
Code actions
^^^^^^^^^^^^
+- Improved the extract-to-function code action to allow extracting statements
+ with overloaded operators like ``<<`` of ``std::ostream``.
+
Signature help
^^^^^^^^^^^^^^
Ping
Rebase & Ping
:white_check_mark: With the latest revision this PR passed the C/C++ code formatter.
:white_check_mark: With the latest revision this PR passed the Python code formatter.
Would be great to see this merged. This bug prevents basic function extractions from working.
I can confirm this builds and fixes the issue. For example, in:
#include <iostream>
#include <ostream>
int main() {
int a = 5;
int b = 7;
int c;
c = a+b;
std::cout << a+b << std::endl;
return 0;
}
Without this patch, there is no refactor available for:
c = a+b;
std::cout << a+b << std::endl
With the patch, it can be extracted
Needs rebase and another typo fix. Looks good to me otherwise.
@5chmidti I'm excited for this to be mainlined. Give me a thumbs up if you want me to fix the typo and rebase for you -- happy to do it this weekend.
I'm a bit of a outsider to the LLVM project, but I'd say a fix to a broken, already shipped, feature now is better than a general fix down the line.
On Sat, Oct 26, 2024, 3:23 AM Julian Schmidt @.***> wrote:
@.**** commented on this pull request.
CC @HighCommander4 https://github.com/HighCommander4 @kadircet https://github.com/kadircet
Technically, Sam told me last EuroLLVM that he had some refactor of the SelectionTree that would resolve issues like this in the general case, but as it is unclear that those changes will get in someday/in the 'near' future, I think that this PR makes sense to merge. Even though this is kind of a band-aid fix. Thoughts?
In clang-tools-extra/clangd/refactor/tweaks/ExtractFunction.cpp https://github.com/llvm/llvm-project/pull/81640#discussion_r1817768904:
- // selection range in selectionTree. Additionally, a CXXOperatorCallExpr of a
- // binary operation can be unselected because it's children claim the entire
⬇️ Suggested change
- // selection range in selectionTree. Additionally, a CXXOperatorCallExpr of a
- // binary operation can be unselected because it's children claim the entire
- // selection range in selectionTree. Additionally, a CXXOperatorCallExpr of a
- // binary operation can be unselected because its children claim the entire
— Reply to this email directly, view it on GitHub https://github.com/llvm/llvm-project/pull/81640#pullrequestreview-2397141474, or unsubscribe https://github.com/notifications/unsubscribe-auth/AA3J4MJEX4VEB24H7HVXXWTZ5NURZAVCNFSM6AAAAABDG6LIWCVHI2DSMVQWIX3LMV43YUDVNRWFEZLROVSXG5CSMV3GSZLXHMZDGOJXGE2DCNBXGQ . You are receiving this because you commented.Message ID: @.***>
Do I understand correctly that this is a partial fix for https://github.com/clangd/clangd/issues/1254 in that it addresses the issue with overloaded operators in particular, but it still leaves in place the limitation that a single expression statement (of any kind) cannot be extracted?
If so, would you mind adding an "unavailable" test case for the latter case, with some sort of "TODO, we'd like to relax this limitation" comment?
Do I understand correctly that this is a partial fix for clangd/clangd#1254 in that it addresses the issue with overloaded operators in particular, but it still leaves in place the limitation that a single expression statement (of any kind) cannot be extracted?
Exactly
If so, would you mind adding an "unavailable" test case for the latter case, with some sort of "TODO, we'd like to relax this limitation" comment?
Done
Thanks for your work on this, Julian! I'm excited to have this tool work properly!