| //===--- ExtractRepeatedExpressionIntoVariable.cpp - ---------------------===// |
| // |
| // The LLVM Compiler Infrastructure |
| // |
| // This file is distributed under the University of Illinois Open Source |
| // License. See LICENSE.TXT for details. |
| // |
| //===----------------------------------------------------------------------===// |
| // |
| // Implements the "Extract repeated expression into variable" refactoring |
| // operation. |
| // |
| //===----------------------------------------------------------------------===// |
| |
| #include "ExtractionUtils.h" |
| #include "RefactoringOperations.h" |
| #include "SourceLocationUtilities.h" |
| #include "clang/AST/AST.h" |
| #include "clang/AST/ASTContext.h" |
| #include "clang/AST/Expr.h" |
| #include "clang/AST/RecursiveASTVisitor.h" |
| |
| using namespace clang; |
| using namespace clang::tooling; |
| |
| namespace { |
| |
| class ExtractRepeatedExpressionIntoVariableOperation |
| : public RefactoringOperation { |
| public: |
| ExtractRepeatedExpressionIntoVariableOperation( |
| const Expr *E, ArrayRef<const Expr *> Duplicates, const Decl *ParentDecl) |
| : E(E), DuplicateExpressions(Duplicates.begin(), Duplicates.end()), |
| ParentDecl(ParentDecl) {} |
| |
| const Stmt *getTransformedStmt() const override { return E; } |
| |
| llvm::Expected<RefactoringResult> perform(ASTContext &Context, const Preprocessor &ThePreprocessor, |
| const RefactoringOptionSet &Options, |
| unsigned SelectedCandidateIndex) override; |
| |
| const Expr *E; |
| SmallVector<const Expr *, 4> DuplicateExpressions; |
| const Decl *ParentDecl; |
| }; |
| |
| using UseOfDeclaration = std::pair<const Decl *, unsigned>; |
| |
| bool shouldIgnoreParens(const ParenExpr *E) { |
| if (!E) |
| return false; |
| const Expr *Child = E->getSubExpr(); |
| // Ignore the parens unless they are around an expression that |
| // really needs them. |
| if (isa<UnaryOperator>(Child) || isa<BinaryOperator>(Child) || |
| isa<AbstractConditionalOperator>(Child) || |
| isa<CXXOperatorCallExpr>(Child)) |
| return false; |
| return true; |
| } |
| |
| /// Builds up a list of declarations that are used in an expression. |
| class DuplicateExprSemanticProfiler |
| : public RecursiveASTVisitor<DuplicateExprSemanticProfiler> { |
| unsigned Index = 0; |
| llvm::SmallVectorImpl<UseOfDeclaration> &DeclRefs; |
| |
| public: |
| DuplicateExprSemanticProfiler( |
| llvm::SmallVectorImpl<UseOfDeclaration> &DeclRefs) |
| : DeclRefs(DeclRefs) { |
| DeclRefs.clear(); |
| } |
| |
| bool VisitStmt(const Stmt *S) { |
| if (!shouldIgnoreParens(dyn_cast<ParenExpr>(S))) |
| ++Index; |
| return true; |
| } |
| |
| bool VisitDeclRefExpr(const DeclRefExpr *E) { |
| if (E->getDecl()) |
| DeclRefs.emplace_back(E->getDecl(), Index); |
| return true; |
| } |
| }; |
| |
| class DuplicateExprFinder : public RecursiveASTVisitor<DuplicateExprFinder>, |
| PrinterHelper { |
| const Expr *Target; |
| const ASTContext &Context; |
| const PrintingPolicy &PP; |
| Stmt::StmtClass ExprKind; |
| QualType T; |
| std::string ExprString, OSString; |
| llvm::SmallVector<UseOfDeclaration, 8> ExprDecls, DeclUses; |
| |
| void printExpr(std::string &Str, const Expr *E) { |
| llvm::raw_string_ostream OS(Str); |
| E->printPretty(OS, /*Helper=*/this, PP); |
| } |
| |
| public: |
| SmallVector<const Expr *, 4> DuplicateExpressions; |
| |
| DuplicateExprFinder(const Expr *E, const ASTContext &Context, |
| const PrintingPolicy &PP) |
| : Target(E), Context(Context), PP(PP), ExprKind(E->getStmtClass()), |
| T(E->getType()) { |
| printExpr(ExprString, E); |
| DuplicateExprSemanticProfiler(ExprDecls).TraverseStmt( |
| const_cast<Expr *>(E)); |
| } |
| |
| bool handledStmt(Stmt *E, raw_ostream &OS) final override { |
| if (const auto *Paren = dyn_cast<ParenExpr>(E)) { |
| if (!shouldIgnoreParens(Paren)) |
| return false; |
| Paren->getSubExpr()->printPretty(OS, /*Helper=*/this, PP); |
| return true; |
| } |
| return false; |
| } |
| |
| bool VisitStmt(const Stmt *S) { |
| if (S->getStmtClass() != ExprKind) |
| return true; |
| const auto *E = cast<Expr>(S); |
| if (E == Target) { |
| DuplicateExpressions.push_back(E); |
| return true; |
| } |
| // The expression should not be in a macro. |
| SourceRange R = E->getSourceRange(); |
| if (R.getBegin().isMacroID()) { |
| if (!Context.getSourceManager().isMacroArgExpansion(R.getBegin())) |
| return true; |
| } |
| if (R.getEnd().isMacroID()) { |
| if (!Context.getSourceManager().isMacroArgExpansion(R.getEnd())) |
| return true; |
| } |
| // The expression types should match. |
| if (E->getType() != T) |
| return true; |
| // Check if the expression is a duplicate by comparing their lexical |
| // representations. |
| OSString.clear(); |
| printExpr(OSString, E); |
| if (OSString == ExprString) { |
| DuplicateExprSemanticProfiler(DeclUses).TraverseStmt( |
| const_cast<Expr *>(E)); |
| // Check if they're semantically equivalent. |
| if (ExprDecls.size() == DeclUses.size() && |
| std::equal(ExprDecls.begin(), ExprDecls.end(), DeclUses.begin())) |
| DuplicateExpressions.push_back(E); |
| } |
| return true; |
| } |
| }; |
| |
| } // end anonymous namespace |
| |
| static QualType returnTypeOfCall(const Expr *E) { |
| if (const auto *Call = dyn_cast<CallExpr>(E)) { |
| if (const auto *Fn = Call->getDirectCallee()) |
| return Fn->getReturnType(); |
| } else if (const auto *Msg = dyn_cast<ObjCMessageExpr>(E)) { |
| if (const auto *M = Msg->getMethodDecl()) |
| return M->getReturnType(); |
| } else if (const auto *PRE = dyn_cast<ObjCPropertyRefExpr>(E)) { |
| if (PRE->isImplicitProperty()) { |
| if (const auto *M = PRE->getImplicitPropertyGetter()) |
| return M->getReturnType(); |
| } else if (const auto *Prop = PRE->getExplicitProperty()) |
| return Prop->getType(); |
| } |
| return QualType(); |
| } |
| |
| static bool isRepeatableExpression(const Stmt *S) { |
| if (const auto *Op = dyn_cast<CXXOperatorCallExpr>(S)) |
| return Op->getOperator() == OO_Call || Op->getOperator() == OO_Subscript; |
| return isa<CallExpr>(S) || isa<ObjCMessageExpr>(S) || |
| isa<ObjCPropertyRefExpr>(S); |
| } |
| |
| RefactoringOperationResult |
| clang::tooling::initiateExtractRepeatedExpressionIntoVariableOperation( |
| ASTSlice &Slice, ASTContext &Context, SourceLocation Location, |
| SourceRange SelectionRange, bool CreateOperation) { |
| const Stmt *S; |
| const Decl *ParentDecl; |
| if (SelectionRange.isValid()) { |
| auto SelectedStmt = Slice.getSelectedStmtSet(); |
| if (!SelectedStmt) |
| return None; |
| if (!SelectedStmt->containsSelectionRange) |
| return None; |
| if (!isRepeatableExpression(SelectedStmt->containsSelectionRange)) |
| return None; |
| S = SelectedStmt->containsSelectionRange; |
| ParentDecl = |
| Slice.parentDeclForIndex(*SelectedStmt->containsSelectionRangeIndex); |
| } else { |
| auto SelectedStmt = Slice.nearestSelectedStmt(isRepeatableExpression); |
| if (!SelectedStmt) |
| return None; |
| S = SelectedStmt->getStmt(); |
| ParentDecl = SelectedStmt->getParentDecl(); |
| } |
| |
| const Expr *E = cast<Expr>(S); |
| // Check if the function/method returns a reference/pointer. |
| QualType T = returnTypeOfCall(E); |
| if (!T.getTypePtrOrNull() || |
| (!T->isAnyPointerType() && !T->isReferenceType())) |
| return None; |
| |
| DuplicateExprFinder DupFinder(E, Context, Context.getPrintingPolicy()); |
| DupFinder.TraverseDecl(const_cast<Decl *>(ParentDecl)); |
| if (DupFinder.DuplicateExpressions.size() < 2) |
| return None; |
| |
| RefactoringOperationResult Result; |
| Result.Initiated = true; |
| if (!CreateOperation) |
| return Result; |
| auto Operation = |
| llvm::make_unique<ExtractRepeatedExpressionIntoVariableOperation>( |
| E, DupFinder.DuplicateExpressions, ParentDecl); |
| Result.RefactoringOp = std::move(Operation); |
| return Result; |
| } |
| |
| static StringRef nameForExtractedVariable(const Expr *E) { |
| auto SuggestedName = extract::nameForExtractedVariable(E); |
| if (!SuggestedName) |
| return "duplicate"; |
| return *SuggestedName; |
| } |
| |
| llvm::Expected<RefactoringResult> |
| ExtractRepeatedExpressionIntoVariableOperation::perform( |
| ASTContext &Context, const Preprocessor &ThePreprocessor, |
| const RefactoringOptionSet &Options, unsigned SelectedCandidateIndex) { |
| std::vector<RefactoringReplacement> Replacements; |
| |
| const SourceManager &SM = Context.getSourceManager(); |
| SourceLocation InsertionLoc = |
| extract::locationForExtractedVariableDeclaration(DuplicateExpressions, |
| ParentDecl, SM); |
| if (InsertionLoc.isInvalid()) |
| return llvm::make_error<RefactoringOperationError>( |
| "no appropriate insertion location found"); |
| |
| StringRef Name = nameForExtractedVariable(E); |
| |
| // Create the variable that will hold the value of the duplicate expression. |
| std::string VariableDeclarationString; |
| llvm::raw_string_ostream OS(VariableDeclarationString); |
| QualType T = returnTypeOfCall(E); |
| PrintingPolicy PP = Context.getPrintingPolicy(); |
| PP.SuppressStrongLifetime = true; |
| PP.SuppressLifetimeQualifiers = true; |
| PP.SuppressUnwrittenScope = true; |
| T.print(OS, PP, /*PlaceHolder*/ Name); |
| OS << " = "; |
| PrintingPolicy ExprPP = Context.getPrintingPolicy(); |
| ExprPP.SuppressStrongLifetime = true; |
| ExprPP.SuppressImplicitBase = true; |
| E->printPretty(OS, /*Helper=*/nullptr, ExprPP); |
| OS << ";\n"; |
| Replacements.emplace_back(SourceRange(InsertionLoc, InsertionLoc), OS.str()); |
| |
| // Replace the duplicates with a reference to the variable. |
| for (const Expr *E : DuplicateExpressions) |
| Replacements.emplace_back( |
| SourceRange(SM.getSpellingLoc(E->getLocStart()), |
| getPreciseTokenLocEnd(SM.getSpellingLoc(E->getLocEnd()), SM, |
| Context.getLangOpts())), |
| Name); |
| |
| return std::move(Replacements); |
| } |