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"
38class ExtractionContext {
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; }
46 SourceRange getExtractionChars()
const;
48 tooling::Replacement replaceWithVar(SourceRange Chars,
49 llvm::StringRef VarName)
const;
51 tooling::Replacement insertDeclaration(llvm::StringRef VarName,
52 SourceRange InitChars)
const;
55 bool Extractable =
false;
56 const clang::Expr *Expr;
58 const SelectionTree::Node *ExprNode;
60 const clang::Stmt *InsertionPoint =
nullptr;
61 const SourceManager &SM;
62 const ASTContext &Ctx;
64 std::vector<clang::Decl *> ReferencedDecls;
66 bool exprIsValidOutside(
const clang::Stmt *Scope)
const;
68 const clang::Stmt *computeInsertionPoint()
const;
72static std::vector<clang::Decl *>
73computeReferencedDecls(
const clang::Expr *Expr) {
75 class FindDeclRefsVisitor
76 :
public clang::RecursiveASTVisitor<FindDeclRefsVisitor> {
78 std::vector<Decl *> ReferencedDecls;
79 bool VisitDeclRefExpr(DeclRefExpr *
DeclRef) {
82 if (
const auto *
const Method =
83 llvm::dyn_cast<CXXMethodDecl>(
DeclRef->getDecl());
84 Method !=
nullptr &&
Method->getParent()->isLambda()) {
87 ReferencedDecls.push_back(
DeclRef->getDecl());
96 bool TraverseLambdaExpr(LambdaExpr *LExpr) {
97 for (
const auto &[Capture, Initializer] :
98 llvm::zip(LExpr->captures(), LExpr->capture_inits())) {
99 TraverseLambdaCapture(LExpr, &Capture, Initializer);
102 if (clang::Expr *
const RequiresClause =
103 LExpr->getTrailingRequiresClause()) {
104 TraverseStmt(RequiresClause);
107 for (
auto *
const TemplateParam : LExpr->getExplicitTemplateParameters())
108 TraverseDecl(TemplateParam);
110 if (
auto *
const CallOperator = LExpr->getCallOperator()) {
111 TraverseType(CallOperator->getDeclaredReturnType());
113 for (
auto *
const Param : CallOperator->parameters()) {
114 TraverseParmVarDecl(Param);
117 for (
auto *
const Attr : CallOperator->attrs()) {
126 FindDeclRefsVisitor Visitor;
127 Visitor.TraverseStmt(
const_cast<Stmt *
>(cast<Stmt>(Expr)));
128 return Visitor.ReferencedDecls;
131static QualType computeVariableType(
const Expr *Expr,
const ASTContext &Ctx) {
132 if (Ctx.getLangOpts().CPlusPlus11)
133 return Ctx.getAutoDeductType();
135 if (Expr->hasPlaceholderType(BuiltinType::PseudoObject)) {
136 if (
const auto *PR = dyn_cast<ObjCPropertyRefExpr>(Expr)) {
137 if (PR->isMessagingSetter()) {
143 }
else if (PR->isMessagingGetter()) {
144 if (PR->isExplicitProperty())
145 return PR->getExplicitProperty()->getType();
147 return PR->getImplicitPropertyGetter()->getReturnType();
153 return Expr->getType();
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();
165 VarType = computeVariableType(Expr, Ctx);
166 if (VarType.isNull())
170 AttributedType::stripOuterNullability(VarType);
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(ReferencedDecl->getBeginLoc(), ScopeBegin, ScopeEnd) &&
181 SM.isPointWithin(ReferencedDecl->getEndLoc(), ScopeBegin, ScopeEnd))
196const clang::Stmt *ExtractionContext::computeInsertionPoint()
const {
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)) {
204 if (InsertionPoint->Parent->ASTNode.get<ParmVarDecl>() !=
nullptr) {
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);
221 if (InsertionPoint->ASTNode.get<VarDecl>())
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>();
230 if (CurInsertionPoint && !exprIsValidOutside(CurInsertionPoint))
232 if (
const clang::Stmt *CurParent = CurNode->Parent->ASTNode.get<Stmt>()) {
233 if (isa<CompoundStmt>(CurParent)) {
235 if (CurParent->getBeginLoc().isMacroID())
237 return CurInsertionPoint;
246ExtractionContext::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);
254ExtractionContext::insertDeclaration(llvm::StringRef VarName,
255 SourceRange InitializerChars)
const {
256 llvm::StringRef ExtractionCode =
toSourceCode(SM, InitializerChars);
257 const SourceLocation InsertionLoc =
259 InsertionPoint->getSourceRange())
261 std::string ExtractedVarDecl =
262 printType(VarType, ExprNode->getDeclContext(), VarName) +
" = " +
263 ExtractionCode.str() +
"; ";
264 return tooling::Replacement(SM, InsertionLoc, 0, ExtractedVarDecl);
285struct ParsedBinaryOperator {
291 bool parse(
const SelectionTree::Node &N) {
294 if (
const BinaryOperator *Op =
295 llvm::dyn_cast_or_null<BinaryOperator>(N.ASTNode.get<Expr>())) {
296 Kind = Op->getOpcode();
301 if (
const CXXOperatorCallExpr *Op =
302 llvm::dyn_cast_or_null<CXXOperatorCallExpr>(
303 N.ASTNode.get<Expr>())) {
304 if (!Op->isInfixBinaryOp())
307 Kind = BinaryOperator::getOverloadedOpcode(Op->getOperator());
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))
321 bool associative()
const {
337 bool crossesMacroBoundary(
const SourceManager &SM) {
338 FileID F = SM.getFileID(
ExprLoc);
340 if (SM.getFileID(Child->ASTNode.get<Expr>()->getExprLoc()) != F)
358const SourceRange getBinaryOperatorRange(
const SelectionTree::Node &N,
359 const SourceManager &SM,
360 const LangOptions &LangOpts) {
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;
372 const SelectionTree::Node *Start = Op.SelectedOperands.front();
373 const SelectionTree::Node *End = Op.SelectedOperands.back();
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) {
380 Start = Op.SelectedOperands.back();
384 Start = Op.SelectedOperands.front();
394SourceRange ExtractionContext::getExtractionChars()
const {
396 SourceRange BinaryOperatorRange =
397 getBinaryOperatorRange(*ExprNode, SM, Ctx.getLangOpts());
398 if (BinaryOperatorRange.isValid())
399 return BinaryOperatorRange;
406const SelectionTree::Node *getCallExpr(
const SelectionTree::Node *
DeclRef) {
407 const SelectionTree::Node &MaybeCallee =
DeclRef->outerImplicit();
408 const SelectionTree::Node *MaybeCall = MaybeCallee.Parent;
412 llvm::dyn_cast_or_null<CallExpr>(MaybeCall->ASTNode.get<Expr>());
415 if (
CE->getCallee() != MaybeCallee.ASTNode.get<Expr>())
422bool childExprIsStmt(
const Stmt *Outer,
const Expr *
Inner) {
423 if (!Outer || !
Inner)
426 if (llvm::isa<CompoundStmt>(Outer))
428 if (llvm::isa<SwitchCase>(Outer))
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();
447bool eligibleForExtraction(
const SelectionTree::Node *N) {
448 const Expr *
E = N->ASTNode.get<Expr>();
453 const Type *ExprType =
E->getType().getTypePtrOrNull();
454 if (!ExprType || ExprType->isVoidType())
459 if (llvm::isa<DeclRefExpr>(
E))
463 if (
const auto *ME = dyn_cast<MemberExpr>(
E))
464 if (
const auto *TE = dyn_cast<CXXThisExpr>(ME->getBase()->IgnoreImpCasts()))
465 if (TE->isImplicit())
470 ParsedBinaryOperator BinOp;
471 bool IsBinOp = BinOp.parse(*N);
472 if (IsBinOp && BinaryOperator::isAssignmentOp(BinOp.Kind))
475 const SelectionTree::Node &OuterImplicit = N->outerImplicit();
476 const auto *
Parent = OuterImplicit.Parent;
483 if (childExprIsStmt(
Parent->ASTNode.get<Stmt>(),
484 OuterImplicit.ASTNode.get<Expr>()))
487 std::function<bool(
const SelectionTree::Node *)> IsFullySelected =
488 [&](
const SelectionTree::Node *N) {
489 if (N->ASTNode.getSourceRange().isValid() &&
490 N->Selected != SelectionTree::Complete)
492 for (
const auto *Child : N->Children) {
493 if (!IsFullySelected(Child))
498 auto ExprIsFullySelectedTargetNode = [&](
const Expr *
E) {
499 if (
E != OuterImplicit.ASTNode.get<Expr>())
508 return IsFullySelected(N);
514 if (
const auto *BO =
Parent->ASTNode.get<BinaryOperator>()) {
515 if (BO->isAssignmentOp() && ExprIsFullySelectedTargetNode(BO->getRHS()))
524 if (
const auto *
Decl =
Parent->ASTNode.get<VarDecl>()) {
525 if (!
Decl->isInitCapture() &&
526 ExprIsFullySelectedTargetNode(
Decl->getInit())) {
538const SelectionTree::Node *computeExtractedExpr(
const SelectionTree::Node *N) {
541 const SelectionTree::Node *TargetNode = N;
542 const clang::Expr *SelectedExpr = N->ASTNode.get<clang::Expr>();
546 if (llvm::isa<DeclRefExpr>(SelectedExpr) ||
547 llvm::isa<MemberExpr>(SelectedExpr))
548 if (
const SelectionTree::Node *Call = getCallExpr(N))
551 if (
const BinaryOperator *BinOpExpr =
552 dyn_cast_or_null<BinaryOperator>(SelectedExpr)) {
553 if (BinOpExpr->getOpcode() == BinaryOperatorKind::BO_Assign)
556 if (!TargetNode || !eligibleForExtraction(TargetNode))
568class ExtractVariable :
public Tweak {
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";
576 llvm::StringLiteral kind()
const override {
577 return CodeAction::REFACTOR_KIND;
582 std::unique_ptr<ExtractionContext> Target;
585bool ExtractVariable::prepare(
const Selection &Inputs) {
587 if (Inputs.SelectionBegin == Inputs.SelectionEnd)
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();
597Expected<Tweak::Effect> ExtractVariable::apply(
const Selection &Inputs) {
598 tooling::Replacements Result;
600 std::string VarName =
"placeholder";
601 SourceRange
Range = Target->getExtractionChars();
603 if (
auto Err = Result.add(Target->insertDeclaration(VarName,
Range)))
604 return std::move(Err);
606 if (
auto Err = Result.add(Target->replaceWithVar(
Range, VarName)))
607 return std::move(Err);
608 return Effect::mainFileEdit(Inputs.AST->getSourceManager(),
const FunctionDecl * Decl
std::pair< Context, Canceler > Inner
CharSourceRange Range
SourceRange for the file name.
std::vector< const char * > Expected
::clang::DynTypedNode Node
#define REGISTER_TWEAK(Subclass)
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.
std::string printType(const QualType QT, const DeclContext &CurContext, const llvm::StringRef Placeholder)
Returns a QualType as string.
llvm::StringRef toSourceCode(const SourceManager &SM, SourceRange R)
Returns the source code covered by the source range.
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//