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
34namespace clang {
35namespace clangd {
36namespace {
37// information regarding the Expr that is being extracted
38class 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 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
54private:
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
72static std::vector<clang::Decl *>
73computeReferencedDecls(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>(Val: 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(t: LExpr->captures(), u: LExpr->capture_inits())) {
99 TraverseLambdaCapture(LE: LExpr, C: &Capture, Init: 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(T: 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(S: const_cast<Stmt *>(cast<Stmt>(Val: Expr)));
128 return Visitor.ReferencedDecls;
129}
130
131static QualType computeVariableType(const Expr *Expr, const ASTContext &Ctx) {
132 if (Ctx.getLangOpts().CPlusPlus11)
133 return Ctx.getAutoDeductType();
134
135 if (Expr->hasPlaceholderType(K: BuiltinType::PseudoObject)) {
136 if (const auto *PR = dyn_cast<ObjCPropertyRefExpr>(Val: 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
156ExtractionContext::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
175bool 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(Location: ReferencedDecl->getBeginLoc(), Start: ScopeBegin, End: ScopeEnd) &&
181 SM.isPointWithin(Location: ReferencedDecl->getEndLoc(), Start: ScopeBegin, End: 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.
196const 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>(Val: 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>(Val: Stmt) || isa<CompoundStmt>(Val: Stmt) ||
217 isa<CXXForRangeStmt>(Val: Stmt) || isa<DeclStmt>(Val: Stmt) ||
218 isa<DoStmt>(Val: Stmt) || isa<ForStmt>(Val: Stmt) || isa<IfStmt>(Val: Stmt) ||
219 isa<ReturnStmt>(Val: Stmt) || isa<WhileStmt>(Val: 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(Scope: CurInsertionPoint))
231 break;
232 if (const clang::Stmt *CurParent = CurNode->Parent->ASTNode.get<Stmt>()) {
233 if (isa<CompoundStmt>(Val: 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
245tooling::Replacement
246ExtractionContext::replaceWithVar(SourceRange Chars,
247 llvm::StringRef VarName) const {
248 unsigned ExtractionLength =
249 SM.getFileOffset(SpellingLoc: Chars.getEnd()) - SM.getFileOffset(SpellingLoc: Chars.getBegin());
250 return tooling::Replacement(SM, Chars.getBegin(), ExtractionLength, VarName);
251}
252// returns the Replacement for declaring a new variable storing the extraction
253tooling::Replacement
254ExtractionContext::insertDeclaration(llvm::StringRef VarName,
255 SourceRange InitializerChars) const {
256 llvm::StringRef ExtractionCode = toSourceCode(SM, R: InitializerChars);
257 const SourceLocation InsertionLoc =
258 toHalfOpenFileRange(Mgr: SM, LangOpts: Ctx.getLangOpts(),
259 R: 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.
285struct 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(OO: 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(Elt: 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(SpellingLoc: 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
358const 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: 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(N: 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
394SourceRange ExtractionContext::getExtractionChars() const {
395 // Special case: we're extracting an associative binary subexpression.
396 SourceRange BinaryOperatorRange =
397 getBinaryOperatorRange(N: *ExprNode, SM, LangOpts: 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
406const 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.
422bool 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>(Val: Outer))
427 return true;
428 if (llvm::isa<SwitchCase>(Val: Outer))
429 return true;
430 // Control flow statements use condition etc, but not the body.
431 if (const auto *WS = llvm::dyn_cast<WhileStmt>(Val: Outer))
432 return Inner == WS->getBody();
433 if (const auto *DS = llvm::dyn_cast<DoStmt>(Val: Outer))
434 return Inner == DS->getBody();
435 if (const auto *FS = llvm::dyn_cast<ForStmt>(Val: Outer))
436 return Inner == FS->getBody();
437 if (const auto *FS = llvm::dyn_cast<CXXForRangeStmt>(Val: Outer))
438 return Inner == FS->getBody();
439 if (const auto *IS = llvm::dyn_cast<IfStmt>(Val: 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).
447bool 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>(Val: 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: *N);
472 if (IsBinOp && BinaryOperator::isAssignmentOp(Opc: 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.
538const 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>(Val: SelectedExpr) ||
547 llvm::isa<MemberExpr>(Val: SelectedExpr))
548 if (const SelectionTree::Node *Call = getCallExpr(DeclRef: 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>(Val: SelectedExpr)) {
553 if (BinOpExpr->getOpcode() == BinaryOperatorKind::BO_Assign)
554 return nullptr;
555 }
556 if (!TargetNode || !eligibleForExtraction(N: 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;
568class ExtractVariable : public Tweak {
569public:
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
580private:
581 // the expression to extract
582 std::unique_ptr<ExtractionContext> Target;
583};
584REGISTER_TWEAK(ExtractVariable)
585bool 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(N: Inputs.ASTSelection.commonAncestor()))
593 Target = std::make_unique<ExtractionContext>(args&: N, args: SM, args: Ctx);
594 return Target && Target->isExtractable();
595}
596
597Expected<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(R: Target->insertDeclaration(VarName, InitializerChars: Range)))
604 return std::move(Err);
605 // replace expression with variable name
606 if (auto Err = Result.add(R: Target->replaceWithVar(Chars: Range, VarName)))
607 return std::move(Err);
608 return Effect::mainFileEdit(SM: Inputs.AST->getSourceManager(),
609 Replacements: std::move(Result));
610}
611
612} // namespace
613} // namespace clangd
614} // namespace clang
615

source code of clang-tools-extra/clangd/refactor/tweaks/ExtractVariable.cpp