14 #include "clang/AST/ASTContext.h" 15 #include "clang/AST/Expr.h" 16 #include "clang/AST/ExprCXX.h" 17 #include "clang/AST/OperationKinds.h" 18 #include "clang/AST/RecursiveASTVisitor.h" 19 #include "clang/AST/Stmt.h" 20 #include "clang/AST/StmtCXX.h" 21 #include "clang/Basic/LangOptions.h" 22 #include "clang/Basic/SourceLocation.h" 23 #include "clang/Basic/SourceManager.h" 24 #include "clang/Tooling/Core/Replacement.h" 25 #include "llvm/ADT/None.h" 26 #include "llvm/ADT/SmallVector.h" 27 #include "llvm/ADT/StringRef.h" 28 #include "llvm/Support/Casting.h" 29 #include "llvm/Support/Error.h" 30 #include "llvm/Support/raw_ostream.h" 36 class ExtractionContext {
38 ExtractionContext(
const SelectionTree::Node *Node,
const SourceManager &SM,
39 const ASTContext &
Ctx);
40 const clang::Expr *getExpr()
const {
return Expr; }
41 const SelectionTree::Node *getExprNode()
const {
return ExprNode; }
42 bool isExtractable()
const {
return Extractable; }
44 SourceRange getExtractionChars()
const;
46 tooling::Replacement replaceWithVar(SourceRange Chars,
47 llvm::StringRef VarName)
const;
49 tooling::Replacement insertDeclaration(llvm::StringRef VarName,
50 SourceRange InitChars)
const;
53 bool Extractable =
false;
54 const clang::Expr *Expr;
55 const SelectionTree::Node *ExprNode;
58 const SourceManager &SM;
59 const ASTContext &
Ctx;
61 std::vector<clang::Decl *> ReferencedDecls;
63 bool exprIsValidOutside(
const clang::Stmt *Scope)
const;
65 const clang::Stmt *computeInsertionPoint()
const;
69 static std::vector<clang::Decl *>
70 computeReferencedDecls(
const clang::Expr *Expr) {
72 class FindDeclRefsVisitor
73 :
public clang::RecursiveASTVisitor<FindDeclRefsVisitor> {
75 std::vector<Decl *> ReferencedDecls;
76 bool VisitDeclRefExpr(DeclRefExpr *
DeclRef) {
77 ReferencedDecls.push_back(DeclRef->getDecl());
81 FindDeclRefsVisitor Visitor;
82 Visitor.TraverseStmt(const_cast<Stmt *>(dyn_cast<Stmt>(Expr)));
83 return Visitor.ReferencedDecls;
86 ExtractionContext::ExtractionContext(
const SelectionTree::Node *Node,
87 const SourceManager &SM,
88 const ASTContext &
Ctx)
89 : ExprNode(Node), SM(SM), Ctx(Ctx) {
90 Expr = Node->ASTNode.get<clang::Expr>();
91 ReferencedDecls = computeReferencedDecls(Expr);
99 bool ExtractionContext::exprIsValidOutside(
const clang::Stmt *Scope)
const {
100 SourceLocation ScopeBegin = Scope->getBeginLoc();
101 SourceLocation ScopeEnd = Scope->getEndLoc();
102 for (
const Decl *ReferencedDecl : ReferencedDecls) {
103 if (SM.isPointWithin(ReferencedDecl->getBeginLoc(), ScopeBegin, ScopeEnd) &&
104 SM.isPointWithin(ReferencedDecl->getEndLoc(), ScopeBegin, ScopeEnd))
119 const clang::Stmt *ExtractionContext::computeInsertionPoint()
const {
121 auto CanExtractOutside =
123 if (
const clang::Stmt *Stmt =
InsertionPoint->ASTNode.get<clang::Stmt>()) {
126 if (isa<clang::Expr>(Stmt))
127 return !isa<LambdaExpr>(Stmt);
133 return isa<AttributedStmt>(Stmt) || isa<CompoundStmt>(Stmt) ||
134 isa<CXXForRangeStmt>(Stmt) || isa<DeclStmt>(Stmt) ||
135 isa<DoStmt>(Stmt) || isa<ForStmt>(Stmt) || isa<IfStmt>(Stmt) ||
136 isa<ReturnStmt>(Stmt) || isa<WhileStmt>(Stmt);
142 for (
const SelectionTree::Node *CurNode = getExprNode();
143 CurNode->Parent && CanExtractOutside(CurNode);
144 CurNode = CurNode->Parent) {
145 const clang::Stmt *CurInsertionPoint = CurNode->ASTNode.get<Stmt>();
147 if (CurInsertionPoint && !exprIsValidOutside(CurInsertionPoint))
149 if (
const clang::Stmt *CurParent = CurNode->Parent->ASTNode.get<Stmt>()) {
150 if (isa<CompoundStmt>(CurParent)) {
152 if (CurParent->getBeginLoc().isMacroID())
154 return CurInsertionPoint;
163 ExtractionContext::replaceWithVar(SourceRange Chars,
164 llvm::StringRef VarName)
const {
165 unsigned ExtractionLength =
166 SM.getFileOffset(Chars.getEnd()) - SM.getFileOffset(Chars.getBegin());
167 return tooling::Replacement(SM, Chars.getBegin(), ExtractionLength, VarName);
171 ExtractionContext::insertDeclaration(llvm::StringRef VarName,
172 SourceRange InitializerChars)
const {
173 llvm::StringRef ExtractionCode =
toSourceCode(SM, InitializerChars);
174 const SourceLocation InsertionLoc =
179 std::string ExtractedVarDecl = std::string(
"auto ") + VarName.str() +
" = " +
180 ExtractionCode.str() +
"; ";
181 return tooling::Replacement(SM, InsertionLoc, 0, ExtractedVarDecl);
202 struct ParsedBinaryOperator {
208 bool parse(
const SelectionTree::Node &N) {
209 SelectedOperands.clear();
211 if (
const BinaryOperator *Op =
212 llvm::dyn_cast_or_null<BinaryOperator>(N.ASTNode.get<Expr>())) {
213 Kind = Op->getOpcode();
214 ExprLoc = Op->getExprLoc();
215 SelectedOperands = N.Children;
218 if (
const CXXOperatorCallExpr *Op =
219 llvm::dyn_cast_or_null<CXXOperatorCallExpr>(
220 N.ASTNode.get<Expr>())) {
221 if (!Op->isInfixBinaryOp())
224 Kind = BinaryOperator::getOverloadedOpcode(Op->getOperator());
225 ExprLoc = Op->getExprLoc();
227 for (
const auto* Child : N.Children) {
228 const Expr *
E = Child->ASTNode.get<Expr>();
229 assert(E &&
"callee and args should be Exprs!");
230 if (E == Op->getArg(0) || E == Op->getArg(1))
231 SelectedOperands.push_back(Child);
238 bool associative()
const {
254 bool crossesMacroBoundary(
const SourceManager &SM) {
255 FileID F = SM.getFileID(ExprLoc);
256 for (
const SelectionTree::Node *Child : SelectedOperands)
257 if (SM.getFileID(Child->ASTNode.get<Expr>()->getExprLoc()) != F)
275 const SourceRange getBinaryOperatorRange(
const SelectionTree::Node &N,
276 const SourceManager &SM,
277 const LangOptions &LangOpts) {
279 ParsedBinaryOperator Op;
280 if (!Op.parse(N.ignoreImplicit()) || !Op.associative() ||
281 Op.crossesMacroBoundary(SM) || Op.SelectedOperands.size() != 2)
282 return SourceRange();
283 BinaryOperatorKind OuterOp = Op.Kind;
289 const SelectionTree::Node *Start = Op.SelectedOperands.front();
290 const SelectionTree::Node *End = Op.SelectedOperands.back();
293 while (Op.parse(Start->ignoreImplicit()) && Op.Kind == OuterOp &&
294 !Op.crossesMacroBoundary(SM)) {
295 assert(!Op.SelectedOperands.empty() &&
"got only operator on one side!");
296 if (Op.SelectedOperands.size() == 1) {
297 Start = Op.SelectedOperands.back();
301 Start = Op.SelectedOperands.front();
311 SourceRange ExtractionContext::getExtractionChars()
const {
313 SourceRange BinaryOperatorRange =
314 getBinaryOperatorRange(*ExprNode, SM, Ctx.getLangOpts());
315 if (BinaryOperatorRange.isValid())
316 return BinaryOperatorRange;
323 const SelectionTree::Node *getCallExpr(
const SelectionTree::Node *
DeclRef) {
324 const SelectionTree::Node &MaybeCallee = DeclRef->outerImplicit();
325 const SelectionTree::Node *MaybeCall = MaybeCallee.Parent;
329 llvm::dyn_cast_or_null<CallExpr>(MaybeCall->ASTNode.get<Expr>());
332 if (CE->getCallee() != MaybeCallee.ASTNode.get<Expr>())
339 bool childExprIsStmt(
const Stmt *Outer,
const Expr *Inner) {
340 if (!Outer || !Inner)
343 if (llvm::isa<CompoundStmt>(Outer))
345 if (llvm::isa<SwitchCase>(Outer))
348 if (
const auto* WS = llvm::dyn_cast<WhileStmt>(Outer))
349 return Inner == WS->getBody();
350 if (
const auto* DS = llvm::dyn_cast<DoStmt>(Outer))
351 return Inner == DS->getBody();
352 if (
const auto*
FS = llvm::dyn_cast<ForStmt>(Outer))
353 return Inner ==
FS->getBody();
354 if (
const auto*
FS = llvm::dyn_cast<CXXForRangeStmt>(Outer))
355 return Inner ==
FS->getBody();
356 if (
const auto* IS = llvm::dyn_cast<IfStmt>(Outer))
357 return Inner == IS->getThen() || Inner == IS->getElse();
364 bool eligibleForExtraction(
const SelectionTree::Node *N) {
365 const Expr *
E = N->ASTNode.get<Expr>();
370 if (
const Type *ExprType = E->getType().getTypePtrOrNull())
371 if (ExprType->isVoidType())
376 if (llvm::isa<DeclRefExpr>(E) || llvm::isa<MemberExpr>(
E))
381 ParsedBinaryOperator BinOp;
382 if (BinOp.parse(*N) && BinaryOperator::isAssignmentOp(BinOp.Kind))
389 const SelectionTree::Node &OuterImplicit = N->outerImplicit();
390 if (!OuterImplicit.Parent ||
391 childExprIsStmt(OuterImplicit.Parent->ASTNode.get<Stmt>(),
392 OuterImplicit.ASTNode.get<Expr>()))
403 const SelectionTree::Node *computeExtractedExpr(
const SelectionTree::Node *N) {
406 const SelectionTree::Node *TargetNode = N;
407 const clang::Expr *SelectedExpr = N->ASTNode.get<clang::Expr>();
411 if (llvm::isa<DeclRefExpr>(SelectedExpr) ||
412 llvm::isa<MemberExpr>(SelectedExpr))
413 if (
const SelectionTree::Node *Call = getCallExpr(N))
416 if (
const BinaryOperator *BinOpExpr =
417 dyn_cast_or_null<BinaryOperator>(SelectedExpr)) {
418 if (BinOpExpr->getOpcode() == BinaryOperatorKind::BO_Assign)
421 if (!TargetNode || !eligibleForExtraction(TargetNode))
433 class ExtractVariable :
public Tweak {
435 const char *id()
const override final;
436 bool prepare(
const Selection &Inputs)
override;
437 Expected<Effect> apply(
const Selection &Inputs)
override;
438 std::string title()
const override {
439 return "Extract subexpression to variable";
441 Intent intent()
const override {
return Refactor; }
445 std::unique_ptr<ExtractionContext> Target;
448 bool ExtractVariable::prepare(
const Selection &Inputs) {
450 if (Inputs.SelectionBegin == Inputs.SelectionEnd)
452 const ASTContext &Ctx = Inputs.AST->getASTContext();
453 const SourceManager &SM = Inputs.AST->getSourceManager();
454 if (
const SelectionTree::Node *N =
455 computeExtractedExpr(Inputs.ASTSelection.commonAncestor()))
456 Target = std::make_unique<ExtractionContext>(N, SM,
Ctx);
457 return Target && Target->isExtractable();
460 Expected<Tweak::Effect> ExtractVariable::apply(
const Selection &Inputs) {
461 tooling::Replacements Result;
463 std::string VarName =
"dummy";
464 SourceRange
Range = Target->getExtractionChars();
466 if (
auto Err = Result.add(Target->insertDeclaration(VarName, Range)))
467 return std::move(Err);
469 if (
auto Err = Result.add(Target->replaceWithVar(Range, VarName)))
470 return std::move(Err);
471 return Effect::mainFileEdit(Inputs.AST->getSourceManager(),
const FunctionDecl * Decl
#define REGISTER_TWEAK(Subclass)
llvm::Optional< SourceRange > toHalfOpenFileRange(const SourceManager &SM, const LangOptions &LangOpts, SourceRange R)
Turns a token range into a half-open range and checks its correctness.
llvm::StringRef toSourceCode(const SourceManager &SM, SourceRange R)
Returns the source code covered by the source range.
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//
CharSourceRange Range
SourceRange for the file name.
const DeclRefExpr * DeclRef