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-exception 6 // 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 34 namespace clang { 35 namespace clangd { 36 namespace { 37 // information regarding the Expr that is being extracted 38 class ExtractionContext { 39 public: 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 VarName 48 tooling::Replacement replaceWithVar(SourceRange Chars, 49 llvm::StringRef VarName) const; 50 // Generate Replacement for declaring the selected Expr as a new variable 51 tooling::Replacement insertDeclaration(llvm::StringRef VarName, 52 SourceRange InitChars) const; 53 54 private: 55 bool Extractable = false; 56 const clang::Expr *Expr; 57 QualType VarType; 58 const SelectionTree::Node *ExprNode; 59 // Stmt before which we will extract 60 const clang::Stmt *InsertionPoint = nullptr; 61 const SourceManager &SM; 62 const ASTContext &Ctx; 63 // Decls referenced in the Expr 64 std::vector<clang::Decl *> ReferencedDecls; 65 // returns true if the Expr doesn't reference any variable declared in scope 66 bool exprIsValidOutside(const clang::Stmt *Scope) const; 67 // computes the Stmt before which we will extract out Expr 68 const clang::Stmt *computeInsertionPoint() const; 69 }; 70 71 // Returns all the Decls referenced inside the given Expr 72 static std::vector<clang::Decl *> 73 computeReferencedDecls(const clang::Expr *Expr) { 74 // RAV subclass to find all DeclRefs in a given Stmt 75 class FindDeclRefsVisitor 76 : public clang::RecursiveASTVisitor<FindDeclRefsVisitor> { 77 public: 78 std::vector<Decl *> ReferencedDecls; 79 bool VisitDeclRefExpr(DeclRefExpr *DeclRef) { // NOLINT 80 // Stop the call operator of lambdas from being marked as a referenced 81 // DeclRefExpr in immediately invoked lambdas. 82 if (const auto *const Method = 83 llvm::dyn_cast<CXXMethodDecl>(DeclRef->getDecl()); 84 Method != nullptr && Method->getParent()->isLambda()) { 85 return true; 86 } 87 ReferencedDecls.push_back(DeclRef->getDecl()); 88 return true; 89 } 90 91 // Local variables declared inside of the selected lambda cannot go out of 92 // scope. The DeclRefExprs that are important are the variables captured, 93 // the DeclRefExprs inside the initializers of init-capture variables, 94 // variables mentioned in trailing return types, constraints and explicit 95 // defaulted template parameters. 96 bool TraverseLambdaExpr(LambdaExpr *LExpr) { 97 for (const auto &[Capture, Initializer] : 98 llvm::zip(LExpr->captures(), LExpr->capture_inits())) { 99 TraverseLambdaCapture(LExpr, &Capture, Initializer); 100 } 101 102 if (clang::Expr *const RequiresClause = 103 LExpr->getTrailingRequiresClause()) { 104 TraverseStmt(RequiresClause); 105 } 106 107 for (auto *const TemplateParam : LExpr->getExplicitTemplateParameters()) 108 TraverseDecl(TemplateParam); 109 110 if (auto *const CallOperator = LExpr->getCallOperator()) { 111 TraverseType(CallOperator->getDeclaredReturnType()); 112 113 for (auto *const Param : CallOperator->parameters()) { 114 TraverseParmVarDecl(Param); 115 } 116 117 for (auto *const Attr : CallOperator->attrs()) { 118 TraverseAttr(Attr); 119 } 120 } 121 122 return true; 123 } 124 }; 125 126 FindDeclRefsVisitor Visitor; 127 Visitor.TraverseStmt(const_cast<Stmt *>(cast<Stmt>(Expr))); 128 return Visitor.ReferencedDecls; 129 } 130 131 static QualType computeVariableType(const Expr *Expr, const ASTContext &Ctx) { 132 if (Ctx.getLangOpts().CPlusPlus11) 133 return Ctx.getAutoDeductType(); 134 135 if (Expr->hasPlaceholderType(BuiltinType::PseudoObject)) { 136 if (const auto *PR = dyn_cast<ObjCPropertyRefExpr>(Expr)) { 137 if (PR->isMessagingSetter()) { 138 // Don't support extracting a compound reference like `self.prop += 1` 139 // since the meaning changes after extraction since we'll no longer call 140 // the setter. Non compound access like `self.prop = 1` is invalid since 141 // it returns nil (setter method must have a void return type). 142 return QualType(); 143 } else if (PR->isMessagingGetter()) { 144 if (PR->isExplicitProperty()) 145 return PR->getExplicitProperty()->getType(); 146 else 147 return PR->getImplicitPropertyGetter()->getReturnType(); 148 } 149 } else { 150 return QualType(); 151 } 152 } 153 return Expr->getType(); 154 } 155 156 ExtractionContext::ExtractionContext(const SelectionTree::Node *Node, 157 const SourceManager &SM, 158 const ASTContext &Ctx) 159 : ExprNode(Node), SM(SM), Ctx(Ctx) { 160 Expr = Node->ASTNode.get<clang::Expr>(); 161 ReferencedDecls = computeReferencedDecls(Expr); 162 InsertionPoint = computeInsertionPoint(); 163 if (InsertionPoint) 164 Extractable = true; 165 VarType = computeVariableType(Expr, Ctx); 166 if (VarType.isNull()) 167 Extractable = false; 168 else 169 // Strip the outer nullability since it's not common for local variables. 170 AttributedType::stripOuterNullability(VarType); 171 } 172 173 // checks whether extracting before InsertionPoint will take a 174 // variable reference out of scope 175 bool ExtractionContext::exprIsValidOutside(const clang::Stmt *Scope) const { 176 SourceLocation ScopeBegin = Scope->getBeginLoc(); 177 SourceLocation ScopeEnd = Scope->getEndLoc(); 178 for (const Decl *ReferencedDecl : ReferencedDecls) { 179 if (ReferencedDecl->getBeginLoc().isValid() && 180 SM.isPointWithin(ReferencedDecl->getBeginLoc(), ScopeBegin, ScopeEnd) && 181 SM.isPointWithin(ReferencedDecl->getEndLoc(), ScopeBegin, ScopeEnd)) 182 return false; 183 } 184 return true; 185 } 186 187 // Return the Stmt before which we need to insert the extraction. 188 // To find the Stmt, we go up the AST Tree and if the Parent of the current 189 // Stmt is a CompoundStmt, we can extract inside this CompoundStmt just before 190 // the current Stmt. We ALWAYS insert before a Stmt whose parent is a 191 // CompoundStmt 192 // 193 // FIXME: Extraction from label, switch and case statements 194 // FIXME: Doens't work for FoldExpr 195 // FIXME: Ensure extraction from loops doesn't change semantics. 196 const clang::Stmt *ExtractionContext::computeInsertionPoint() const { 197 // returns true if we can extract before InsertionPoint 198 auto CanExtractOutside = 199 [](const SelectionTree::Node *InsertionPoint) -> bool { 200 if (const clang::Stmt *Stmt = InsertionPoint->ASTNode.get<clang::Stmt>()) { 201 if (isa<clang::Expr>(Stmt)) { 202 // Do not allow extraction from the initializer of a defaulted parameter 203 // to a local variable (e.g. a function-local lambda). 204 if (InsertionPoint->Parent->ASTNode.get<ParmVarDecl>() != nullptr) { 205 return false; 206 } 207 208 return true; 209 } 210 211 // We don't yet allow extraction from switch/case stmt as we would need to 212 // jump over the switch stmt even if there is a CompoundStmt inside the 213 // switch. And there are other Stmts which we don't care about (e.g. 214 // continue and break) as there can never be anything to extract from 215 // them. 216 return isa<AttributedStmt>(Stmt) || isa<CompoundStmt>(Stmt) || 217 isa<CXXForRangeStmt>(Stmt) || isa<DeclStmt>(Stmt) || 218 isa<DoStmt>(Stmt) || isa<ForStmt>(Stmt) || isa<IfStmt>(Stmt) || 219 isa<ReturnStmt>(Stmt) || isa<WhileStmt>(Stmt); 220 } 221 if (InsertionPoint->ASTNode.get<VarDecl>()) 222 return true; 223 return false; 224 }; 225 for (const SelectionTree::Node *CurNode = getExprNode(); 226 CurNode->Parent && CanExtractOutside(CurNode); 227 CurNode = CurNode->Parent) { 228 const clang::Stmt *CurInsertionPoint = CurNode->ASTNode.get<Stmt>(); 229 // give up if extraction will take a variable out of scope 230 if (CurInsertionPoint && !exprIsValidOutside(CurInsertionPoint)) 231 break; 232 if (const clang::Stmt *CurParent = CurNode->Parent->ASTNode.get<Stmt>()) { 233 if (isa<CompoundStmt>(CurParent)) { 234 // Ensure we don't write inside a macro. 235 if (CurParent->getBeginLoc().isMacroID()) 236 continue; 237 return CurInsertionPoint; 238 } 239 } 240 } 241 return nullptr; 242 } 243 244 // returns the replacement for substituting the extraction with VarName 245 tooling::Replacement 246 ExtractionContext::replaceWithVar(SourceRange Chars, 247 llvm::StringRef VarName) const { 248 unsigned ExtractionLength = 249 SM.getFileOffset(Chars.getEnd()) - SM.getFileOffset(Chars.getBegin()); 250 return tooling::Replacement(SM, Chars.getBegin(), ExtractionLength, VarName); 251 } 252 // returns the Replacement for declaring a new variable storing the extraction 253 tooling::Replacement 254 ExtractionContext::insertDeclaration(llvm::StringRef VarName, 255 SourceRange InitializerChars) const { 256 llvm::StringRef ExtractionCode = toSourceCode(SM, InitializerChars); 257 const SourceLocation InsertionLoc = 258 toHalfOpenFileRange(SM, Ctx.getLangOpts(), 259 InsertionPoint->getSourceRange()) 260 ->getBegin(); 261 std::string ExtractedVarDecl = 262 printType(VarType, ExprNode->getDeclContext(), VarName) + " = " + 263 ExtractionCode.str() + "; "; 264 return tooling::Replacement(SM, InsertionLoc, 0, ExtractedVarDecl); 265 } 266 267 // Helpers for handling "binary subexpressions" like a + [[b + c]] + d. 268 // 269 // These are special, because the formal AST doesn't match what users expect: 270 // - the AST is ((a + b) + c) + d, so the ancestor expression is `a + b + c`. 271 // - but extracting `b + c` is reasonable, as + is (mathematically) associative. 272 // 273 // So we try to support these cases with some restrictions: 274 // - the operator must be associative 275 // - no mixing of operators is allowed 276 // - we don't look inside macro expansions in the subexpressions 277 // - we only adjust the extracted range, so references in the unselected parts 278 // of the AST expression (e.g. `a`) are still considered referenced for 279 // the purposes of calculating the insertion point. 280 // FIXME: it would be nice to exclude these references, by micromanaging 281 // the computeReferencedDecls() calls around the binary operator tree. 282 283 // Information extracted about a binary operator encounted in a SelectionTree. 284 // It can represent either an overloaded or built-in operator. 285 struct ParsedBinaryOperator { 286 BinaryOperatorKind Kind; 287 SourceLocation ExprLoc; 288 llvm::SmallVector<const SelectionTree::Node *> SelectedOperands; 289 290 // If N is a binary operator, populate this and return true. 291 bool parse(const SelectionTree::Node &N) { 292 SelectedOperands.clear(); 293 294 if (const BinaryOperator *Op = 295 llvm::dyn_cast_or_null<BinaryOperator>(N.ASTNode.get<Expr>())) { 296 Kind = Op->getOpcode(); 297 ExprLoc = Op->getExprLoc(); 298 SelectedOperands = N.Children; 299 return true; 300 } 301 if (const CXXOperatorCallExpr *Op = 302 llvm::dyn_cast_or_null<CXXOperatorCallExpr>( 303 N.ASTNode.get<Expr>())) { 304 if (!Op->isInfixBinaryOp()) 305 return false; 306 307 Kind = BinaryOperator::getOverloadedOpcode(Op->getOperator()); 308 ExprLoc = Op->getExprLoc(); 309 // Not all children are args, there's also the callee (operator). 310 for (const auto *Child : N.Children) { 311 const Expr *E = Child->ASTNode.get<Expr>(); 312 assert(E && "callee and args should be Exprs!"); 313 if (E == Op->getArg(0) || E == Op->getArg(1)) 314 SelectedOperands.push_back(Child); 315 } 316 return true; 317 } 318 return false; 319 } 320 321 bool associative() const { 322 // Must also be left-associative, or update getBinaryOperatorRange()! 323 switch (Kind) { 324 case BO_Add: 325 case BO_Mul: 326 case BO_And: 327 case BO_Or: 328 case BO_Xor: 329 case BO_LAnd: 330 case BO_LOr: 331 return true; 332 default: 333 return false; 334 } 335 } 336 337 bool crossesMacroBoundary(const SourceManager &SM) { 338 FileID F = SM.getFileID(ExprLoc); 339 for (const SelectionTree::Node *Child : SelectedOperands) 340 if (SM.getFileID(Child->ASTNode.get<Expr>()->getExprLoc()) != F) 341 return true; 342 return false; 343 } 344 }; 345 346 // If have an associative operator at the top level, then we must find 347 // the start point (rightmost in LHS) and end point (leftmost in RHS). 348 // We can only descend into subtrees where the operator matches. 349 // 350 // e.g. for a + [[b + c]] + d 351 // + 352 // / \ 353 // N-> + d 354 // / \ 355 // + c <- End 356 // / \ 357 // a b <- Start 358 const SourceRange getBinaryOperatorRange(const SelectionTree::Node &N, 359 const SourceManager &SM, 360 const LangOptions &LangOpts) { 361 // If N is not a suitable binary operator, bail out. 362 ParsedBinaryOperator Op; 363 if (!Op.parse(N.ignoreImplicit()) || !Op.associative() || 364 Op.crossesMacroBoundary(SM) || Op.SelectedOperands.size() != 2) 365 return SourceRange(); 366 BinaryOperatorKind OuterOp = Op.Kind; 367 368 // Because the tree we're interested in contains only one operator type, and 369 // all eligible operators are left-associative, the shape of the tree is 370 // very restricted: it's a linked list along the left edges. 371 // This simplifies our implementation. 372 const SelectionTree::Node *Start = Op.SelectedOperands.front(); // LHS 373 const SelectionTree::Node *End = Op.SelectedOperands.back(); // RHS 374 // End is already correct: it can't be an OuterOp (as it's left-associative). 375 // Start needs to be pushed down int the subtree to the right spot. 376 while (Op.parse(Start->ignoreImplicit()) && Op.Kind == OuterOp && 377 !Op.crossesMacroBoundary(SM)) { 378 assert(!Op.SelectedOperands.empty() && "got only operator on one side!"); 379 if (Op.SelectedOperands.size() == 1) { // Only Op.RHS selected 380 Start = Op.SelectedOperands.back(); 381 break; 382 } 383 // Op.LHS is (at least partially) selected, so descend into it. 384 Start = Op.SelectedOperands.front(); 385 } 386 387 return SourceRange( 388 toHalfOpenFileRange(SM, LangOpts, Start->ASTNode.getSourceRange()) 389 ->getBegin(), 390 toHalfOpenFileRange(SM, LangOpts, End->ASTNode.getSourceRange()) 391 ->getEnd()); 392 } 393 394 SourceRange ExtractionContext::getExtractionChars() const { 395 // Special case: we're extracting an associative binary subexpression. 396 SourceRange BinaryOperatorRange = 397 getBinaryOperatorRange(*ExprNode, SM, Ctx.getLangOpts()); 398 if (BinaryOperatorRange.isValid()) 399 return BinaryOperatorRange; 400 401 // Usual case: we're extracting the whole expression. 402 return *toHalfOpenFileRange(SM, Ctx.getLangOpts(), Expr->getSourceRange()); 403 } 404 405 // Find the CallExpr whose callee is the (possibly wrapped) DeclRef 406 const SelectionTree::Node *getCallExpr(const SelectionTree::Node *DeclRef) { 407 const SelectionTree::Node &MaybeCallee = DeclRef->outerImplicit(); 408 const SelectionTree::Node *MaybeCall = MaybeCallee.Parent; 409 if (!MaybeCall) 410 return nullptr; 411 const CallExpr *CE = 412 llvm::dyn_cast_or_null<CallExpr>(MaybeCall->ASTNode.get<Expr>()); 413 if (!CE) 414 return nullptr; 415 if (CE->getCallee() != MaybeCallee.ASTNode.get<Expr>()) 416 return nullptr; 417 return MaybeCall; 418 } 419 420 // Returns true if Inner (which is a direct child of Outer) is appearing as 421 // a statement rather than an expression whose value can be used. 422 bool childExprIsStmt(const Stmt *Outer, const Expr *Inner) { 423 if (!Outer || !Inner) 424 return false; 425 // Exclude the most common places where an expr can appear but be unused. 426 if (llvm::isa<CompoundStmt>(Outer)) 427 return true; 428 if (llvm::isa<SwitchCase>(Outer)) 429 return true; 430 // Control flow statements use condition etc, but not the body. 431 if (const auto *WS = llvm::dyn_cast<WhileStmt>(Outer)) 432 return Inner == WS->getBody(); 433 if (const auto *DS = llvm::dyn_cast<DoStmt>(Outer)) 434 return Inner == DS->getBody(); 435 if (const auto *FS = llvm::dyn_cast<ForStmt>(Outer)) 436 return Inner == FS->getBody(); 437 if (const auto *FS = llvm::dyn_cast<CXXForRangeStmt>(Outer)) 438 return Inner == FS->getBody(); 439 if (const auto *IS = llvm::dyn_cast<IfStmt>(Outer)) 440 return Inner == IS->getThen() || Inner == IS->getElse(); 441 // Assume all other cases may be actual expressions. 442 // This includes the important case of subexpressions (where Outer is Expr). 443 return false; 444 } 445 446 // check if N can and should be extracted (e.g. is not void-typed). 447 bool eligibleForExtraction(const SelectionTree::Node *N) { 448 const Expr *E = N->ASTNode.get<Expr>(); 449 if (!E) 450 return false; 451 452 // Void expressions can't be assigned to variables. 453 const Type *ExprType = E->getType().getTypePtrOrNull(); 454 if (!ExprType || ExprType->isVoidType()) 455 return false; 456 457 // A plain reference to a name (e.g. variable) isn't worth extracting. 458 // FIXME: really? What if it's e.g. `std::is_same<void, void>::value`? 459 if (llvm::isa<DeclRefExpr>(E)) 460 return false; 461 462 // Similarly disallow extraction for member exprs with an implicit `this`. 463 if (const auto *ME = dyn_cast<MemberExpr>(E)) 464 if (const auto *TE = dyn_cast<CXXThisExpr>(ME->getBase()->IgnoreImpCasts())) 465 if (TE->isImplicit()) 466 return false; 467 468 // Extracting Exprs like a = 1 gives placeholder = a = 1 which isn't useful. 469 // FIXME: we could still hoist the assignment, and leave the variable there? 470 ParsedBinaryOperator BinOp; 471 bool IsBinOp = BinOp.parse(*N); 472 if (IsBinOp && BinaryOperator::isAssignmentOp(BinOp.Kind)) 473 return false; 474 475 const SelectionTree::Node &OuterImplicit = N->outerImplicit(); 476 const auto *Parent = OuterImplicit.Parent; 477 if (!Parent) 478 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>())) 485 return false; 486 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 511 // Disable extraction of full RHS on assignment operations, e.g: 512 // x = [[RHS_EXPR]]; 513 // This would just result in duplicating the code. 514 if (const auto *BO = Parent->ASTNode.get<BinaryOperator>()) { 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())) { 527 return false; 528 } 529 } 530 531 return true; 532 } 533 534 // Find the Expr node that we're going to extract. 535 // We don't want to trigger for assignment expressions and variable/field 536 // DeclRefs. For function/member function, we want to extract the entire 537 // function call. 538 const SelectionTree::Node *computeExtractedExpr(const SelectionTree::Node *N) { 539 if (!N) 540 return nullptr; 541 const SelectionTree::Node *TargetNode = N; 542 const clang::Expr *SelectedExpr = N->ASTNode.get<clang::Expr>(); 543 if (!SelectedExpr) 544 return nullptr; 545 // For function and member function DeclRefs, extract the whole call. 546 if (llvm::isa<DeclRefExpr>(SelectedExpr) || 547 llvm::isa<MemberExpr>(SelectedExpr)) 548 if (const SelectionTree::Node *Call = getCallExpr(N)) 549 TargetNode = Call; 550 // Extracting Exprs like a = 1 gives placeholder = a = 1 which isn't useful. 551 if (const BinaryOperator *BinOpExpr = 552 dyn_cast_or_null<BinaryOperator>(SelectedExpr)) { 553 if (BinOpExpr->getOpcode() == BinaryOperatorKind::BO_Assign) 554 return nullptr; 555 } 556 if (!TargetNode || !eligibleForExtraction(TargetNode)) 557 return nullptr; 558 return TargetNode; 559 } 560 561 /// Extracts an expression to the variable placeholder 562 /// Before: 563 /// int x = 5 + 4 * 3; 564 /// ^^^^^ 565 /// After: 566 /// auto placeholder = 5 + 4; 567 /// int x = placeholder * 3; 568 class ExtractVariable : public Tweak { 569 public: 570 const char *id() const final; 571 bool prepare(const Selection &Inputs) override; 572 Expected<Effect> apply(const Selection &Inputs) override; 573 std::string title() const override { 574 return "Extract subexpression to variable"; 575 } 576 llvm::StringLiteral kind() const override { 577 return CodeAction::REFACTOR_KIND; 578 } 579 580 private: 581 // the expression to extract 582 std::unique_ptr<ExtractionContext> Target; 583 }; 584 REGISTER_TWEAK(ExtractVariable) 585 bool ExtractVariable::prepare(const Selection &Inputs) { 586 // we don't trigger on empty selections for now 587 if (Inputs.SelectionBegin == Inputs.SelectionEnd) 588 return false; 589 const ASTContext &Ctx = Inputs.AST->getASTContext(); 590 const SourceManager &SM = Inputs.AST->getSourceManager(); 591 if (const SelectionTree::Node *N = 592 computeExtractedExpr(Inputs.ASTSelection.commonAncestor())) 593 Target = std::make_unique<ExtractionContext>(N, SM, Ctx); 594 return Target && Target->isExtractable(); 595 } 596 597 Expected<Tweak::Effect> ExtractVariable::apply(const Selection &Inputs) { 598 tooling::Replacements Result; 599 // FIXME: get variable name from user or suggest based on type 600 std::string VarName = "placeholder"; 601 SourceRange Range = Target->getExtractionChars(); 602 // insert new variable declaration 603 if (auto Err = Result.add(Target->insertDeclaration(VarName, Range))) 604 return std::move(Err); 605 // replace expression with variable name 606 if (auto Err = Result.add(Target->replaceWithVar(Range, VarName))) 607 return std::move(Err); 608 return Effect::mainFileEdit(Inputs.AST->getSourceManager(), 609 std::move(Result)); 610 } 611 612 } // namespace 613 } // namespace clangd 614 } // namespace clang 615