14 #include "clang/AST/ASTContext.h" 15 #include "clang/AST/Expr.h" 16 #include "clang/AST/OperationKinds.h" 17 #include "clang/AST/RecursiveASTVisitor.h" 18 #include "clang/AST/Stmt.h" 19 #include "clang/AST/StmtCXX.h" 20 #include "clang/Basic/LangOptions.h" 21 #include "clang/Basic/SourceLocation.h" 22 #include "clang/Basic/SourceManager.h" 23 #include "clang/Tooling/Core/Replacement.h" 24 #include "llvm/ADT/None.h" 25 #include "llvm/ADT/SmallVector.h" 26 #include "llvm/ADT/StringRef.h" 27 #include "llvm/Support/Casting.h" 28 #include "llvm/Support/Error.h" 34 class ExtractionContext {
36 ExtractionContext(
const SelectionTree::Node *Node,
const SourceManager &SM,
37 const ASTContext &
Ctx);
38 const clang::Expr *getExpr()
const {
return Expr; }
39 const SelectionTree::Node *getExprNode()
const {
return ExprNode; }
40 bool isExtractable()
const {
return Extractable; }
42 tooling::Replacement replaceWithVar(llvm::StringRef VarName)
const;
44 tooling::Replacement insertDeclaration(llvm::StringRef VarName)
const;
47 bool Extractable =
false;
48 const clang::Expr *Expr;
49 const SelectionTree::Node *ExprNode;
51 const clang::Stmt *InsertionPoint =
nullptr;
52 const SourceManager &SM;
53 const ASTContext &
Ctx;
55 std::vector<clang::Decl *> ReferencedDecls;
57 bool exprIsValidOutside(
const clang::Stmt *Scope)
const;
59 const clang::Stmt *computeInsertionPoint()
const;
63 static std::vector<clang::Decl *>
64 computeReferencedDecls(
const clang::Expr *Expr) {
66 class FindDeclRefsVisitor
67 :
public clang::RecursiveASTVisitor<FindDeclRefsVisitor> {
69 std::vector<Decl *> ReferencedDecls;
70 bool VisitDeclRefExpr(DeclRefExpr *
DeclRef) {
71 ReferencedDecls.push_back(DeclRef->getDecl());
75 FindDeclRefsVisitor Visitor;
76 Visitor.TraverseStmt(const_cast<Stmt *>(dyn_cast<Stmt>(Expr)));
77 return Visitor.ReferencedDecls;
82 static bool isExtractableExpr(
const clang::Expr *Expr) {
84 const Type *ExprType = Expr->getType().getTypePtrOrNull();
87 return !ExprType->isVoidType();
92 ExtractionContext::ExtractionContext(
const SelectionTree::Node *Node,
93 const SourceManager &SM,
94 const ASTContext &
Ctx)
95 : ExprNode(Node), SM(SM), Ctx(Ctx) {
96 Expr = Node->ASTNode.get<clang::Expr>();
97 if (isExtractableExpr(Expr)) {
98 ReferencedDecls = computeReferencedDecls(Expr);
99 InsertionPoint = computeInsertionPoint();
107 bool ExtractionContext::exprIsValidOutside(
const clang::Stmt *Scope)
const {
108 SourceLocation ScopeBegin = Scope->getBeginLoc();
109 SourceLocation ScopeEnd = Scope->getEndLoc();
110 for (
const Decl *ReferencedDecl : ReferencedDecls) {
111 if (SM.isPointWithin(ReferencedDecl->getBeginLoc(), ScopeBegin, ScopeEnd) &&
112 SM.isPointWithin(ReferencedDecl->getEndLoc(), ScopeBegin, ScopeEnd))
127 const clang::Stmt *ExtractionContext::computeInsertionPoint()
const {
129 auto CanExtractOutside =
130 [](
const SelectionTree::Node *InsertionPoint) ->
bool {
131 if (
const clang::Stmt *Stmt = InsertionPoint->ASTNode.get<clang::Stmt>()) {
134 if (isa<clang::Expr>(Stmt))
135 return !isa<LambdaExpr>(Stmt);
141 return isa<AttributedStmt>(Stmt) || isa<CompoundStmt>(Stmt) ||
142 isa<CXXForRangeStmt>(Stmt) || isa<DeclStmt>(Stmt) ||
143 isa<DoStmt>(Stmt) || isa<ForStmt>(Stmt) || isa<IfStmt>(Stmt) ||
144 isa<LabelStmt>(Stmt) || isa<ReturnStmt>(Stmt) ||
145 isa<WhileStmt>(Stmt);
147 if (InsertionPoint->ASTNode.get<VarDecl>())
151 for (
const SelectionTree::Node *CurNode = getExprNode();
152 CurNode->Parent && CanExtractOutside(CurNode);
153 CurNode = CurNode->Parent) {
154 const clang::Stmt *CurInsertionPoint = CurNode->ASTNode.get<Stmt>();
156 if (CurInsertionPoint && !exprIsValidOutside(CurInsertionPoint))
158 if (
const clang::Stmt *CurParent = CurNode->Parent->ASTNode.get<Stmt>()) {
159 if (isa<CompoundStmt>(CurParent)) {
161 if (CurParent->getBeginLoc().isMacroID())
163 return CurInsertionPoint;
171 ExtractionContext::replaceWithVar(llvm::StringRef VarName)
const {
172 const llvm::Optional<SourceRange> ExtractionRng =
174 unsigned ExtractionLength = SM.getFileOffset(ExtractionRng->getEnd()) -
175 SM.getFileOffset(ExtractionRng->getBegin());
176 return tooling::Replacement(SM, ExtractionRng->getBegin(), ExtractionLength,
181 ExtractionContext::insertDeclaration(llvm::StringRef VarName)
const {
182 const llvm::Optional<SourceRange> ExtractionRng =
184 assert(ExtractionRng &&
"ExtractionRng should not be null");
185 llvm::StringRef ExtractionCode =
toSourceCode(SM, *ExtractionRng);
186 const SourceLocation InsertionLoc =
188 InsertionPoint->getSourceRange())
191 std::string ExtractedVarDecl = std::string(
"auto ") + VarName.str() +
" = " +
192 ExtractionCode.str() +
"; ";
193 return tooling::Replacement(SM, InsertionLoc, 0, ExtractedVarDecl);
203 class ExtractVariable :
public Tweak {
205 const char *id() const override final;
206 bool prepare(const Selection &Inputs) override;
207 Expected<Effect> apply(const Selection &Inputs) override;
208 std::
string title()
const override {
209 return "Extract subexpression to variable";
211 Intent intent()
const override {
return Refactor; }
215 std::unique_ptr<ExtractionContext> Target;
218 bool ExtractVariable::prepare(
const Selection &Inputs) {
219 const ASTContext &Ctx = Inputs.AST.getASTContext();
220 const SourceManager &SM = Inputs.AST.getSourceManager();
221 const SelectionTree::Node *N = Inputs.ASTSelection.commonAncestor();
223 if (!N || Inputs.SelectionBegin == Inputs.SelectionEnd)
225 Target = llvm::make_unique<ExtractionContext>(N, SM,
Ctx);
226 return Target->isExtractable();
229 Expected<Tweak::Effect> ExtractVariable::apply(
const Selection &Inputs) {
230 tooling::Replacements
Result;
232 std::string VarName =
"dummy";
234 if (
auto Err = Result.add(Target->insertDeclaration(VarName)))
235 return std::move(Err);
237 if (
auto Err = Result.add(Target->replaceWithVar(VarName)))
238 return std::move(Err);
239 return Effect::applyEdit(Result);
#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++ -*-===//
llvm::Optional< llvm::Expected< tooling::AtomicChanges > > Result
std::vector< const char * > Expected
const DeclRefExpr * DeclRef