@@ -49,7 +49,8 @@ class ExtractionContext {
49
49
llvm::StringRef VarName) const ;
50
50
// Generate Replacement for declaring the selected Expr as a new variable
51
51
tooling::Replacement insertDeclaration (llvm::StringRef VarName,
52
- SourceRange InitChars) const ;
52
+ SourceRange InitChars,
53
+ bool AddSemicolon) const ;
53
54
54
55
private:
55
56
bool Extractable = false ;
@@ -252,15 +253,18 @@ ExtractionContext::replaceWithVar(SourceRange Chars,
252
253
// returns the Replacement for declaring a new variable storing the extraction
253
254
tooling::Replacement
254
255
ExtractionContext::insertDeclaration (llvm::StringRef VarName,
255
- SourceRange InitializerChars) const {
256
+ SourceRange InitializerChars,
257
+ bool AddSemicolon) const {
256
258
llvm::StringRef ExtractionCode = toSourceCode (SM, InitializerChars);
257
259
const SourceLocation InsertionLoc =
258
260
toHalfOpenFileRange (SM, Ctx.getLangOpts (),
259
261
InsertionPoint->getSourceRange ())
260
262
->getBegin ();
261
263
std::string ExtractedVarDecl =
262
264
printType (VarType, ExprNode->getDeclContext (), VarName) + " = " +
263
- ExtractionCode.str () + " ; " ;
265
+ ExtractionCode.str ();
266
+ if (AddSemicolon)
267
+ ExtractedVarDecl += " ; " ;
264
268
return tooling::Replacement (SM, InsertionLoc, 0 , ExtractedVarDecl);
265
269
}
266
270
@@ -419,12 +423,10 @@ const SelectionTree::Node *getCallExpr(const SelectionTree::Node *DeclRef) {
419
423
420
424
// Returns true if Inner (which is a direct child of Outer) is appearing as
421
425
// a statement rather than an expression whose value can be used.
422
- bool childExprIsStmt (const Stmt *Outer, const Expr *Inner) {
426
+ bool childExprIsDisallowedStmt (const Stmt *Outer, const Expr *Inner) {
423
427
if (!Outer || !Inner)
424
428
return false ;
425
429
// Exclude the most common places where an expr can appear but be unused.
426
- if (llvm::isa<CompoundStmt>(Outer))
427
- return true ;
428
430
if (llvm::isa<SwitchCase>(Outer))
429
431
return true ;
430
432
// Control flow statements use condition etc, but not the body.
@@ -476,12 +478,9 @@ bool eligibleForExtraction(const SelectionTree::Node *N) {
476
478
const auto *Parent = OuterImplicit.Parent ;
477
479
if (!Parent)
478
480
return false ;
479
- // We don't want to extract expressions used as statements, that would leave
480
- // a `placeholder;` around that has no effect.
481
- // Unfortunately because the AST doesn't have ExprStmt, we have to check in
482
- // this roundabout way.
483
- if (childExprIsStmt (Parent->ASTNode .get <Stmt>(),
484
- OuterImplicit.ASTNode .get <Expr>()))
481
+ // Filter non-applicable expression statements.
482
+ if (childExprIsDisallowedStmt (Parent->ASTNode .get <Stmt>(),
483
+ OuterImplicit.ASTNode .get <Expr>()))
485
484
return false ;
486
485
487
486
std::function<bool (const SelectionTree::Node *)> IsFullySelected =
@@ -516,6 +515,12 @@ bool eligibleForExtraction(const SelectionTree::Node *N) {
516
515
return false ;
517
516
}
518
517
518
+ // If e.g. a capture clause was selected, the target node is the lambda
519
+ // expression. We only want to offer the extraction if the entire lambda
520
+ // expression was selected.
521
+ if (llvm::isa<LambdaExpr>(E))
522
+ return N->Selected == SelectionTree::Complete;
523
+
519
524
// The same logic as for assignments applies to initializations.
520
525
// However, we do allow extracting the RHS of an init capture, as it is
521
526
// a valid use case to move non-trivial expressions out of the capture clause.
@@ -599,10 +604,24 @@ Expected<Tweak::Effect> ExtractVariable::apply(const Selection &Inputs) {
599
604
// FIXME: get variable name from user or suggest based on type
600
605
std::string VarName = " placeholder" ;
601
606
SourceRange Range = Target->getExtractionChars ();
602
- // insert new variable declaration
603
- if (auto Err = Result.add (Target->insertDeclaration (VarName, Range)))
607
+
608
+ const SelectionTree::Node &OuterImplicit =
609
+ Target->getExprNode ()->outerImplicit ();
610
+ assert (OuterImplicit.Parent );
611
+ bool IsExprStmt = llvm::isa_and_nonnull<CompoundStmt>(
612
+ OuterImplicit.Parent ->ASTNode .get <Stmt>());
613
+
614
+ // insert new variable declaration. add a semicolon if and only if
615
+ // we are not dealing with an expression statement, which already has
616
+ // a semicolon that stays where it is, as it's not part of the range.
617
+ if (auto Err =
618
+ Result.add (Target->insertDeclaration (VarName, Range, !IsExprStmt)))
604
619
return std::move (Err);
605
- // replace expression with variable name
620
+
621
+ // replace expression with variable name, unless it's an expression statement,
622
+ // in which case we remove it.
623
+ if (IsExprStmt)
624
+ VarName.clear ();
606
625
if (auto Err = Result.add (Target->replaceWithVar (Range, VarName)))
607
626
return std::move (Err);
608
627
return Effect::mainFileEdit (Inputs.AST ->getSourceManager (),
0 commit comments