634 lines · cpp
1//===--- ExtractVariable.cpp ------------------------------------*- C++-*-===//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===//8#include "AST.h"9#include "ParsedAST.h"10#include "Protocol.h"11#include "Selection.h"12#include "SourceCode.h"13#include "refactor/Tweak.h"14#include "clang/AST/ASTContext.h"15#include "clang/AST/Decl.h"16#include "clang/AST/DeclCXX.h"17#include "clang/AST/Expr.h"18#include "clang/AST/ExprCXX.h"19#include "clang/AST/LambdaCapture.h"20#include "clang/AST/OperationKinds.h"21#include "clang/AST/RecursiveASTVisitor.h"22#include "clang/AST/Stmt.h"23#include "clang/AST/StmtCXX.h"24#include "clang/Basic/LangOptions.h"25#include "clang/Basic/SourceLocation.h"26#include "clang/Basic/SourceManager.h"27#include "clang/Tooling/Core/Replacement.h"28#include "llvm/ADT/SmallVector.h"29#include "llvm/ADT/StringRef.h"30#include "llvm/Support/Casting.h"31#include "llvm/Support/Error.h"32#include "llvm/Support/raw_ostream.h"33 34namespace clang {35namespace clangd {36namespace {37// information regarding the Expr that is being extracted38class ExtractionContext {39public:40 ExtractionContext(const SelectionTree::Node *Node, const SourceManager &SM,41 const ASTContext &Ctx);42 const clang::Expr *getExpr() const { return Expr; }43 const SelectionTree::Node *getExprNode() const { return ExprNode; }44 bool isExtractable() const { return Extractable; }45 // The half-open range for the expression to be extracted.46 SourceRange getExtractionChars() const;47 // Generate Replacement for replacing selected expression with given VarName48 tooling::Replacement replaceWithVar(SourceRange Chars,49 llvm::StringRef VarName) const;50 // Generate Replacement for declaring the selected Expr as a new variable51 tooling::Replacement insertDeclaration(llvm::StringRef VarName,52 SourceRange InitChars,53 bool AddSemicolon) const;54 55private:56 bool Extractable = false;57 const clang::Expr *Expr;58 QualType VarType;59 const SelectionTree::Node *ExprNode;60 // Stmt before which we will extract61 const clang::Stmt *InsertionPoint = nullptr;62 const SourceManager &SM;63 const ASTContext &Ctx;64 // Decls referenced in the Expr65 std::vector<clang::Decl *> ReferencedDecls;66 // returns true if the Expr doesn't reference any variable declared in scope67 bool exprIsValidOutside(const clang::Stmt *Scope) const;68 // computes the Stmt before which we will extract out Expr69 const clang::Stmt *computeInsertionPoint() const;70};71 72// Returns all the Decls referenced inside the given Expr73static std::vector<clang::Decl *>74computeReferencedDecls(const clang::Expr *Expr) {75 // RAV subclass to find all DeclRefs in a given Stmt76 class FindDeclRefsVisitor77 : public clang::RecursiveASTVisitor<FindDeclRefsVisitor> {78 public:79 std::vector<Decl *> ReferencedDecls;80 bool VisitDeclRefExpr(DeclRefExpr *DeclRef) { // NOLINT81 // Stop the call operator of lambdas from being marked as a referenced82 // DeclRefExpr in immediately invoked lambdas.83 if (const auto *const Method =84 llvm::dyn_cast<CXXMethodDecl>(DeclRef->getDecl());85 Method != nullptr && Method->getParent()->isLambda()) {86 return true;87 }88 ReferencedDecls.push_back(DeclRef->getDecl());89 return true;90 }91 92 // Local variables declared inside of the selected lambda cannot go out of93 // scope. The DeclRefExprs that are important are the variables captured,94 // the DeclRefExprs inside the initializers of init-capture variables,95 // variables mentioned in trailing return types, constraints and explicit96 // defaulted template parameters.97 bool TraverseLambdaExpr(LambdaExpr *LExpr) {98 for (const auto &[Capture, Initializer] :99 llvm::zip(LExpr->captures(), LExpr->capture_inits())) {100 TraverseLambdaCapture(LExpr, &Capture, Initializer);101 }102 103 if (const clang::Expr *RequiresClause =104 LExpr->getTrailingRequiresClause().ConstraintExpr) {105 TraverseStmt(const_cast<clang::Expr *>(RequiresClause));106 }107 108 for (auto *const TemplateParam : LExpr->getExplicitTemplateParameters())109 TraverseDecl(TemplateParam);110 111 if (auto *const CallOperator = LExpr->getCallOperator()) {112 TraverseType(CallOperator->getDeclaredReturnType());113 114 for (auto *const Param : CallOperator->parameters()) {115 TraverseParmVarDecl(Param);116 }117 118 for (auto *const Attr : CallOperator->attrs()) {119 TraverseAttr(Attr);120 }121 }122 123 return true;124 }125 };126 127 FindDeclRefsVisitor Visitor;128 Visitor.TraverseStmt(const_cast<Stmt *>(cast<Stmt>(Expr)));129 return Visitor.ReferencedDecls;130}131 132static QualType computeVariableType(const Expr *Expr, const ASTContext &Ctx) {133 if (Ctx.getLangOpts().CPlusPlus11)134 return Ctx.getAutoDeductType();135 136 if (Expr->hasPlaceholderType(BuiltinType::PseudoObject)) {137 if (const auto *PR = dyn_cast<ObjCPropertyRefExpr>(Expr)) {138 if (PR->isMessagingSetter()) {139 // Don't support extracting a compound reference like `self.prop += 1`140 // since the meaning changes after extraction since we'll no longer call141 // the setter. Non compound access like `self.prop = 1` is invalid since142 // it returns nil (setter method must have a void return type).143 return QualType();144 } else if (PR->isMessagingGetter()) {145 if (PR->isExplicitProperty())146 return PR->getExplicitProperty()->getType();147 else148 return PR->getImplicitPropertyGetter()->getReturnType();149 }150 } else {151 return QualType();152 }153 }154 return Expr->getType();155}156 157ExtractionContext::ExtractionContext(const SelectionTree::Node *Node,158 const SourceManager &SM,159 const ASTContext &Ctx)160 : ExprNode(Node), SM(SM), Ctx(Ctx) {161 Expr = Node->ASTNode.get<clang::Expr>();162 ReferencedDecls = computeReferencedDecls(Expr);163 InsertionPoint = computeInsertionPoint();164 if (InsertionPoint)165 Extractable = true;166 VarType = computeVariableType(Expr, Ctx);167 if (VarType.isNull())168 Extractable = false;169 else170 // Strip the outer nullability since it's not common for local variables.171 AttributedType::stripOuterNullability(VarType);172}173 174// checks whether extracting before InsertionPoint will take a175// variable reference out of scope176bool ExtractionContext::exprIsValidOutside(const clang::Stmt *Scope) const {177 SourceLocation ScopeBegin = Scope->getBeginLoc();178 SourceLocation ScopeEnd = Scope->getEndLoc();179 for (const Decl *ReferencedDecl : ReferencedDecls) {180 if (ReferencedDecl->getBeginLoc().isValid() &&181 SM.isPointWithin(ReferencedDecl->getBeginLoc(), ScopeBegin, ScopeEnd) &&182 SM.isPointWithin(ReferencedDecl->getEndLoc(), ScopeBegin, ScopeEnd))183 return false;184 }185 return true;186}187 188// Return the Stmt before which we need to insert the extraction.189// To find the Stmt, we go up the AST Tree and if the Parent of the current190// Stmt is a CompoundStmt, we can extract inside this CompoundStmt just before191// the current Stmt. We ALWAYS insert before a Stmt whose parent is a192// CompoundStmt193//194// FIXME: Extraction from label, switch and case statements195// FIXME: Doens't work for FoldExpr196// FIXME: Ensure extraction from loops doesn't change semantics.197const clang::Stmt *ExtractionContext::computeInsertionPoint() const {198 // returns true if we can extract before InsertionPoint199 auto CanExtractOutside =200 [](const SelectionTree::Node *InsertionPoint) -> bool {201 if (const clang::Stmt *Stmt = InsertionPoint->ASTNode.get<clang::Stmt>()) {202 if (isa<clang::Expr>(Stmt)) {203 // Do not allow extraction from the initializer of a defaulted parameter204 // to a local variable (e.g. a function-local lambda).205 if (InsertionPoint->Parent->ASTNode.get<ParmVarDecl>() != nullptr) {206 return false;207 }208 209 return true;210 }211 212 // We don't yet allow extraction from switch/case stmt as we would need to213 // jump over the switch stmt even if there is a CompoundStmt inside the214 // switch. And there are other Stmts which we don't care about (e.g.215 // continue and break) as there can never be anything to extract from216 // them.217 return isa<AttributedStmt>(Stmt) || isa<CompoundStmt>(Stmt) ||218 isa<CXXForRangeStmt>(Stmt) || isa<DeclStmt>(Stmt) ||219 isa<DoStmt>(Stmt) || isa<ForStmt>(Stmt) || isa<IfStmt>(Stmt) ||220 isa<ReturnStmt>(Stmt) || isa<WhileStmt>(Stmt);221 }222 if (InsertionPoint->ASTNode.get<VarDecl>())223 return true;224 return false;225 };226 for (const SelectionTree::Node *CurNode = getExprNode();227 CurNode->Parent && CanExtractOutside(CurNode);228 CurNode = CurNode->Parent) {229 const clang::Stmt *CurInsertionPoint = CurNode->ASTNode.get<Stmt>();230 // give up if extraction will take a variable out of scope231 if (CurInsertionPoint && !exprIsValidOutside(CurInsertionPoint))232 break;233 if (const clang::Stmt *CurParent = CurNode->Parent->ASTNode.get<Stmt>()) {234 if (isa<CompoundStmt>(CurParent)) {235 // Ensure we don't write inside a macro.236 if (CurParent->getBeginLoc().isMacroID())237 continue;238 return CurInsertionPoint;239 }240 }241 }242 return nullptr;243}244 245// returns the replacement for substituting the extraction with VarName246tooling::Replacement247ExtractionContext::replaceWithVar(SourceRange Chars,248 llvm::StringRef VarName) const {249 unsigned ExtractionLength =250 SM.getFileOffset(Chars.getEnd()) - SM.getFileOffset(Chars.getBegin());251 return tooling::Replacement(SM, Chars.getBegin(), ExtractionLength, VarName);252}253// returns the Replacement for declaring a new variable storing the extraction254tooling::Replacement255ExtractionContext::insertDeclaration(llvm::StringRef VarName,256 SourceRange InitializerChars,257 bool AddSemicolon) const {258 llvm::StringRef ExtractionCode = toSourceCode(SM, InitializerChars);259 const SourceLocation InsertionLoc =260 toHalfOpenFileRange(SM, Ctx.getLangOpts(),261 InsertionPoint->getSourceRange())262 ->getBegin();263 std::string ExtractedVarDecl =264 printType(VarType, ExprNode->getDeclContext(), VarName) + " = " +265 ExtractionCode.str();266 if (AddSemicolon)267 ExtractedVarDecl += "; ";268 return tooling::Replacement(SM, InsertionLoc, 0, ExtractedVarDecl);269}270 271// Helpers for handling "binary subexpressions" like a + [[b + c]] + d.272//273// These are special, because the formal AST doesn't match what users expect:274// - the AST is ((a + b) + c) + d, so the ancestor expression is `a + b + c`.275// - but extracting `b + c` is reasonable, as + is (mathematically) associative.276//277// So we try to support these cases with some restrictions:278// - the operator must be associative279// - no mixing of operators is allowed280// - we don't look inside macro expansions in the subexpressions281// - we only adjust the extracted range, so references in the unselected parts282// of the AST expression (e.g. `a`) are still considered referenced for283// the purposes of calculating the insertion point.284// FIXME: it would be nice to exclude these references, by micromanaging285// the computeReferencedDecls() calls around the binary operator tree.286 287// Information extracted about a binary operator encounted in a SelectionTree.288// It can represent either an overloaded or built-in operator.289struct ParsedBinaryOperator {290 BinaryOperatorKind Kind;291 SourceLocation ExprLoc;292 llvm::SmallVector<const SelectionTree::Node *> SelectedOperands;293 294 // If N is a binary operator, populate this and return true.295 bool parse(const SelectionTree::Node &N) {296 SelectedOperands.clear();297 298 if (const BinaryOperator *Op =299 llvm::dyn_cast_or_null<BinaryOperator>(N.ASTNode.get<Expr>())) {300 Kind = Op->getOpcode();301 ExprLoc = Op->getExprLoc();302 SelectedOperands = N.Children;303 return true;304 }305 if (const CXXOperatorCallExpr *Op =306 llvm::dyn_cast_or_null<CXXOperatorCallExpr>(307 N.ASTNode.get<Expr>())) {308 if (!Op->isInfixBinaryOp())309 return false;310 311 Kind = BinaryOperator::getOverloadedOpcode(Op->getOperator());312 ExprLoc = Op->getExprLoc();313 // Not all children are args, there's also the callee (operator).314 for (const auto *Child : N.Children) {315 const Expr *E = Child->ASTNode.get<Expr>();316 assert(E && "callee and args should be Exprs!");317 if (E == Op->getArg(0) || E == Op->getArg(1))318 SelectedOperands.push_back(Child);319 }320 return true;321 }322 return false;323 }324 325 bool associative() const {326 // Must also be left-associative, or update getBinaryOperatorRange()!327 switch (Kind) {328 case BO_Add:329 case BO_Mul:330 case BO_And:331 case BO_Or:332 case BO_Xor:333 case BO_LAnd:334 case BO_LOr:335 return true;336 default:337 return false;338 }339 }340 341 bool crossesMacroBoundary(const SourceManager &SM) {342 FileID F = SM.getFileID(ExprLoc);343 for (const SelectionTree::Node *Child : SelectedOperands)344 if (SM.getFileID(Child->ASTNode.get<Expr>()->getExprLoc()) != F)345 return true;346 return false;347 }348};349 350// If have an associative operator at the top level, then we must find351// the start point (rightmost in LHS) and end point (leftmost in RHS).352// We can only descend into subtrees where the operator matches.353//354// e.g. for a + [[b + c]] + d355// +356// / \357// N-> + d358// / \359// + c <- End360// / \361// a b <- Start362SourceRange getBinaryOperatorRange(const SelectionTree::Node &N,363 const SourceManager &SM,364 const LangOptions &LangOpts) {365 // If N is not a suitable binary operator, bail out.366 ParsedBinaryOperator Op;367 if (!Op.parse(N.ignoreImplicit()) || !Op.associative() ||368 Op.crossesMacroBoundary(SM) || Op.SelectedOperands.size() != 2)369 return SourceRange();370 BinaryOperatorKind OuterOp = Op.Kind;371 372 // Because the tree we're interested in contains only one operator type, and373 // all eligible operators are left-associative, the shape of the tree is374 // very restricted: it's a linked list along the left edges.375 // This simplifies our implementation.376 const SelectionTree::Node *Start = Op.SelectedOperands.front(); // LHS377 const SelectionTree::Node *End = Op.SelectedOperands.back(); // RHS378 // End is already correct: it can't be an OuterOp (as it's left-associative).379 // Start needs to be pushed down int the subtree to the right spot.380 while (Op.parse(Start->ignoreImplicit()) && Op.Kind == OuterOp &&381 !Op.crossesMacroBoundary(SM)) {382 assert(!Op.SelectedOperands.empty() && "got only operator on one side!");383 if (Op.SelectedOperands.size() == 1) { // Only Op.RHS selected384 Start = Op.SelectedOperands.back();385 break;386 }387 // Op.LHS is (at least partially) selected, so descend into it.388 Start = Op.SelectedOperands.front();389 }390 391 return SourceRange(392 toHalfOpenFileRange(SM, LangOpts, Start->ASTNode.getSourceRange())393 ->getBegin(),394 toHalfOpenFileRange(SM, LangOpts, End->ASTNode.getSourceRange())395 ->getEnd());396}397 398SourceRange ExtractionContext::getExtractionChars() const {399 // Special case: we're extracting an associative binary subexpression.400 SourceRange BinaryOperatorRange =401 getBinaryOperatorRange(*ExprNode, SM, Ctx.getLangOpts());402 if (BinaryOperatorRange.isValid())403 return BinaryOperatorRange;404 405 // Usual case: we're extracting the whole expression.406 return *toHalfOpenFileRange(SM, Ctx.getLangOpts(), Expr->getSourceRange());407}408 409// Find the CallExpr whose callee is the (possibly wrapped) DeclRef410const SelectionTree::Node *getCallExpr(const SelectionTree::Node *DeclRef) {411 const SelectionTree::Node &MaybeCallee = DeclRef->outerImplicit();412 const SelectionTree::Node *MaybeCall = MaybeCallee.Parent;413 if (!MaybeCall)414 return nullptr;415 const CallExpr *CE =416 llvm::dyn_cast_or_null<CallExpr>(MaybeCall->ASTNode.get<Expr>());417 if (!CE)418 return nullptr;419 if (CE->getCallee() != MaybeCallee.ASTNode.get<Expr>())420 return nullptr;421 return MaybeCall;422}423 424// Returns true if Inner (which is a direct child of Outer) is appearing as425// a statement rather than an expression whose value can be used.426bool childExprIsDisallowedStmt(const Stmt *Outer, const Expr *Inner) {427 if (!Outer || !Inner)428 return false;429 // Exclude the most common places where an expr can appear but be unused.430 if (llvm::isa<SwitchCase>(Outer))431 return true;432 // Control flow statements use condition etc, but not the body.433 if (const auto *WS = llvm::dyn_cast<WhileStmt>(Outer))434 return Inner == WS->getBody();435 if (const auto *DS = llvm::dyn_cast<DoStmt>(Outer))436 return Inner == DS->getBody();437 if (const auto *FS = llvm::dyn_cast<ForStmt>(Outer))438 return Inner == FS->getBody();439 if (const auto *FS = llvm::dyn_cast<CXXForRangeStmt>(Outer))440 return Inner == FS->getBody();441 if (const auto *IS = llvm::dyn_cast<IfStmt>(Outer))442 return Inner == IS->getThen() || Inner == IS->getElse();443 // Assume all other cases may be actual expressions.444 // This includes the important case of subexpressions (where Outer is Expr).445 return false;446}447 448// check if N can and should be extracted (e.g. is not void-typed).449bool eligibleForExtraction(const SelectionTree::Node *N) {450 const Expr *E = N->ASTNode.get<Expr>();451 if (!E)452 return false;453 454 // Void expressions can't be assigned to variables.455 const Type *ExprType = E->getType().getTypePtrOrNull();456 if (!ExprType || ExprType->isVoidType())457 return false;458 459 // A plain reference to a name (e.g. variable) isn't worth extracting.460 // FIXME: really? What if it's e.g. `std::is_same<void, void>::value`?461 if (llvm::isa<DeclRefExpr>(E))462 return false;463 464 // Similarly disallow extraction for member exprs with an implicit `this`.465 if (const auto *ME = dyn_cast<MemberExpr>(E))466 if (const auto *TE = dyn_cast<CXXThisExpr>(ME->getBase()->IgnoreImpCasts()))467 if (TE->isImplicit())468 return false;469 470 // Extracting Exprs like a = 1 gives placeholder = a = 1 which isn't useful.471 // FIXME: we could still hoist the assignment, and leave the variable there?472 ParsedBinaryOperator BinOp;473 bool IsBinOp = BinOp.parse(*N);474 if (IsBinOp && BinaryOperator::isAssignmentOp(BinOp.Kind))475 return false;476 477 const SelectionTree::Node &OuterImplicit = N->outerImplicit();478 const auto *Parent = OuterImplicit.Parent;479 if (!Parent)480 return false;481 // Filter non-applicable expression statements.482 if (childExprIsDisallowedStmt(Parent->ASTNode.get<Stmt>(),483 OuterImplicit.ASTNode.get<Expr>()))484 return false;485 486 std::function<bool(const SelectionTree::Node *)> IsFullySelected =487 [&](const SelectionTree::Node *N) {488 if (N->ASTNode.getSourceRange().isValid() &&489 N->Selected != SelectionTree::Complete)490 return false;491 for (const auto *Child : N->Children) {492 if (!IsFullySelected(Child))493 return false;494 }495 return true;496 };497 auto ExprIsFullySelectedTargetNode = [&](const Expr *E) {498 if (E != OuterImplicit.ASTNode.get<Expr>())499 return false;500 501 // The above condition is the only relevant one except for binary operators.502 // Without the following code, we would fail to offer extraction for e.g.:503 // int x = 1 + 2 + [[3 + 4 + 5]];504 // See the documentation of ParsedBinaryOperator for further details.505 if (!IsBinOp)506 return true;507 return IsFullySelected(N);508 };509 510 // Disable extraction of full RHS on assignment operations, e.g:511 // x = [[RHS_EXPR]];512 // This would just result in duplicating the code.513 if (const auto *BO = Parent->ASTNode.get<BinaryOperator>()) {514 if (BO->isAssignmentOp() && ExprIsFullySelectedTargetNode(BO->getRHS()))515 return false;516 }517 518 // If e.g. a capture clause was selected, the target node is the lambda519 // expression. We only want to offer the extraction if the entire lambda520 // expression was selected.521 if (llvm::isa<LambdaExpr>(E))522 return N->Selected == SelectionTree::Complete;523 524 // The same logic as for assignments applies to initializations.525 // However, we do allow extracting the RHS of an init capture, as it is526 // a valid use case to move non-trivial expressions out of the capture clause.527 // FIXME: In that case, the extracted variable should be captured directly,528 // rather than an explicit copy.529 if (const auto *Decl = Parent->ASTNode.get<VarDecl>()) {530 if (!Decl->isInitCapture() &&531 ExprIsFullySelectedTargetNode(Decl->getInit())) {532 return false;533 }534 }535 536 return true;537}538 539// Find the Expr node that we're going to extract.540// We don't want to trigger for assignment expressions and variable/field541// DeclRefs. For function/member function, we want to extract the entire542// function call.543const SelectionTree::Node *computeExtractedExpr(const SelectionTree::Node *N) {544 if (!N)545 return nullptr;546 const SelectionTree::Node *TargetNode = N;547 const clang::Expr *SelectedExpr = N->ASTNode.get<clang::Expr>();548 if (!SelectedExpr)549 return nullptr;550 // For function and member function DeclRefs, extract the whole call.551 if (llvm::isa<DeclRefExpr>(SelectedExpr) ||552 llvm::isa<MemberExpr>(SelectedExpr))553 if (const SelectionTree::Node *Call = getCallExpr(N))554 TargetNode = Call;555 // Extracting Exprs like a = 1 gives placeholder = a = 1 which isn't useful.556 if (const BinaryOperator *BinOpExpr =557 dyn_cast_or_null<BinaryOperator>(SelectedExpr)) {558 if (BinOpExpr->getOpcode() == BinaryOperatorKind::BO_Assign)559 return nullptr;560 }561 if (!TargetNode || !eligibleForExtraction(TargetNode))562 return nullptr;563 return TargetNode;564}565 566/// Extracts an expression to the variable placeholder567/// Before:568/// int x = 5 + 4 * 3;569/// ^^^^^570/// After:571/// auto placeholder = 5 + 4;572/// int x = placeholder * 3;573class ExtractVariable : public Tweak {574public:575 const char *id() const final;576 bool prepare(const Selection &Inputs) override;577 Expected<Effect> apply(const Selection &Inputs) override;578 std::string title() const override {579 return "Extract subexpression to variable";580 }581 llvm::StringLiteral kind() const override {582 return CodeAction::REFACTOR_KIND;583 }584 585private:586 // the expression to extract587 std::unique_ptr<ExtractionContext> Target;588};589REGISTER_TWEAK(ExtractVariable)590bool ExtractVariable::prepare(const Selection &Inputs) {591 // we don't trigger on empty selections for now592 if (Inputs.SelectionBegin == Inputs.SelectionEnd)593 return false;594 const ASTContext &Ctx = Inputs.AST->getASTContext();595 const SourceManager &SM = Inputs.AST->getSourceManager();596 if (const SelectionTree::Node *N =597 computeExtractedExpr(Inputs.ASTSelection.commonAncestor()))598 Target = std::make_unique<ExtractionContext>(N, SM, Ctx);599 return Target && Target->isExtractable();600}601 602Expected<Tweak::Effect> ExtractVariable::apply(const Selection &Inputs) {603 tooling::Replacements Result;604 // FIXME: get variable name from user or suggest based on type605 std::string VarName = "placeholder";606 SourceRange Range = Target->getExtractionChars();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 if615 // we are not dealing with an expression statement, which already has616 // 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)))619 return std::move(Err);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();625 if (auto Err = Result.add(Target->replaceWithVar(Range, VarName)))626 return std::move(Err);627 return Effect::mainFileEdit(Inputs.AST->getSourceManager(),628 std::move(Result));629}630 631} // namespace632} // namespace clangd633} // namespace clang634