clang-tools 20.0.0git
ExtractVariable.cpp
Go to the documentation of this file.
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,
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 extract
61 const clang::Stmt *InsertionPoint = nullptr;
62 const SourceManager &SM;
63 const ASTContext &Ctx;
64 // Decls referenced in the Expr
65 std::vector<clang::Decl *> ReferencedDecls;
66 // returns true if the Expr doesn't reference any variable declared in scope
67 bool exprIsValidOutside(const clang::Stmt *Scope) const;
68 // computes the Stmt before which we will extract out Expr
69 const clang::Stmt *computeInsertionPoint() const;
70};
71
72// Returns all the Decls referenced inside the given Expr
73static std::vector<clang::Decl *>
74computeReferencedDecls(const clang::Expr *Expr) {
75 // RAV subclass to find all DeclRefs in a given Stmt
76 class FindDeclRefsVisitor
77 : public clang::RecursiveASTVisitor<FindDeclRefsVisitor> {
78 public:
79 std::vector<Decl *> ReferencedDecls;
80 bool VisitDeclRefExpr(DeclRefExpr *DeclRef) { // NOLINT
81 // Stop the call operator of lambdas from being marked as a referenced
82 // 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 of
93 // 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 explicit
96 // 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 (clang::Expr *const RequiresClause =
104 LExpr->getTrailingRequiresClause()) {
105 TraverseStmt(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 call
141 // the setter. Non compound access like `self.prop = 1` is invalid since
142 // 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 else
148 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 else
170 // 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 a
175// variable reference out of scope
176bool 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 current
190// Stmt is a CompoundStmt, we can extract inside this CompoundStmt just before
191// the current Stmt. We ALWAYS insert before a Stmt whose parent is a
192// CompoundStmt
193//
194// FIXME: Extraction from label, switch and case statements
195// FIXME: Doens't work for FoldExpr
196// FIXME: Ensure extraction from loops doesn't change semantics.
197const clang::Stmt *ExtractionContext::computeInsertionPoint() const {
198 // returns true if we can extract before InsertionPoint
199 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 parameter
204 // 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 to
213 // jump over the switch stmt even if there is a CompoundStmt inside the
214 // 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 from
216 // 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 scope
231 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 VarName
246tooling::Replacement
247ExtractionContext::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 extraction
254tooling::Replacement
255ExtractionContext::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 associative
279// - no mixing of operators is allowed
280// - we don't look inside macro expansions in the subexpressions
281// - we only adjust the extracted range, so references in the unselected parts
282// of the AST expression (e.g. `a`) are still considered referenced for
283// the purposes of calculating the insertion point.
284// FIXME: it would be nice to exclude these references, by micromanaging
285// 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 find
351// 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]] + d
355// +
356// / \
357// N-> + d
358// / \
359// + c <- End
360// / \
361// a b <- Start
362const SourceRange 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, and
373 // all eligible operators are left-associative, the shape of the tree is
374 // very restricted: it's a linked list along the left edges.
375 // This simplifies our implementation.
376 const SelectionTree::Node *Start = Op.SelectedOperands.front(); // LHS
377 const SelectionTree::Node *End = Op.SelectedOperands.back(); // RHS
378 // 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 selected
384 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) DeclRef
410const 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 as
425// 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 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
524 // The same logic as for assignments applies to initializations.
525 // However, we do allow extracting the RHS of an init capture, as it is
526 // 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/field
541// DeclRefs. For function/member function, we want to extract the entire
542// 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 placeholder
567/// 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 extract
587 std::unique_ptr<ExtractionContext> Target;
588};
589REGISTER_TWEAK(ExtractVariable)
590bool ExtractVariable::prepare(const Selection &Inputs) {
591 // we don't trigger on empty selections for now
592 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 type
605 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 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)))
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} // namespace
632} // namespace clangd
633} // namespace clang
const Expr * E
const FunctionDecl * Decl
BindArgumentKind Kind
CaptureExpr CE
std::pair< Context, Canceler > Inner
const Node * Parent
llvm::SmallVector< const SelectionTree::Node * > SelectedOperands
SourceLocation ExprLoc
NodeType Type
CharSourceRange Range
SourceRange for the file name.
std::vector< const char * > Expected
::clang::DynTypedNode Node
#define REGISTER_TWEAK(Subclass)
Definition: Tweak.h:129
const DeclRefExpr * DeclRef
std::optional< SourceRange > toHalfOpenFileRange(const SourceManager &SM, const LangOptions &LangOpts, SourceRange R)
Turns a token range into a half-open range and checks its correctness.
Definition: SourceCode.cpp:430
std::string printType(const QualType QT, const DeclContext &CurContext, const llvm::StringRef Placeholder)
Returns a QualType as string.
Definition: AST.cpp:394
llvm::StringRef toSourceCode(const SourceManager &SM, SourceRange R)
Returns the source code covered by the source range.
Definition: SourceCode.cpp:452
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//