-
Notifications
You must be signed in to change notification settings - Fork 15.3k
[clangd] fix extract-to-function for overloaded operators #81640
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[clangd] fix extract-to-function for overloaded operators #81640
Conversation
|
@llvm/pr-subscribers-clang-tools-extra @llvm/pr-subscribers-clangd Author: Julian Schmidt (5chmidti) ChangesWhen selecting code that contains the use of overloaded operators, Fixes clangd/clangd#1254 Full diff: https://github.com/llvm/llvm-project/pull/81640.diff 3 Files Affected:
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 |
6efc30e to
6b62ed1
Compare
|
Rebase & Ping |
|
✅ With the latest revision this PR passed the C/C++ code formatter. |
|
✅ 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: Without this patch, there is no refactor available for: 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. |
5chmidti
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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?
|
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
<#81640 (comment)>:
> + // 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
<#81640 (review)>,
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 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? 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? |
9d67aa7 to
a8f90e7
Compare
Exactly
Done |
|
Thanks for your work on this, Julian! I'm excited to have this tool work properly! |
| // selection range in selectionTree. Additionally, a 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>() && |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we need to make a corresponding change here? The comment suggests that it's special-casing DeclStmt for the same reason as this code.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think that would have any effect, because to hit this case, the common ancestor of our root statements needs to be completely selected, but the CXXOperatorCallExpr will be unselected as shown in this if here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I guess the difference is that a DeclStmt can have a single VarDecl child which could be completely selected, leaving the DeclStmt unselected. There is no analogous situation for a CXXOperatorCallExpr which has multiple children.
| // FIXME: Expressions are currently not extracted | ||
| EXPECT_EQ(apply(R"cpp( | ||
| void sink(int); | ||
| void call() { sink([[1+1]]); } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I was thinking more specifically about entire expression-statements, e.g. [[stream << 42;]].
Extraction of arbitrary subexpressions would be nice as well, but likely more involved; there is a work in progress patch here.
(We can keep this test case too.)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've added another testcase for extracting a single expression
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
a8f90e7 to
38003e3
Compare
HighCommander4
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks. +1 to taking this targeted fix without waiting for a larger refactor.
| // selection range in selectionTree. Additionally, a 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>() && |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I guess the difference is that a DeclStmt can have a single VarDecl child which could be completely selected, leaving the DeclStmt unselected. There is no analogous situation for a CXXOperatorCallExpr which has multiple children.
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 allowextract-to-function to work with these operators, make unselected
CXXOperatorCallExprs valid root statements, just likeDeclStmts.Partially fixes clangd/clangd#1254