-
Notifications
You must be signed in to change notification settings - Fork 13.6k
[clangd] Consider expression statements in ExtractVariable tweak #112525
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
Conversation
@llvm/pr-subscribers-clangd @llvm/pr-subscribers-clang-tools-extra Author: Christian Kandeler (ckandeler) ChangesFor instance: Full diff: https://github.com/llvm/llvm-project/pull/112525.diff 2 Files Affected:
diff --git a/clang-tools-extra/clangd/refactor/tweaks/ExtractVariable.cpp b/clang-tools-extra/clangd/refactor/tweaks/ExtractVariable.cpp
index 3b378153eafd52..77eeb3df4773f2 100644
--- a/clang-tools-extra/clangd/refactor/tweaks/ExtractVariable.cpp
+++ b/clang-tools-extra/clangd/refactor/tweaks/ExtractVariable.cpp
@@ -49,7 +49,8 @@ class ExtractionContext {
llvm::StringRef VarName) const;
// Generate Replacement for declaring the selected Expr as a new variable
tooling::Replacement insertDeclaration(llvm::StringRef VarName,
- SourceRange InitChars) const;
+ SourceRange InitChars,
+ bool AddSemicolon) const;
private:
bool Extractable = false;
@@ -252,7 +253,8 @@ ExtractionContext::replaceWithVar(SourceRange Chars,
// returns the Replacement for declaring a new variable storing the extraction
tooling::Replacement
ExtractionContext::insertDeclaration(llvm::StringRef VarName,
- SourceRange InitializerChars) const {
+ SourceRange InitializerChars,
+ bool AddSemicolon) const {
llvm::StringRef ExtractionCode = toSourceCode(SM, InitializerChars);
const SourceLocation InsertionLoc =
toHalfOpenFileRange(SM, Ctx.getLangOpts(),
@@ -260,7 +262,9 @@ ExtractionContext::insertDeclaration(llvm::StringRef VarName,
->getBegin();
std::string ExtractedVarDecl =
printType(VarType, ExprNode->getDeclContext(), VarName) + " = " +
- ExtractionCode.str() + "; ";
+ ExtractionCode.str();
+ if (AddSemicolon)
+ ExtractedVarDecl += "; ";
return tooling::Replacement(SM, InsertionLoc, 0, ExtractedVarDecl);
}
@@ -423,8 +427,6 @@ bool childExprIsStmt(const Stmt *Outer, const Expr *Inner) {
if (!Outer || !Inner)
return false;
// Exclude the most common places where an expr can appear but be unused.
- if (llvm::isa<CompoundStmt>(Outer))
- return true;
if (llvm::isa<SwitchCase>(Outer))
return true;
// Control flow statements use condition etc, but not the body.
@@ -516,6 +518,12 @@ bool eligibleForExtraction(const SelectionTree::Node *N) {
return false;
}
+ // If e.g. a capture clause was selected, the target node is the lambda
+ // expression. We only want to offer the extraction if the entire lambda
+ // expression was selected.
+ if (llvm::isa<LambdaExpr>(E))
+ return N->Selected == SelectionTree::Complete;
+
// The same logic as for assignments applies to initializations.
// However, we do allow extracting the RHS of an init capture, as it is
// a valid use case to move non-trivial expressions out of the capture clause.
@@ -599,10 +607,22 @@ Expected<Tweak::Effect> ExtractVariable::apply(const Selection &Inputs) {
// FIXME: get variable name from user or suggest based on type
std::string VarName = "placeholder";
SourceRange Range = Target->getExtractionChars();
+
+ const SelectionTree::Node &OuterImplicit =
+ Target->getExprNode()->outerImplicit();
+ assert(OuterImplicit.Parent);
+ bool IsStmtExpr = llvm::isa_and_nonnull<CompoundStmt>(
+ OuterImplicit.Parent->ASTNode.get<Stmt>());
+
// insert new variable declaration
- if (auto Err = Result.add(Target->insertDeclaration(VarName, Range)))
+ if (auto Err =
+ Result.add(Target->insertDeclaration(VarName, Range, !IsStmtExpr)))
return std::move(Err);
- // replace expression with variable name
+
+ // replace expression with variable name, unless it's a statement expression,
+ // in which case we remove it.
+ if (IsStmtExpr)
+ VarName.clear();
if (auto Err = Result.add(Target->replaceWithVar(Range, VarName)))
return std::move(Err);
return Effect::mainFileEdit(Inputs.AST->getSourceManager(),
diff --git a/clang-tools-extra/clangd/unittests/tweaks/ExtractVariableTests.cpp b/clang-tools-extra/clangd/unittests/tweaks/ExtractVariableTests.cpp
index 656b62c9a1f4e1..c813f3ddee89e4 100644
--- a/clang-tools-extra/clangd/unittests/tweaks/ExtractVariableTests.cpp
+++ b/clang-tools-extra/clangd/unittests/tweaks/ExtractVariableTests.cpp
@@ -152,7 +152,7 @@ TEST_F(ExtractVariableTest, Test) {
a = [[b]];
a = [[xyz()]];
// statement expression
- [[xyz()]];
+ [[v()]];
while (a)
[[++a]];
// label statement
@@ -493,6 +493,16 @@ TEST_F(ExtractVariableTest, Test) {
a = a + 1;
}
})cpp"},
+ {R"cpp(
+ int func() { return 0; }
+ int main() {
+ [[func()]];
+ })cpp",
+ R"cpp(
+ int func() { return 0; }
+ int main() {
+ auto placeholder = func();
+ })cpp"},
{R"cpp(
template <typename T>
auto call(T t) { return t(); }
|
@ckandeler Could you elaborate on the motivation for this change? In my mind, the value proposition of the "extract variable" refactoring is that it saves you the work of moving the expression from one location to another, and typing the name of the variable twice (once at the declaration and once at the use). In the expression-statement case, the expression isn't moving to a new location, and the variable name is only mentioned in one place. In essence, the refactoring amounts to prepending |
My thinking was that because the expression is assignable and it's not clearly pointless to do so, the tweak should be available (plus it still saves some keystrokes). My concrete motivation was due to user requests on the client side, e.g.: |
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.
Ok, fair enough.
c1bf009
to
8310456
Compare
Rebased. |
For instance: int func(); int main() { func(); // => auto placeholder = func(); }
8310456
to
9993b14
Compare
Addressed review comments. |
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, LGTM
... in favor of clangd's "Extract Variable" tweak. As of llvm/llvm-project#112525, this functionality should be fully covered by clangd. Task-number: QTCREATORBUG-9363 Task-number: QTCREATORBUG-9578 Change-Id: I00ce996ef37b26152d93ed91fa5d1ea69e619618
... in favor of clangd's "Extract Variable" tweak. As of llvm/llvm-project#112525, this functionality should be fully covered by clangd. Task-number: QTCREATORBUG-9363 Task-number: QTCREATORBUG-9578 Change-Id: I00ce996ef37b26152d93ed91fa5d1ea69e619618 Reviewed-by: Christian Stenger <[email protected]>
For instance:
int func();
int main()
{
func(); // => auto placeholder = func();
}