Skip to content

Commit c9974ae

Browse files
authored
[clangd] Do not offer extraction to variable for decl init expression (#69477)
That would turn: int x = f() + 1; into: auto placeholder = f() + 1; int x = placeholder; which makes little sense and is clearly not intended, as stated explicitly by a comment in eligibleForExtraction(). It appears that the declaration case was simply forgotten (the assignment case was already implemented).
1 parent f668a08 commit c9974ae

File tree

2 files changed

+55
-16
lines changed

2 files changed

+55
-16
lines changed

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

Lines changed: 40 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -468,7 +468,8 @@ bool eligibleForExtraction(const SelectionTree::Node *N) {
468468
// Extracting Exprs like a = 1 gives placeholder = a = 1 which isn't useful.
469469
// FIXME: we could still hoist the assignment, and leave the variable there?
470470
ParsedBinaryOperator BinOp;
471-
if (BinOp.parse(*N) && BinaryOperator::isAssignmentOp(BinOp.Kind))
471+
bool IsBinOp = BinOp.parse(*N);
472+
if (IsBinOp && BinaryOperator::isAssignmentOp(BinOp.Kind))
472473
return false;
473474

474475
const SelectionTree::Node &OuterImplicit = N->outerImplicit();
@@ -483,13 +484,48 @@ bool eligibleForExtraction(const SelectionTree::Node *N) {
483484
OuterImplicit.ASTNode.get<Expr>()))
484485
return false;
485486

487+
std::function<bool(const SelectionTree::Node *)> IsFullySelected =
488+
[&](const SelectionTree::Node *N) {
489+
if (N->ASTNode.getSourceRange().isValid() &&
490+
N->Selected != SelectionTree::Complete)
491+
return false;
492+
for (const auto *Child : N->Children) {
493+
if (!IsFullySelected(Child))
494+
return false;
495+
}
496+
return true;
497+
};
498+
auto ExprIsFullySelectedTargetNode = [&](const Expr *E) {
499+
if (E != OuterImplicit.ASTNode.get<Expr>())
500+
return false;
501+
502+
// The above condition is the only relevant one except for binary operators.
503+
// Without the following code, we would fail to offer extraction for e.g.:
504+
// int x = 1 + 2 + [[3 + 4 + 5]];
505+
// See the documentation of ParsedBinaryOperator for further details.
506+
if (!IsBinOp)
507+
return true;
508+
return IsFullySelected(N);
509+
};
510+
486511
// Disable extraction of full RHS on assignment operations, e.g:
487-
// auto x = [[RHS_EXPR]];
512+
// x = [[RHS_EXPR]];
488513
// This would just result in duplicating the code.
489514
if (const auto *BO = Parent->ASTNode.get<BinaryOperator>()) {
490-
if (BO->isAssignmentOp() &&
491-
BO->getRHS() == OuterImplicit.ASTNode.get<Expr>())
515+
if (BO->isAssignmentOp() && ExprIsFullySelectedTargetNode(BO->getRHS()))
516+
return false;
517+
}
518+
519+
// The same logic as for assignments applies to initializations.
520+
// However, we do allow extracting the RHS of an init capture, as it is
521+
// a valid use case to move non-trivial expressions out of the capture clause.
522+
// FIXME: In that case, the extracted variable should be captured directly,
523+
// rather than an explicit copy.
524+
if (const auto *Decl = Parent->ASTNode.get<VarDecl>()) {
525+
if (!Decl->isInitCapture() &&
526+
ExprIsFullySelectedTargetNode(Decl->getInit())) {
492527
return false;
528+
}
493529
}
494530

495531
return true;

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

Lines changed: 15 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -27,10 +27,10 @@ TEST_F(ExtractVariableTest, Test) {
2727
return [[[[t.b[[a]]r]]([[t.z]])]];
2828
}
2929
void f() {
30-
int a = [[5 +]] [[4 * [[[[xyz]]()]]]];
30+
int a = 5 + [[4 * [[[[xyz]]()]]]];
3131
// multivariable initialization
3232
if(1)
33-
int x = [[1]], y = [[a + 1]], a = [[1]], z = a + 1;
33+
int x = [[1]] + 1, y = a + [[1]], a = [[1]] + 2, z = a + 1;
3434
// if without else
3535
if([[1]])
3636
a = [[1]] + 1;
@@ -61,7 +61,7 @@ TEST_F(ExtractVariableTest, Test) {
6161
ExtraArgs = {"-xc"};
6262
const char *AvailableC = R"cpp(
6363
void foo() {
64-
int x = [[1]];
64+
int x = [[1]] + 1;
6565
})cpp";
6666
EXPECT_AVAILABLE(AvailableC);
6767

@@ -79,7 +79,7 @@ TEST_F(ExtractVariableTest, Test) {
7979
@end
8080
@implementation Foo
8181
- (void)method {
82-
int x = [[1 + 2]];
82+
int x = [[1]] + 2;
8383
}
8484
@end)cpp";
8585
EXPECT_AVAILABLE(AvailableObjC);
@@ -103,6 +103,9 @@ TEST_F(ExtractVariableTest, Test) {
103103
}
104104
int z = [[1]];
105105
} t;
106+
int x = [[1 + 2]];
107+
int y;
108+
y = [[1 + 2]];
106109
return [[t]].bar([[t]].z);
107110
}
108111
void v() { return; }
@@ -430,8 +433,8 @@ TEST_F(ExtractVariableTest, Test) {
430433
int member = 42;
431434
};
432435
)cpp"},
433-
{R"cpp(void f() { auto x = [[ [](){ return 42; }]]; })cpp",
434-
R"cpp(void f() { auto placeholder = [](){ return 42; }; auto x = placeholder; })cpp"},
436+
{R"cpp(void f() { auto x = +[[ [](){ return 42; }]]; })cpp",
437+
R"cpp(void f() { auto placeholder = [](){ return 42; }; auto x = + placeholder; })cpp"},
435438
{R"cpp(
436439
template <typename T>
437440
auto sink(T f) { return f(); }
@@ -515,13 +518,13 @@ TEST_F(ExtractVariableTest, Test) {
515518
{R"cpp(
516519
template <typename ...Ts>
517520
void foo(Ts ...args) {
518-
auto x = [[ [&args...]() {} ]];
521+
auto x = +[[ [&args...]() {} ]];
519522
}
520523
)cpp",
521524
R"cpp(
522525
template <typename ...Ts>
523526
void foo(Ts ...args) {
524-
auto placeholder = [&args...]() {}; auto x = placeholder ;
527+
auto placeholder = [&args...]() {}; auto x = + placeholder ;
525528
}
526529
)cpp"},
527530
{R"cpp(
@@ -533,7 +536,7 @@ TEST_F(ExtractVariableTest, Test) {
533536
int main() {
534537
Coordinates c = {};
535538
const auto [x, y] = c;
536-
auto f = [[ [&]() { return x + y; } ]];
539+
auto f = [[ [&]() { return x + y; } ]]();
537540
}
538541
)cpp",
539542
R"cpp(
@@ -545,7 +548,7 @@ TEST_F(ExtractVariableTest, Test) {
545548
int main() {
546549
Coordinates c = {};
547550
const auto [x, y] = c;
548-
auto placeholder = [&]() { return x + y; }; auto f = placeholder ;
551+
auto placeholder = [&]() { return x + y; }; auto f = placeholder ();
549552
}
550553
)cpp"},
551554
{R"cpp(
@@ -557,7 +560,7 @@ TEST_F(ExtractVariableTest, Test) {
557560
int main() {
558561
Coordinates c = {};
559562
if (const auto [x, y] = c; x > y) {
560-
auto f = [[ [&]() { return x + y; } ]];
563+
auto f = [[ [&]() { return x + y; } ]]();
561564
}
562565
}
563566
)cpp",
@@ -570,7 +573,7 @@ TEST_F(ExtractVariableTest, Test) {
570573
int main() {
571574
Coordinates c = {};
572575
if (const auto [x, y] = c; x > y) {
573-
auto placeholder = [&]() { return x + y; }; auto f = placeholder ;
576+
auto placeholder = [&]() { return x + y; }; auto f = placeholder ();
574577
}
575578
}
576579
)cpp"},

0 commit comments

Comments
 (0)