@@ -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
@@ -423,8 +427,6 @@ bool childExprIsStmt(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.
@@ -516,6 +518,12 @@ bool eligibleForExtraction(const SelectionTree::Node *N) {
516518 return false ;
517519 }
518520
521+ // If e.g. a capture clause was selected, the target node is the lambda
522+ // expression. We only want to offer the extraction if the entire lambda
523+ // expression was selected.
524+ if (llvm::isa<LambdaExpr>(E))
525+ return N->Selected == SelectionTree::Complete;
526+
519527 // The same logic as for assignments applies to initializations.
520528 // However, we do allow extracting the RHS of an init capture, as it is
521529 // 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) {
599607 // FIXME: get variable name from user or suggest based on type
600608 std::string VarName = " placeholder" ;
601609 SourceRange Range = Target->getExtractionChars ();
610+
611+ const SelectionTree::Node &OuterImplicit =
612+ Target->getExprNode ()->outerImplicit ();
613+ assert (OuterImplicit.Parent );
614+ bool IsStmtExpr = llvm::isa_and_nonnull<CompoundStmt>(
615+ OuterImplicit.Parent ->ASTNode .get <Stmt>());
616+
602617 // insert new variable declaration
603- if (auto Err = Result.add (Target->insertDeclaration (VarName, Range)))
618+ if (auto Err =
619+ Result.add (Target->insertDeclaration (VarName, Range, !IsStmtExpr)))
604620 return std::move (Err);
605- // replace expression with variable name
621+
622+ // replace expression with variable name, unless it's a statement expression,
623+ // in which case we remove it.
624+ if (IsStmtExpr)
625+ VarName.clear ();
606626 if (auto Err = Result.add (Target->replaceWithVar (Range, VarName)))
607627 return std::move (Err);
608628 return Effect::mainFileEdit (Inputs.AST ->getSourceManager (),
0 commit comments