@@ -49,7 +49,8 @@ class ExtractionContext {
4949 llvm::StringRef VarName) const ;
5050 // Generate Replacement for declaring the selected Expr as a new variable
5151 tooling::Replacement insertDeclaration (llvm::StringRef VarName,
52- SourceRange InitChars) const ;
52+ SourceRange InitChars,
53+ bool AddSemicolon) const ;
5354
5455private:
5556 bool Extractable = false ;
@@ -252,15 +253,18 @@ ExtractionContext::replaceWithVar(SourceRange Chars,
252253// returns the Replacement for declaring a new variable storing the extraction
253254tooling::Replacement
254255ExtractionContext::insertDeclaration (llvm::StringRef VarName,
255- SourceRange InitializerChars) const {
256+ SourceRange InitializerChars,
257+ bool AddSemicolon) const {
256258 llvm::StringRef ExtractionCode = toSourceCode (SM, InitializerChars);
257259 const SourceLocation InsertionLoc =
258260 toHalfOpenFileRange (SM, Ctx.getLangOpts (),
259261 InsertionPoint->getSourceRange ())
260262 ->getBegin ();
261263 std::string ExtractedVarDecl =
262264 printType (VarType, ExprNode->getDeclContext (), VarName) + " = " +
263- ExtractionCode.str () + " ; " ;
265+ ExtractionCode.str ();
266+ if (AddSemicolon)
267+ ExtractedVarDecl += " ; " ;
264268 return tooling::Replacement (SM, InsertionLoc, 0 , ExtractedVarDecl);
265269}
266270
@@ -419,12 +423,10 @@ const SelectionTree::Node *getCallExpr(const SelectionTree::Node *DeclRef) {
419423
420424// Returns true if Inner (which is a direct child of Outer) is appearing as
421425// 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) {
423427 if (!Outer || !Inner)
424428 return false ;
425429 // Exclude the most common places where an expr can appear but be unused.
426- if (llvm::isa<CompoundStmt>(Outer))
427- return true ;
428430 if (llvm::isa<SwitchCase>(Outer))
429431 return true ;
430432 // Control flow statements use condition etc, but not the body.
@@ -476,12 +478,9 @@ bool eligibleForExtraction(const SelectionTree::Node *N) {
476478 const auto *Parent = OuterImplicit.Parent ;
477479 if (!Parent)
478480 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>()))
485484 return false ;
486485
487486 std::function<bool (const SelectionTree::Node *)> IsFullySelected =
@@ -516,6 +515,12 @@ bool eligibleForExtraction(const SelectionTree::Node *N) {
516515 return false ;
517516 }
518517
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+
519524 // The same logic as for assignments applies to initializations.
520525 // However, we do allow extracting the RHS of an init capture, as it is
521526 // 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) {
599604 // FIXME: get variable name from user or suggest based on type
600605 std::string VarName = " placeholder" ;
601606 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)))
604619 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 ();
606625 if (auto Err = Result.add (Target->replaceWithVar (Range, VarName)))
607626 return std::move (Err);
608627 return Effect::mainFileEdit (Inputs.AST ->getSourceManager (),
0 commit comments