| //===--- Extract.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" refactoring operation. |
| // |
| //===----------------------------------------------------------------------===// |
| |
| #include "ExtractionUtils.h" |
| #include "RefactoringOperations.h" |
| #include "SourceLocationUtilities.h" |
| #include "StmtUtils.h" |
| #include "TypeUtils.h" |
| #include "clang/AST/AST.h" |
| #include "clang/AST/ASTContext.h" |
| #include "clang/AST/Expr.h" |
| #include "clang/AST/RecursiveASTVisitor.h" |
| #include "clang/ASTMatchers/ASTMatchFinder.h" |
| #include "clang/Lex/Lexer.h" |
| #include "clang/Lex/MacroInfo.h" |
| #include "clang/Lex/Preprocessor.h" |
| #include "clang/Rewrite/Core/Rewriter.h" |
| #include "clang/Tooling/Refactor/RefactoringOptions.h" |
| #include "llvm/ADT/DenseMap.h" |
| #include "llvm/Support/Path.h" |
| #include <algorithm> |
| |
| using namespace clang; |
| using namespace clang::tooling; |
| |
| namespace { |
| |
| struct CompoundStatementRange { |
| CompoundStmt::const_body_iterator First, Last; |
| |
| const Stmt *getFirst() const { |
| // We must have selected just the child of the case, since a selection that |
| // includes the case is treated like a selection of the entire switch. |
| if (const auto *Case = dyn_cast<SwitchCase>(*First)) { |
| if (const Stmt *S = Case->getSubStmt()) |
| return S; |
| } |
| return *First; |
| } |
| |
| const Stmt *getLast() const { return *Last; } |
| |
| // TODO: We might not want to iterate over the switch case if we've just |
| // selected its child. We should switch over to an array of nodes instead of |
| // an iterator pair instead. |
| CompoundStmt::const_body_iterator begin() const { return First; } |
| CompoundStmt::const_body_iterator end() const { return Last + 1; } |
| }; |
| |
| enum class ExtractionKind { Function, Method, Expression }; |
| |
| class ExtractOperation : public RefactoringOperation { |
| public: |
| struct CandidateInfo { |
| CandidateInfo(SourceRange Range, StringRef PreInsertedText = "", |
| const Stmt *AnalyzedStatement = nullptr) |
| : Range(Range), PreInsertedText(PreInsertedText), |
| AnalyzedStatement(AnalyzedStatement) {} |
| |
| /// The candidate token range, i.e. the end location is the starting |
| /// location of the last token. |
| SourceRange Range; |
| /// The text that should be inserted before the call to the extracted |
| /// function. |
| StringRef PreInsertedText; |
| /// The expression that should be analyzed for captured variables and the |
| /// return value. |
| const Stmt *AnalyzedStatement; |
| }; |
| |
| ExtractOperation(const Stmt *S, const Stmt *ParentStmt, |
| const Decl *FunctionLikeParentDecl, |
| std::vector<std::string> Candidates, |
| Optional<CompoundStatementRange> ExtractedStmtRange, |
| Optional<CandidateInfo> FirstCandidateInfo, |
| ExtractionKind Kind) |
| : S(S), ParentStmt(ParentStmt), |
| FunctionLikeParentDecl(FunctionLikeParentDecl), |
| Candidates(std::move(Candidates)), |
| ExtractedStmtRange(ExtractedStmtRange), Kind(Kind) { |
| if (FirstCandidateInfo) |
| CandidateExtractionInfo.push_back(*FirstCandidateInfo); |
| } |
| |
| const Stmt *getTransformedStmt() const override { |
| if (ExtractedStmtRange) |
| return ExtractedStmtRange->getFirst(); |
| return S; |
| } |
| |
| const Stmt *getLastTransformedStmt() const override { |
| if (ExtractedStmtRange) |
| return ExtractedStmtRange->getLast(); |
| return nullptr; |
| } |
| |
| std::vector<std::string> getRefactoringCandidates() override { |
| return Candidates; |
| } |
| |
| std::vector<RefactoringActionType> getAvailableSubActions() override { |
| std::vector<RefactoringActionType> SubActions; |
| if (isa<CXXMethodDecl>(FunctionLikeParentDecl) || |
| isa<ObjCMethodDecl>(FunctionLikeParentDecl)) |
| SubActions.push_back(RefactoringActionType::Extract_Method); |
| if (isLexicalExpression(S, ParentStmt)) |
| SubActions.push_back(RefactoringActionType::Extract_Expression); |
| return SubActions; |
| } |
| |
| bool isMethodExtraction() const { return Kind == ExtractionKind::Method; } |
| |
| bool isExpressionExtraction() const { |
| return Kind == ExtractionKind::Expression; |
| } |
| |
| llvm::Expected<RefactoringResult> perform(ASTContext &Context, const Preprocessor &ThePreprocessor, |
| const RefactoringOptionSet &Options, |
| unsigned SelectedCandidateIndex) override; |
| |
| llvm::Expected<RefactoringResult> |
| performExpressionExtraction(ASTContext &Context, PrintingPolicy &PP); |
| |
| const Stmt *S, *ParentStmt; |
| const Decl *FunctionLikeParentDecl; |
| std::vector<std::string> Candidates; |
| /// A set of extraction candidates that correspond to the extracted code. |
| SmallVector<CandidateInfo, 2> CandidateExtractionInfo; |
| Optional<CompoundStatementRange> ExtractedStmtRange; |
| ExtractionKind Kind; |
| }; |
| |
| } // end anonymous namespace |
| |
| bool isSimpleExpression(const Expr *E) { |
| switch (E->IgnoreParenCasts()->getStmtClass()) { |
| case Stmt::DeclRefExprClass: |
| case Stmt::PredefinedExprClass: |
| case Stmt::IntegerLiteralClass: |
| case Stmt::FloatingLiteralClass: |
| case Stmt::ImaginaryLiteralClass: |
| case Stmt::CharacterLiteralClass: |
| case Stmt::StringLiteralClass: |
| return true; |
| default: |
| return false; |
| } |
| } |
| |
| static bool isMultipleCandidateBinOp(BinaryOperatorKind Op) { |
| return Op == BO_Add || Op == BO_Sub; |
| } |
| |
| /// Searches for the selected statement in the given CompoundStatement, looking |
| /// through things like PseudoObjectExpressions. |
| static CompoundStmt::const_body_iterator |
| findSelectedStmt(CompoundStmt::body_const_range Statements, |
| const Stmt *Target) { |
| return llvm::find_if(Statements, [=](const Stmt *S) { |
| if (S == Target) |
| return true; |
| if (const auto *POE = dyn_cast<PseudoObjectExpr>(S)) { |
| if (POE->getSyntacticForm() == Target) |
| return true; |
| } |
| return false; |
| }); |
| } |
| |
| /// Returns the first and the last statements that should be extracted from a |
| /// compound statement. |
| Optional<CompoundStatementRange> getExtractedStatements(const CompoundStmt *CS, |
| const Stmt *Begin, |
| const Stmt *End) { |
| if (CS->body_empty()) |
| return None; |
| assert(Begin && End); |
| CompoundStatementRange Result; |
| Result.First = findSelectedStmt(CS->body(), Begin); |
| assert(Result.First != CS->body_end()); |
| Result.Last = findSelectedStmt( |
| CompoundStmt::body_const_range(Result.First, CS->body_end()), End); |
| assert(Result.Last != CS->body_end()); |
| return Result; |
| } |
| |
| static RefactoringOperationResult |
| initiateAnyExtractOperation(ASTSlice &Slice, ASTContext &Context, |
| SourceLocation Location, SourceRange SelectionRange, |
| bool CreateOperation, |
| ExtractionKind Kind = ExtractionKind::Function) { |
| auto SelectedStmtsOpt = Slice.getSelectedStmtSet(); |
| if (!SelectedStmtsOpt) |
| return None; |
| SelectedStmtSet Stmts = *SelectedStmtsOpt; |
| // The selection range is contained entirely within this statement (without |
| // taking leading/trailing comments and whitespace into account). |
| const Stmt *Selected = Stmts.containsSelectionRange; |
| |
| // We only want to perform the extraction if the selection range is entirely |
| // within a body of a function or method. |
| if (!Selected) |
| return None; |
| const Decl *ParentDecl = |
| Slice.parentDeclForIndex(*Stmts.containsSelectionRangeIndex); |
| |
| if (!ParentDecl || |
| (!Stmts.isCompoundStatementPartiallySelected() && |
| !Slice.isContainedInCompoundStmt(*Stmts.containsSelectionRangeIndex))) |
| return RefactoringOperationResult( |
| "the selected expression is not in a function"); |
| |
| if (isa<Expr>(Selected) && isSimpleExpression(cast<Expr>(Selected))) |
| return RefactoringOperationResult("the selected expression is too simple"); |
| if (const auto *PRE = dyn_cast<ObjCPropertyRefExpr>(Selected)) { |
| if (!PRE->isMessagingGetter()) |
| return RefactoringOperationResult("property setter can't be extracted"); |
| } |
| |
| const Stmt *ParentStmt = |
| Slice.parentStmtForIndex(*Stmts.containsSelectionRangeIndex); |
| if (Kind == ExtractionKind::Expression && |
| !isLexicalExpression(Selected, ParentStmt)) |
| return None; |
| |
| RefactoringOperationResult Result; |
| Result.Initiated = true; |
| if (!CreateOperation) |
| return Result; |
| |
| Optional<CompoundStatementRange> ExtractedStmtRange; |
| |
| // Check if there are multiple candidates that can be extracted. |
| std::vector<std::string> Candidates; |
| Optional<ExtractOperation::CandidateInfo> FirstCandidateInfo; |
| if (const auto *BinOp = dyn_cast<BinaryOperator>(Selected)) { |
| // Binary '+' and '-' operators allow multiple candidates when the |
| // selection range starts after the LHS expression but still overlaps |
| // with the RHS. |
| if (isMultipleCandidateBinOp(BinOp->getOpcode()) && |
| (!Stmts.containsSelectionRangeStart || |
| getPreciseTokenLocEnd( |
| BinOp->getLHS()->getLocEnd(), Context.getSourceManager(), |
| Context.getLangOpts()) == SelectionRange.getBegin()) && |
| Stmts.containsSelectionRangeEnd) { |
| SourceRange FirstCandidateRange = |
| SourceRange(SelectionRange.getBegin(), BinOp->getLocEnd()); |
| if (FirstCandidateRange.getEnd().isMacroID()) |
| FirstCandidateRange.setEnd(Context.getSourceManager().getExpansionLoc( |
| FirstCandidateRange.getEnd())); |
| FirstCandidateInfo = ExtractOperation::CandidateInfo( |
| FirstCandidateRange, "+ ", |
| /*AnalyzedStatement=*/BinOp->getRHS()); |
| Candidates.push_back( |
| Lexer::getSourceText( |
| CharSourceRange::getTokenRange(FirstCandidateRange), |
| Context.getSourceManager(), Context.getLangOpts()) |
| .trim()); |
| Candidates.push_back(Lexer::getSourceText( |
| CharSourceRange::getTokenRange(BinOp->getSourceRange()), |
| Context.getSourceManager(), Context.getLangOpts())); |
| } |
| } else if (const auto *CS = dyn_cast<CompoundStmt>(Selected)) { |
| // We want to extract some child statements from a compound statement unless |
| // we've selected the entire compound statement including the opening and |
| // closing brace. |
| if (Stmts.containsSelectionRangeStart) |
| ExtractedStmtRange = |
| getExtractedStatements(CS, Stmts.containsSelectionRangeStart, |
| Stmts.containsSelectionRangeEnd); |
| } |
| |
| auto Operation = llvm::make_unique<ExtractOperation>( |
| Selected, ParentStmt, ParentDecl, std::move(Candidates), |
| ExtractedStmtRange, FirstCandidateInfo, Kind); |
| auto &CandidateExtractionInfo = Operation->CandidateExtractionInfo; |
| SourceRange Range; |
| if (ExtractedStmtRange) |
| Range = SourceRange(ExtractedStmtRange->getFirst()->getLocStart(), |
| ExtractedStmtRange->getLast()->getLocEnd()); |
| else |
| Range = Selected->getSourceRange(); |
| bool IsBeginMacroArgument = false; |
| if (Range.getBegin().isMacroID()) { |
| if (Context.getSourceManager().isMacroArgExpansion(Range.getBegin())) { |
| Range.setBegin( |
| Context.getSourceManager().getSpellingLoc(Range.getBegin())); |
| IsBeginMacroArgument = true; |
| } else { |
| Range.setBegin( |
| Context.getSourceManager().getExpansionLoc(Range.getBegin())); |
| } |
| } |
| if (Range.getEnd().isMacroID()) { |
| if (IsBeginMacroArgument && |
| Context.getSourceManager().isMacroArgExpansion(Range.getEnd())) |
| Range.setEnd(Context.getSourceManager().getSpellingLoc(Range.getEnd())); |
| else |
| Range.setEnd( |
| Context.getSourceManager().getExpansionRange(Range.getEnd()).second); |
| } |
| CandidateExtractionInfo.push_back(ExtractOperation::CandidateInfo(Range)); |
| Result.RefactoringOp = std::move(Operation); |
| return Result; |
| } |
| |
| RefactoringOperationResult clang::tooling::initiateExtractOperation( |
| ASTSlice &Slice, ASTContext &Context, SourceLocation Location, |
| SourceRange SelectionRange, bool CreateOperation) { |
| return initiateAnyExtractOperation(Slice, Context, Location, SelectionRange, |
| CreateOperation); |
| } |
| |
| RefactoringOperationResult clang::tooling::initiateExtractMethodOperation( |
| ASTSlice &Slice, ASTContext &Context, SourceLocation Location, |
| SourceRange SelectionRange, bool CreateOperation) { |
| // TODO: Verify that method extraction is actually possible. |
| return initiateAnyExtractOperation(Slice, Context, Location, SelectionRange, |
| CreateOperation, ExtractionKind::Method); |
| } |
| |
| RefactoringOperationResult clang::tooling::initiateExtractExpressionOperation( |
| ASTSlice &Slice, ASTContext &Context, SourceLocation Location, |
| SourceRange SelectionRange, bool CreateOperation) { |
| RefactoringOperationResult R = |
| initiateAnyExtractOperation(Slice, Context, Location, SelectionRange, |
| CreateOperation, ExtractionKind::Expression); |
| return R; |
| } |
| |
| using ReferencedEntity = |
| llvm::PointerUnion<const DeclRefExpr *, const FieldDecl *>; |
| |
| /// Iterate over the entities (variables/instance variables) that are directly |
| /// referenced by the given expression \p E. |
| /// |
| /// Note: Objective-C ivars are always captured via 'self'. |
| static void findEntitiesDirectlyReferencedInExpr( |
| const Expr *E, |
| llvm::function_ref<void(const ReferencedEntity &Entity)> Handler) { |
| E = E->IgnoreParenCasts(); |
| if (const auto *DRE = dyn_cast<DeclRefExpr>(E)) |
| return Handler(DRE); |
| |
| if (const auto *ME = dyn_cast<MemberExpr>(E)) { |
| if (isa<CXXThisExpr>(ME->getBase()->IgnoreParenCasts())) { |
| if (const auto *FD = dyn_cast_or_null<FieldDecl>(ME->getMemberDecl())) |
| Handler(FD); |
| return; |
| } |
| if (const auto *MD = ME->getMemberDecl()) { |
| if (isa<FieldDecl>(MD) || isa<IndirectFieldDecl>(MD)) |
| findEntitiesDirectlyReferencedInExpr(ME->getBase(), Handler); |
| } |
| return; |
| } |
| |
| if (const auto *CO = dyn_cast<ConditionalOperator>(E)) { |
| findEntitiesDirectlyReferencedInExpr(CO->getTrueExpr(), Handler); |
| findEntitiesDirectlyReferencedInExpr(CO->getFalseExpr(), Handler); |
| return; |
| } |
| |
| if (const auto *BO = dyn_cast<BinaryOperator>(E)) { |
| if (BO->getOpcode() == BO_Comma) |
| return findEntitiesDirectlyReferencedInExpr(BO->getRHS(), Handler); |
| } |
| } |
| |
| template <typename T, typename Matcher> |
| static void |
| findMatchingParameters(Matcher &ParameterMatcher, const Stmt *S, |
| ASTContext &Context, StringRef Node, |
| llvm::function_ref<void(const T *E)> Handler) { |
| using namespace clang::ast_matchers; |
| auto Matches = match(findAll(callExpr(ParameterMatcher)), *S, Context); |
| for (const auto &Match : Matches) |
| Handler(Match.template getNodeAs<T>(Node)); |
| Matches = match(findAll(cxxConstructExpr(ParameterMatcher)), *S, Context); |
| for (const auto &Match : Matches) |
| Handler(Match.template getNodeAs<T>(Node)); |
| } |
| |
| static void |
| findUseOfConstThis(const Stmt *S, ASTContext &Context, |
| llvm::function_ref<void(const CXXThisExpr *E)> Handler) { |
| using namespace clang::ast_matchers; |
| // Check the receiver in method call and member operator calls. |
| auto This = cxxThisExpr().bind("this"); |
| auto ThisReceiver = ignoringParenCasts( |
| anyOf(This, unaryOperator(hasOperatorName("*"), |
| hasUnaryOperand(ignoringParenCasts(This))))); |
| auto ConstMethodCallee = callee(cxxMethodDecl(isConst())); |
| auto Matches = match( |
| findAll(expr(anyOf(cxxMemberCallExpr(ConstMethodCallee, on(ThisReceiver)), |
| cxxOperatorCallExpr(ConstMethodCallee, |
| hasArgument(0, ThisReceiver))))), |
| *S, Context); |
| for (const auto &Match : Matches) |
| Handler(Match.getNodeAs<CXXThisExpr>("this")); |
| // Check parameters in calls. |
| auto ConstPointee = pointee(qualType(isConstQualified())); |
| auto RefParameter = forEachArgumentWithParam( |
| ThisReceiver, |
| parmVarDecl(hasType(qualType(referenceType(ConstPointee))))); |
| findMatchingParameters(RefParameter, S, Context, "this", Handler); |
| auto PtrParameter = forEachArgumentWithParam( |
| ignoringParenCasts(This), |
| parmVarDecl(hasType(qualType(pointerType(ConstPointee))))); |
| findMatchingParameters(PtrParameter, S, Context, "this", Handler); |
| } |
| |
| static void findArgumentsPassedByNonConstReference( |
| const Stmt *S, ASTContext &Context, |
| llvm::function_ref<void(const Expr *E)> Handler) { |
| using namespace clang::ast_matchers; |
| // Check the receiver in method call and member operator calls. |
| auto NonPointerReceiver = |
| expr(unless(hasType(qualType(pointerType())))).bind("arg"); |
| auto NonConstMethodCallee = callee(cxxMethodDecl(unless(isConst()))); |
| auto Matches = |
| match(findAll(expr(anyOf( |
| cxxMemberCallExpr(NonConstMethodCallee, on(NonPointerReceiver)), |
| cxxOperatorCallExpr(NonConstMethodCallee, |
| hasArgument(0, NonPointerReceiver))))), |
| *S, Context); |
| for (const auto &Match : Matches) |
| Handler(Match.getNodeAs<Expr>("arg")); |
| // Check parameters in calls. |
| auto RefParameter = forEachArgumentWithParam( |
| expr().bind("arg"), parmVarDecl(hasType(qualType(referenceType(unless( |
| pointee(qualType(isConstQualified())))))))); |
| Matches = match(findAll(callExpr(RefParameter)), *S, Context); |
| for (const auto &Match : Matches) |
| Handler(Match.getNodeAs<Expr>("arg")); |
| Matches = match(findAll(cxxConstructExpr(RefParameter)), *S, Context); |
| for (const auto &Match : Matches) |
| Handler(Match.getNodeAs<Expr>("arg")); |
| } |
| |
| static void findAddressExpressionsPassedByConstPointer( |
| const Stmt *S, ASTContext &Context, |
| llvm::function_ref<void(const UnaryOperator *E)> Handler) { |
| using namespace clang::ast_matchers; |
| auto ConstPtrParameter = forEachArgumentWithParam( |
| ignoringParenImpCasts(unaryOperator(hasOperatorName("&")).bind("arg")), |
| parmVarDecl(hasType( |
| qualType(pointerType(pointee(qualType(isConstQualified()))))))); |
| auto Matches = match(findAll(callExpr(ConstPtrParameter)), *S, Context); |
| for (const auto &Match : Matches) |
| Handler(Match.getNodeAs<UnaryOperator>("arg")); |
| Matches = match(findAll(cxxConstructExpr(ConstPtrParameter)), *S, Context); |
| for (const auto &Match : Matches) |
| Handler(Match.getNodeAs<UnaryOperator>("arg")); |
| } |
| |
| static bool isImplicitInitializer(const VarDecl *VD) { |
| assert(VD->hasInit()); |
| const auto *E = VD->getInit(); |
| if (isa<ExprWithCleanups>(E)) |
| return false; |
| const auto *Construct = dyn_cast<CXXConstructExpr>(E); |
| if (!Construct) |
| return E->getLocStart() == VD->getLocation(); |
| return Construct->getParenOrBraceRange().isInvalid(); |
| } |
| |
| static const Expr *getInitializerExprWithLexicalRange(const Expr *E) { |
| if (const auto *EWC = dyn_cast<ExprWithCleanups>(E)) { |
| if (const auto *Construct = dyn_cast<CXXConstructExpr>(EWC->getSubExpr())) { |
| if (Construct->getNumArgs() == 1) { |
| if (const auto *ME = |
| dyn_cast<MaterializeTemporaryExpr>(Construct->getArg(0))) |
| return ME; |
| } |
| } |
| } |
| return E; |
| } |
| |
| namespace { |
| |
| class ExtractedCodeVisitor : public RecursiveASTVisitor<ExtractedCodeVisitor> { |
| int DefineOrdering = 0; |
| |
| public: |
| struct CaptureInfo { |
| bool IsMutated = false; |
| bool IsDefined = false; |
| bool IsAddressTaken = false; |
| bool IsConstAddressTaken = false; |
| bool IsFieldCapturedWithThis = false; |
| bool IsUsed = false; |
| int DefineOrderingPriority = 0; |
| |
| bool isPassedByRefOrPtr() const { |
| return IsMutated || IsAddressTaken || IsConstAddressTaken; |
| } |
| bool isRefOrPtrConst() const { |
| return IsConstAddressTaken && !IsMutated && !IsAddressTaken; |
| } |
| }; |
| |
| const ImplicitParamDecl *SelfDecl; |
| |
| ExtractedCodeVisitor(const ImplicitParamDecl *SelfDecl) |
| : SelfDecl(SelfDecl) {} |
| |
| bool HasReturnInExtracted = false; |
| |
| CaptureInfo &captureVariable(const VarDecl *VD) { |
| CaptureInfo &Result = CapturedVariables[VD]; |
| Result.IsUsed = true; |
| return Result; |
| } |
| |
| CaptureInfo &captureField(const FieldDecl *FD) { return CapturedFields[FD]; } |
| |
| bool VisitDeclRefExpr(const DeclRefExpr *E) { |
| const VarDecl *VD = dyn_cast<VarDecl>(E->getDecl()); |
| if (!VD) |
| return true; |
| if (VD == SelfDecl) { |
| CaptureSelf = true; |
| SelfType = VD->getType(); |
| return true; |
| } |
| if (!VD->isLocalVarDeclOrParm()) |
| return true; |
| captureVariable(VD); |
| return true; |
| } |
| |
| void captureThisWithoutConstConcerns(const CXXThisExpr *E) { |
| CaptureThis = true; |
| ThisRecordType = E->getType()->getPointeeType(); |
| } |
| |
| bool VisitCXXThisExpr(const CXXThisExpr *E) { |
| captureThisWithoutConstConcerns(E); |
| ThisUsesWithUnknownConstness.insert(E); |
| return true; |
| } |
| |
| bool TraverseMemberExpr(MemberExpr *E) { |
| const auto *Base = dyn_cast<CXXThisExpr>(E->getBase()->IgnoreParenCasts()); |
| if (!Base) |
| return RecursiveASTVisitor::TraverseMemberExpr(E); |
| const FieldDecl *FD = dyn_cast_or_null<FieldDecl>(E->getMemberDecl()); |
| if (!FD) |
| return RecursiveASTVisitor::TraverseMemberExpr(E); |
| CaptureInfo &Info = captureField(FD); |
| // Don't capture the implicit 'this' for private fields as we don't want to |
| // capture this if we only use the private fields. |
| if (FD->getAccess() == AS_public || !Base->isImplicit()) { |
| Info.IsFieldCapturedWithThis = true; |
| // The member might have an effect on the constness of the captured 'this' |
| // but this is checked via mutation/const tracking for the field itself, |
| // so we just capture 'this' without worrying about checking if it's used |
| // in a 'const' manner here. |
| captureThisWithoutConstConcerns(Base); |
| } |
| return true; |
| } |
| |
| void captureSuper(QualType T) { |
| if (CaptureSuper) |
| return; |
| SuperType = T; |
| CaptureSuper = true; |
| } |
| |
| bool TraverseObjCPropertyRefExpr(ObjCPropertyRefExpr *E) { |
| if (E->isSuperReceiver()) |
| captureSuper(E->getSuperReceiverType()); |
| // Base might be an opaque expression, so we have to visit it manually as |
| // we don't necessarily visit the setter/getter message sends if just the |
| // property was selected. |
| if (E->isObjectReceiver()) { |
| if (const auto *OVE = dyn_cast<OpaqueValueExpr>(E->getBase())) |
| TraverseStmt(OVE->getSourceExpr()); |
| } |
| return RecursiveASTVisitor::TraverseObjCPropertyRefExpr(E); |
| } |
| |
| bool TraverseBinAssign(BinaryOperator *S) { |
| // RHS might be an opaque expression, if this is a property assignment. We |
| // have to visit it manually as we don't necessarily visit the setter/getter |
| // message sends if just the property was selected. |
| if (const auto *OVE = dyn_cast<OpaqueValueExpr>(S->getRHS())) |
| TraverseStmt(OVE->getSourceExpr()); |
| return RecursiveASTVisitor::TraverseBinAssign(S); |
| } |
| |
| void findCapturedVariableOrFieldsInExpression( |
| const Expr *E, llvm::function_ref<void(CaptureInfo &)> Handler) { |
| findEntitiesDirectlyReferencedInExpr( |
| E, [&Handler, this](const ReferencedEntity &Entity) { |
| if (const auto *DRE = Entity.dyn_cast<const DeclRefExpr *>()) { |
| const VarDecl *VD = dyn_cast<VarDecl>(DRE->getDecl()); |
| if (!VD || !VD->isLocalVarDeclOrParm() || VD->isImplicit()) |
| return; |
| return Handler(captureVariable(VD)); |
| } |
| return Handler(captureField(Entity.get<const FieldDecl *>())); |
| }); |
| } |
| |
| void |
| markDirectlyReferencedVariableOrFieldInExpressionAsMutated(const Expr *E) { |
| findCapturedVariableOrFieldsInExpression( |
| E, [](CaptureInfo &Capture) { Capture.IsMutated = true; }); |
| } |
| |
| bool VisitBinaryOperator(const BinaryOperator *E) { |
| if (E->isAssignmentOp()) |
| markDirectlyReferencedVariableOrFieldInExpressionAsMutated(E->getLHS()); |
| return true; |
| } |
| |
| bool VisitUnaryPreInc(const UnaryOperator *E) { |
| markDirectlyReferencedVariableOrFieldInExpressionAsMutated(E->getSubExpr()); |
| return true; |
| } |
| |
| bool VisitUnaryPostInc(const UnaryOperator *E) { |
| markDirectlyReferencedVariableOrFieldInExpressionAsMutated(E->getSubExpr()); |
| return true; |
| } |
| |
| bool VisitUnaryPreDec(const UnaryOperator *E) { |
| markDirectlyReferencedVariableOrFieldInExpressionAsMutated(E->getSubExpr()); |
| return true; |
| } |
| |
| bool VisitUnaryPostDec(const UnaryOperator *E) { |
| markDirectlyReferencedVariableOrFieldInExpressionAsMutated(E->getSubExpr()); |
| return true; |
| } |
| |
| /// If the given expression refers to a local/instance variable or a |
| /// a member of such variable that variable is marked as captured by |
| /// reference. |
| void captureVariableOrFieldInExpressionByReference(const Expr *E) { |
| findCapturedVariableOrFieldsInExpression( |
| E, [](CaptureInfo &Capture) { Capture.IsAddressTaken = true; }); |
| } |
| |
| bool VisitUnaryAddrOf(const UnaryOperator *E) { |
| // Capture the entity with 'const' reference/pointer when its address is |
| // passed into a function that takes a 'const' pointer and no other |
| // mutations or non-const address/reference acquisitions occur. |
| if (AddressExpressionsPassedToConstPointerParameter.count(E)) |
| findCapturedVariableOrFieldsInExpression( |
| E->getSubExpr(), |
| [](CaptureInfo &Capture) { Capture.IsConstAddressTaken = true; }); |
| else |
| captureVariableOrFieldInExpressionByReference(E->getSubExpr()); |
| return true; |
| } |
| |
| bool VisitObjCMessageExpr(const ObjCMessageExpr *E) { |
| if (E->getSuperLoc().isValid()) |
| captureSuper(E->getSuperType()); |
| const ObjCMethodDecl *MD = E->getMethodDecl(); |
| if (!MD) |
| return true; |
| for (const auto &Param : llvm::enumerate(MD->parameters())) { |
| QualType T = Param.value()->getType(); |
| if (Param.index() >= E->getNumArgs()) |
| break; |
| if (T->isReferenceType() && !T->getPointeeType().isConstQualified()) |
| captureVariableOrFieldInExpressionByReference(E->getArg(Param.index())); |
| if (T->isPointerType() && T->getPointeeType().isConstQualified()) { |
| // Check if this is an '&' passed into a const pointer parameter. |
| const Expr *Arg = E->getArg(Param.index()); |
| if (const auto *Op = |
| dyn_cast<UnaryOperator>(Arg->IgnoreParenImpCasts())) { |
| if (Op->getOpcode() == UO_AddrOf) |
| AddressExpressionsPassedToConstPointerParameter.insert(Op); |
| } |
| } |
| } |
| return true; |
| } |
| |
| bool VisitVarDecl(const VarDecl *VD) { |
| // Don't capture using the captureVariable method as we don't want to mark |
| // the declaration as a 'use'. This allows us to avoid passing in variables |
| // that are defined in extracted code, used afterwards, but never actually |
| // used in the extracted code. |
| CaptureInfo &Capture = CapturedVariables[VD]; |
| Capture.IsDefined = true; |
| Capture.DefineOrderingPriority = ++DefineOrdering; |
| // Ensure the capture is marked as 'used' when the variable declaration has |
| // an explicit initialization expression. This allows us to pass it by |
| // reference when it's defined in extracted code, used afterwards, but never |
| // actually used in the extracted code. The main reason why we want to try |
| // to keep this initialization in the extracted code is to preserve |
| // semantics as the initialization expression might have side-effects. |
| if (!Capture.IsUsed && VD->hasInit() && !isImplicitInitializer(VD)) |
| Capture.IsUsed = true; |
| QualType T = VD->getType(); |
| if (T->isReferenceType() && !T->getPointeeType().isConstQualified() && |
| VD->hasInit()) |
| captureVariableOrFieldInExpressionByReference(VD->getInit()); |
| return true; |
| } |
| |
| bool VisitReturnStmt(const ReturnStmt *S) { |
| HasReturnInExtracted = true; |
| return true; |
| } |
| |
| void InspectExtractedStmt(Stmt *S, ASTContext &Context) { |
| findAddressExpressionsPassedByConstPointer( |
| S, Context, [this](const UnaryOperator *Arg) { |
| AddressExpressionsPassedToConstPointerParameter.insert(Arg); |
| }); |
| TraverseStmt(S); |
| findArgumentsPassedByNonConstReference(S, Context, [this](const Expr *Arg) { |
| captureVariableOrFieldInExpressionByReference(Arg); |
| }); |
| if (CaptureThis && !ThisUsesWithUnknownConstness.empty()) { |
| // Compare the definite 'const' uses of 'this' to all the seen uses |
| // (except for the known field uses). |
| findUseOfConstThis(S, Context, [this](const CXXThisExpr *Arg) { |
| ThisUsesWithUnknownConstness.erase(Arg); |
| }); |
| IsThisConstForNonCapturedFieldUses = ThisUsesWithUnknownConstness.empty(); |
| } |
| } |
| |
| llvm::DenseMap<const VarDecl *, CaptureInfo> CapturedVariables; |
| llvm::DenseMap<const FieldDecl *, CaptureInfo> CapturedFields; |
| llvm::SmallPtrSet<const UnaryOperator *, 8> |
| AddressExpressionsPassedToConstPointerParameter; |
| llvm::SmallPtrSet<const CXXThisExpr *, 16> ThisUsesWithUnknownConstness; |
| bool CaptureThis = false; |
| bool IsThisConstForNonCapturedFieldUses = true; |
| QualType ThisRecordType; |
| bool CaptureSelf = false, CaptureSuper = false; |
| QualType SelfType, SuperType; |
| }; |
| |
| /// Traverses the extracted code and finds the uses of captured variables |
| /// that are passed into the extracted function using a pointer. |
| class VariableDefinedInExtractedCodeUseAfterExtractionFinder |
| : public RecursiveASTVisitor< |
| VariableDefinedInExtractedCodeUseAfterExtractionFinder> { |
| bool IsAfterExtracted = false; |
| |
| public: |
| const Stmt *LastExtractedStmt; |
| const llvm::SmallPtrSetImpl<const VarDecl *> &VariablesDefinedInExtractedCode; |
| llvm::SmallPtrSet<const VarDecl *, 4> VariablesUsedAfterExtraction; |
| |
| VariableDefinedInExtractedCodeUseAfterExtractionFinder( |
| const Stmt *LastExtractedStmt, |
| const llvm::SmallPtrSetImpl<const VarDecl *> |
| &VariablesDefinedInExtractedCode) |
| : LastExtractedStmt(LastExtractedStmt), |
| VariablesDefinedInExtractedCode(VariablesDefinedInExtractedCode) {} |
| |
| bool TraverseStmt(Stmt *S) { |
| RecursiveASTVisitor::TraverseStmt(S); |
| if (S == LastExtractedStmt) |
| IsAfterExtracted = true; |
| return true; |
| } |
| |
| bool VisitDeclRefExpr(const DeclRefExpr *E) { |
| if (!IsAfterExtracted) |
| return true; |
| const VarDecl *VD = dyn_cast<VarDecl>(E->getDecl()); |
| if (!VD) |
| return true; |
| if (VariablesDefinedInExtractedCode.count(VD)) |
| VariablesUsedAfterExtraction.insert(VD); |
| return true; |
| } |
| }; |
| |
| class PossibleShadowingVariableFinder |
| : public RecursiveASTVisitor<PossibleShadowingVariableFinder> { |
| const VarDecl *TargetVD; |
| |
| PossibleShadowingVariableFinder(const VarDecl *TargetVD) |
| : TargetVD(TargetVD) {} |
| |
| public: |
| bool VisitVarDecl(const VarDecl *VD) { |
| if (VD == TargetVD || VD->getName() != TargetVD->getName()) |
| return true; |
| return false; |
| } |
| |
| /// Returns true if the given statement \p S has a variable declaration whose |
| /// name is identical to the given variable declaration \p VD. |
| static bool hasShadowingVar(const VarDecl *VD, const Stmt *S) { |
| return !PossibleShadowingVariableFinder(VD).TraverseStmt( |
| const_cast<Stmt *>(S)); |
| } |
| }; |
| |
| /// Traverses the extracted code and rewrites the 'return' statements to ensure |
| /// that they now return some value. |
| class ReturnRewriter : public RecursiveASTVisitor<ReturnRewriter> { |
| Rewriter &SourceRewriter; |
| std::string Text; |
| |
| public: |
| ReturnRewriter(Rewriter &SourceRewriter, StringRef Text) |
| : SourceRewriter(SourceRewriter), Text(std::string(" ") + Text.str()) {} |
| |
| bool VisitReturnStmt(const ReturnStmt *S) { |
| SourceRewriter.InsertText( |
| getPreciseTokenLocEnd(S->getLocEnd(), SourceRewriter.getSourceMgr(), |
| SourceRewriter.getLangOpts()), |
| Text); |
| return true; |
| } |
| }; |
| |
| /// Prints the given initializer expression using the original source code if |
| /// possible. |
| static void printInitializerExpressionUsingOriginalSyntax( |
| const VarDecl *VD, const Expr *E, bool IsDeclaration, const ASTContext &Ctx, |
| llvm::raw_ostream &OS, const PrintingPolicy &PP) { |
| E = getInitializerExprWithLexicalRange(E); |
| SourceRange Range = E->getSourceRange(); |
| bool UseEquals = true; |
| bool UseTypeName = false; |
| if (const auto *Construct = dyn_cast<CXXConstructExpr>(E)) { |
| SourceRange SubRange = Construct->getParenOrBraceRange(); |
| if (SubRange.isValid()) { |
| UseEquals = false; |
| UseTypeName = true; |
| Range = SubRange; |
| } |
| } |
| if (Range.getBegin().isMacroID()) |
| Range.setBegin(Ctx.getSourceManager().getExpansionLoc(Range.getBegin())); |
| if (Range.getEnd().isMacroID()) |
| Range.setEnd(Ctx.getSourceManager().getExpansionLoc(Range.getEnd())); |
| bool IsInvalid = false; |
| StringRef Text = Lexer::getSourceText(CharSourceRange::getTokenRange(Range), |
| Ctx.getSourceManager(), |
| Ctx.getLangOpts(), &IsInvalid); |
| if (IsDeclaration && UseEquals) |
| OS << " = "; |
| else if (!IsDeclaration && UseTypeName) |
| VD->getType().print(OS, PP); |
| if (IsInvalid) |
| E->printPretty(OS, nullptr, PP); |
| else |
| OS << Text; |
| }; |
| |
| /// Traverses the extracted code and rewrites the declaration statements that |
| /// declare variables that are used after the extracted code. |
| class DefinedInExtractedCodeDeclStmtRewriter |
| : public RecursiveASTVisitor<DefinedInExtractedCodeDeclStmtRewriter> { |
| public: |
| Rewriter &SourceRewriter; |
| const llvm::SmallPtrSetImpl<const VarDecl *> &VariablesUsedAfterExtraction; |
| const PrintingPolicy &PP; |
| |
| DefinedInExtractedCodeDeclStmtRewriter( |
| Rewriter &SourceRewriter, const llvm::SmallPtrSetImpl<const VarDecl *> |
| &VariablesUsedAfterExtraction, |
| const PrintingPolicy &PP) |
| : SourceRewriter(SourceRewriter), |
| VariablesUsedAfterExtraction(VariablesUsedAfterExtraction), PP(PP) {} |
| |
| /// When a declaration statement declares variables that are all used |
| /// after extraction, we can rewrite it completely into a set of assignments |
| /// while still preserving the original initializer expressions when we |
| /// can. |
| void rewriteAllVariableDeclarationsToAssignments(const DeclStmt *S) { |
| SourceLocation StartLoc = S->getLocStart(); |
| for (const Decl *D : S->decls()) { |
| const auto *VD = dyn_cast<VarDecl>(D); |
| if (!VD || !VariablesUsedAfterExtraction.count(VD)) |
| continue; |
| if (!VD->hasInit() || isImplicitInitializer(VD)) { |
| // Remove the variable declarations without explicit initializers. |
| // This can affect the semantics of the program if the implicit |
| // initialization expression has side effects. |
| SourceRange Range = SourceRange( |
| StartLoc, S->isSingleDecl() ? S->getLocEnd() : VD->getLocation()); |
| SourceRewriter.RemoveText(Range); |
| continue; |
| } |
| std::string Str; |
| llvm::raw_string_ostream OS(Str); |
| if (StartLoc != S->getLocStart()) |
| OS << "; "; |
| const ASTContext &Ctx = D->getASTContext(); |
| // Dereference the variable unless the source uses C++. |
| if (!Ctx.getLangOpts().CPlusPlus) |
| OS << '*'; |
| OS << VD->getName() << " = "; |
| const Expr *Init = getInitializerExprWithLexicalRange(VD->getInit()); |
| SourceLocation End = Init->getLocStart(); |
| if (const auto *Construct = dyn_cast<CXXConstructExpr>(Init)) { |
| SourceRange SubRange = Construct->getParenOrBraceRange(); |
| if (SubRange.isValid()) { |
| End = SubRange.getBegin(); |
| VD->getType().print(OS, PP); |
| } |
| } |
| if (End.isMacroID()) |
| End = Ctx.getSourceManager().getExpansionLoc(End); |
| auto Range = CharSourceRange::getCharRange(StartLoc, End); |
| SourceRewriter.ReplaceText(StartLoc, SourceRewriter.getRangeSize(Range), |
| OS.str()); |
| StartLoc = getPreciseTokenLocEnd(D->getLocEnd(), Ctx.getSourceManager(), |
| Ctx.getLangOpts()); |
| } |
| } |
| |
| /// When a declaration statement has variables that are both used after |
| /// extraction and not used after extraction, we create new declaration |
| /// statements that declare the unused variables, while creating assignment |
| /// statements that "initialize" the variables that are used after the |
| /// extraction. This way we can preserve the order of |
| /// initialization/assignment from the original declaration statement. |
| void rewriteMixedDeclarations(const DeclStmt *S) { |
| // Completely rewrite the declaration statement. |
| std::string Str; |
| llvm::raw_string_ostream OS(Str); |
| for (const Decl *D : S->decls()) { |
| const ASTContext &Ctx = D->getASTContext(); |
| const VarDecl *VD = dyn_cast<VarDecl>(D); |
| bool IsLast = D == S->decl_end()[-1]; |
| if (!VD) { |
| OS << "<<unsupported declaration>>;"; |
| continue; |
| } |
| |
| auto PrintInit = [&](bool IsDeclaration) { |
| printInitializerExpressionUsingOriginalSyntax( |
| VD, VD->getInit(), IsDeclaration, Ctx, OS, PP); |
| }; |
| if (!VariablesUsedAfterExtraction.count(VD)) { |
| VD->getType().print(OS, PP); |
| OS << " " << VD->getName(); |
| if (VD->hasInit() && !isImplicitInitializer(VD)) |
| PrintInit(/*IsDeclaration=*/true); |
| OS << ";"; |
| if (!IsLast) |
| OS << ' '; |
| continue; |
| } |
| if (VD->hasInit() && !isImplicitInitializer(VD)) { |
| // Dereference the variable unless the source uses C++. |
| if (!Ctx.getLangOpts().CPlusPlus) |
| OS << '*'; |
| OS << VD->getName() << " = "; |
| PrintInit(/*IsDeclaration=*/false); |
| OS << ";"; |
| if (!IsLast) |
| OS << ' '; |
| } |
| } |
| SourceRewriter.ReplaceText(S->getSourceRange(), OS.str()); |
| } |
| |
| bool VisitDeclStmt(const DeclStmt *S) { |
| bool AreAllUsed = true; |
| bool AreNoneUsed = true; |
| for (const Decl *D : S->decls()) { |
| const auto *VD = dyn_cast<VarDecl>(D); |
| if (!VD || !VariablesUsedAfterExtraction.count(VD)) { |
| AreAllUsed = false; |
| continue; |
| } |
| AreNoneUsed = false; |
| // Exit early when both flags were set in the loop. |
| if (!AreAllUsed) |
| break; |
| } |
| if (AreNoneUsed) |
| return true; |
| |
| if (AreAllUsed) |
| rewriteAllVariableDeclarationsToAssignments(S); |
| else |
| rewriteMixedDeclarations(S); |
| return true; |
| } |
| }; |
| |
| /// Takes care of pseudo object expressions and Objective-C properties to avoid |
| /// duplicate rewrites and missing rewrites. |
| template <typename T> |
| class PseudoObjectRewriter : public RecursiveASTVisitor<T> { |
| typedef RecursiveASTVisitor<T> Base; |
| |
| public: |
| bool TraversePseudoObjectExpr(PseudoObjectExpr *E) { |
| return Base::TraverseStmt(E->getSyntacticForm()); |
| } |
| |
| bool TraverseObjCPropertyRefExpr(ObjCPropertyRefExpr *E) { |
| // Base might be an opaque expression, so we have to visit it manually as |
| // we don't necessarily visit the setter/getter message sends if just the |
| // property was selected. |
| if (E->isObjectReceiver()) { |
| if (const auto *OVE = dyn_cast<OpaqueValueExpr>(E->getBase())) |
| Base::TraverseStmt(OVE->getSourceExpr()); |
| } |
| return Base::TraverseObjCPropertyRefExpr(E); |
| } |
| |
| bool TraverseBinAssign(BinaryOperator *S) { |
| // RHS might be an opaque expression, if this is a property assignment. We |
| // have to visit it manually as we don't necessarily visit the setter/getter |
| // message sends if just the property was selected. |
| if (const auto *OVE = dyn_cast<OpaqueValueExpr>(S->getRHS())) |
| Base::TraverseStmt(OVE->getSourceExpr()); |
| return Base::TraverseBinAssign(S); |
| } |
| }; |
| |
| /// Traverses the extracted code and rewrites the uses of captured variables |
| /// that are passed into the extracted function using a pointer. |
| class CapturedVariableCaptureByPointerRewriter |
| : public PseudoObjectRewriter<CapturedVariableCaptureByPointerRewriter> { |
| public: |
| const VarDecl *TargetVD; |
| Rewriter &SourceRewriter; |
| |
| CapturedVariableCaptureByPointerRewriter(const VarDecl *VD, |
| Rewriter &SourceRewriter) |
| : TargetVD(VD), SourceRewriter(SourceRewriter) {} |
| |
| bool isTargetDeclRefExpr(const Expr *E) { |
| const auto *DRE = dyn_cast<DeclRefExpr>(E); |
| if (!DRE) |
| return false; |
| return dyn_cast<VarDecl>(DRE->getDecl()) == TargetVD; |
| } |
| |
| void dereferenceTargetVar(const Expr *E, bool WrapInParens = false) { |
| SourceRewriter.InsertTextBefore(E->getLocStart(), |
| WrapInParens ? "(*" : "*"); |
| if (WrapInParens) |
| SourceRewriter.InsertTextAfterToken(E->getLocEnd(), ")"); |
| } |
| |
| bool VisitDeclRefExpr(const DeclRefExpr *E) { |
| const VarDecl *VD = dyn_cast<VarDecl>(E->getDecl()); |
| if (VD != TargetVD) |
| return true; |
| dereferenceTargetVar(E); |
| return true; |
| } |
| |
| bool TraverseUnaryAddrOf(UnaryOperator *E) { |
| if (const auto *DRE = |
| dyn_cast<DeclRefExpr>(E->getSubExpr()->IgnoreParenCasts())) { |
| const VarDecl *VD = dyn_cast<VarDecl>(DRE->getDecl()); |
| if (VD == TargetVD) { |
| // Remove the '&' as the variable is now a pointer. |
| SourceRewriter.RemoveText( |
| CharSourceRange::getTokenRange(E->getLocStart(), E->getLocStart())); |
| return true; |
| } |
| } |
| return RecursiveASTVisitor::TraverseUnaryAddrOf(E); |
| } |
| |
| bool TraverseMemberExpr(MemberExpr *E) { |
| if (!E->isArrow()) { |
| if (const auto *DRE = |
| dyn_cast<DeclRefExpr>(E->getBase()->IgnoreParenCasts())) { |
| const VarDecl *VD = dyn_cast<VarDecl>(DRE->getDecl()); |
| if (VD == TargetVD) { |
| // Replace '.' with '->'. |
| SourceRewriter.ReplaceText(E->getOperatorLoc(), 1, "->"); |
| return true; |
| } |
| } |
| } else if (isTargetDeclRefExpr(E->getBase()->IgnoreImpCasts())) { |
| // Ensure the variable is wrapped in parenthesis when it's the base of |
| // '->' operator. |
| dereferenceTargetVar(E->getBase(), /*WrapInParens=*/true); |
| return true; |
| } |
| return RecursiveASTVisitor::TraverseMemberExpr(E); |
| } |
| }; |
| |
| /// Traverses the extracted code and rewrites the uses of 'this' that can be |
| /// rewritten as references. |
| class CapturedThisReferenceRewriter |
| : public PseudoObjectRewriter<CapturedThisReferenceRewriter> { |
| public: |
| Rewriter &SourceRewriter; |
| llvm::SmallPtrSet<const CXXThisExpr *, 8> RewrittenExpressions; |
| |
| CapturedThisReferenceRewriter(Rewriter &SourceRewriter) |
| : SourceRewriter(SourceRewriter) {} |
| |
| void rewriteThis(const CXXThisExpr *E) { |
| RewrittenExpressions.insert(E); |
| if (!E->isImplicit()) |
| SourceRewriter.ReplaceText(E->getLocStart(), 4, "object"); |
| else |
| SourceRewriter.InsertText(E->getLocStart(), "object"); |
| } |
| |
| bool VisitMemberExpr(const MemberExpr *E) { |
| const auto *This = |
| dyn_cast<CXXThisExpr>(E->getBase()->IgnoreParenImpCasts()); |
| if (This) { |
| rewriteThis(This); |
| if (!This->isImplicit() && E->isArrow()) |
| SourceRewriter.ReplaceText(E->getOperatorLoc(), 2, "."); |
| else |
| SourceRewriter.InsertText(E->getBase()->getLocEnd(), "."); |
| } |
| return true; |
| } |
| }; |
| |
| /// Traverses the extracted code and rewrites the uses of 'this' into '&object'. |
| class CapturedThisPointerRewriter |
| : public PseudoObjectRewriter<CapturedThisPointerRewriter> { |
| public: |
| Rewriter &SourceRewriter; |
| const llvm::SmallPtrSetImpl<const CXXThisExpr *> &RewrittenExpressions; |
| |
| CapturedThisPointerRewriter( |
| Rewriter &SourceRewriter, |
| const llvm::SmallPtrSetImpl<const CXXThisExpr *> &RewrittenExpressions) |
| : SourceRewriter(SourceRewriter), |
| RewrittenExpressions(RewrittenExpressions) {} |
| |
| void replace(const CXXThisExpr *E, StringRef Text) { |
| SourceRewriter.ReplaceText(E->getLocStart(), 4, Text); |
| } |
| |
| bool VisitCXXThisExpr(const CXXThisExpr *E) { |
| if (RewrittenExpressions.count(E)) |
| return true; |
| if (!E->isImplicit()) |
| replace(E, "&object"); |
| return true; |
| } |
| |
| bool TraverseUnaryDeref(UnaryOperator *E) { |
| if (const auto *This = |
| dyn_cast<CXXThisExpr>(E->getSubExpr()->IgnoreParenImpCasts())) { |
| if (!This->isImplicit()) { |
| // Remove the '*' as the variable is now a reference. |
| SourceRewriter.RemoveText( |
| CharSourceRange::getTokenRange(E->getLocStart(), E->getLocStart())); |
| replace(This, "object"); |
| return true; |
| } |
| } |
| return RecursiveASTVisitor::TraverseUnaryAddrOf(E); |
| } |
| }; |
| |
| /// Traverses the extracted code and rewrites the uses of 'self' into 'object'. |
| class CapturedSelfRewriter : public PseudoObjectRewriter<CapturedSelfRewriter> { |
| public: |
| Rewriter &SourceRewriter; |
| const ImplicitParamDecl *SelfDecl; |
| |
| CapturedSelfRewriter(Rewriter &SourceRewriter, |
| const ImplicitParamDecl *SelfDecl) |
| : SourceRewriter(SourceRewriter), SelfDecl(SelfDecl) { |
| assert(SelfDecl); |
| } |
| |
| bool VisitDeclRefExpr(const DeclRefExpr *E) { |
| const VarDecl *VD = dyn_cast<VarDecl>(E->getDecl()); |
| if (!VD || VD != SelfDecl) |
| return true; |
| if (E->getLocStart().isInvalid()) |
| return true; |
| SourceRewriter.ReplaceText(E->getLocStart(), 4, "object"); |
| return true; |
| } |
| |
| void insertObjectForImplicitSelf(const Expr *E, SourceLocation Loc, |
| StringRef Text) { |
| const auto *DRE = dyn_cast<DeclRefExpr>(E); |
| if (!DRE) |
| return; |
| const VarDecl *VD = dyn_cast<VarDecl>(DRE->getDecl()); |
| if (!VD || VD != SelfDecl || DRE->getLocStart().isValid()) |
| return; |
| SourceRewriter.InsertText(Loc, Text); |
| } |
| |
| bool VisitObjCIvarRefExpr(const ObjCIvarRefExpr *E) { |
| insertObjectForImplicitSelf(E->getBase()->IgnoreImpCasts(), |
| E->getLocStart(), "object->"); |
| return true; |
| } |
| }; |
| |
| /// Traverses the extracted code and rewrites the uses of 'self' into the name |
| /// of the class. |
| class CapturedClassSelfRewriter |
| : public PseudoObjectRewriter<CapturedClassSelfRewriter> { |
| public: |
| Rewriter &SourceRewriter; |
| StringRef ClassName; |
| const ImplicitParamDecl *SelfDecl; |
| |
| CapturedClassSelfRewriter(Rewriter &SourceRewriter, StringRef ClassName, |
| const ImplicitParamDecl *SelfDecl) |
| : SourceRewriter(SourceRewriter), ClassName(ClassName), |
| SelfDecl(SelfDecl) { |
| |
| assert(SelfDecl); |
| } |
| |
| bool VisitDeclRefExpr(const DeclRefExpr *E) { |
| const VarDecl *VD = dyn_cast<VarDecl>(E->getDecl()); |
| if (!VD || VD != SelfDecl || E->getLocStart().isInvalid()) |
| return true; |
| SourceRewriter.ReplaceText(E->getLocStart(), 4, ClassName); |
| return true; |
| } |
| }; |
| |
| /// Traverses the extracted code and rewrites the uses of 'super' into |
| /// 'superObject' or the name of the super class. |
| class CapturedSuperRewriter |
| : public PseudoObjectRewriter<CapturedSuperRewriter> { |
| public: |
| Rewriter &SourceRewriter; |
| StringRef ReplacementString; |
| |
| CapturedSuperRewriter(Rewriter &SourceRewriter, StringRef ReplacementString) |
| : SourceRewriter(SourceRewriter), ReplacementString(ReplacementString) {} |
| |
| void rewriteSuper(SourceLocation Loc) { |
| SourceRewriter.ReplaceText(Loc, strlen("super"), ReplacementString); |
| } |
| |
| bool VisitObjCPropertyRefExpr(const ObjCPropertyRefExpr *E) { |
| if (E->isSuperReceiver()) |
| rewriteSuper(E->getReceiverLocation()); |
| return true; |
| } |
| |
| bool VisitObjCMessageExpr(const ObjCMessageExpr *E) { |
| if (E->getSuperLoc().isValid()) |
| rewriteSuper(E->getSuperLoc()); |
| return true; |
| } |
| }; |
| |
| struct ExtractionSemicolonPolicy { |
| bool IsNeededInExtractedFunction; |
| bool IsNeededInOriginalFunction; |
| |
| static ExtractionSemicolonPolicy neededInExtractedFunction() { |
| return {true, false}; |
| } |
| static ExtractionSemicolonPolicy neededInOriginalFunction() { |
| return {false, true}; |
| } |
| static ExtractionSemicolonPolicy neededInBoth() { return {true, true}; } |
| }; |
| |
| } // end anonymous namespace |
| |
| ExtractionSemicolonPolicy |
| computeSemicolonExtractionPolicy(const Stmt *S, SourceRange &ExtractedRange, |
| const SourceManager &SM, |
| const LangOptions &LangOpts) { |
| if (isa<Expr>(S)) |
| return ExtractionSemicolonPolicy::neededInExtractedFunction(); |
| bool NeedsSemi = isSemicolonRequiredAfter(S); |
| if (!NeedsSemi) |
| return ExtractionSemicolonPolicy::neededInOriginalFunction(); |
| SourceLocation End = ExtractedRange.getEnd(); |
| if (isSemicolonAtLocation(End, SM, LangOpts)) |
| return ExtractionSemicolonPolicy::neededInOriginalFunction(); |
| SourceLocation NextTokenLoc = |
| Lexer::findNextTokenLocationAfterTokenAt(End, SM, LangOpts); |
| if (NextTokenLoc.isValid() && |
| isSemicolonAtLocation(NextTokenLoc, SM, LangOpts) && |
| areOnSameLine(NextTokenLoc, End, SM)) { |
| ExtractedRange.setEnd(NextTokenLoc); |
| return ExtractionSemicolonPolicy::neededInOriginalFunction(); |
| } |
| return ExtractionSemicolonPolicy::neededInBoth(); |
| } |
| |
| PrintingPolicy getPrintingPolicy(const ASTContext &Context, |
| const Preprocessor &PP) { |
| PrintingPolicy Policy = Context.getPrintingPolicy(); |
| // Our printing policy is copied over the ASTContext printing policy whenever |
| // a diagnostic is emitted, so recompute it. |
| Policy.Bool = Context.getLangOpts().Bool; |
| // FIXME: This is duplicated with Sema.cpp. When upstreaming this should be |
| // cleaned up. |
| if (!Policy.Bool) { |
| if (const MacroInfo *BoolMacro = PP.getMacroInfo(Context.getBoolName())) { |
| Policy.Bool = BoolMacro->isObjectLike() && |
| BoolMacro->getNumTokens() == 1 && |
| BoolMacro->getReplacementToken(0).is(tok::kw__Bool); |
| } |
| } |
| return Policy; |
| } |
| |
| static QualType getFunctionLikeParentDeclReturnType(const Decl *D) { |
| // FIXME: might need to handle ObjC blocks in the future. |
| if (const auto *M = dyn_cast<ObjCMethodDecl>(D)) |
| return M->getReturnType(); |
| return cast<FunctionDecl>(D)->getReturnType(); |
| } |
| |
| static const Stmt *getEnclosingDeclBody(const Decl *D) { |
| // FIXME: might need to handle ObjC blocks in the future. |
| if (const auto *M = dyn_cast<ObjCMethodDecl>(D)) |
| return M->getBody(); |
| return cast<FunctionDecl>(D)->getBody(); |
| } |
| |
| static bool isEnclosingMethodConst(const Decl *D) { |
| if (const auto *MD = dyn_cast<CXXMethodDecl>(D)) |
| return MD->isConst(); |
| return false; |
| } |
| |
| static bool isEnclosingMethodStatic(const Decl *D) { |
| if (const auto *MD = dyn_cast<CXXMethodDecl>(D)) |
| return MD->isStatic(); |
| return false; |
| } |
| |
| static bool isEnclosingMethodOutOfLine(const Decl *D) { |
| const auto *MD = dyn_cast<CXXMethodDecl>(D); |
| if (!MD) |
| return false; |
| return MD->isOutOfLine(); |
| } |
| |
| static void printEnclosingMethodScope(const Decl *D, llvm::raw_ostream &OS, |
| const PrintingPolicy &PP) { |
| const auto *MD = dyn_cast<CXXMethodDecl>(D); |
| if (!MD) |
| return; |
| if (!MD->isOutOfLine() || !MD->getQualifier()) |
| return; |
| MD->getQualifier()->print(OS, PP); |
| } |
| |
| static SourceLocation |
| computeFunctionExtractionLocation(const Decl *D, bool IsMethodExtraction) { |
| if (!IsMethodExtraction && isa<CXXMethodDecl>(D)) { |
| // Code from methods that defined in class bodies should be extracted to a |
| // function defined just before the class. |
| while (const auto *RD = dyn_cast<CXXRecordDecl>(D->getLexicalDeclContext())) |
| D = RD; |
| } |
| return D->getLocStart(); |
| } |
| |
| namespace { |
| enum class MethodDeclarationPlacement { After, Before }; |
| |
| /// \brief Represents an entity captured from the original function that's |
| /// passed into the new function/method. |
| struct CapturedVariable { |
| const VarDecl *VD; |
| const FieldDecl *FD; |
| QualType ThisType; |
| bool PassByRefOrPtr; |
| bool IsRefOrPtrConst; |
| bool IsThisSelf = false; |
| bool IsThisSuper = false; |
| bool TakeAddress = false; |
| QualType ParameterType; |
| |
| CapturedVariable(const VarDecl *VD, bool PassByRefOrPtr, bool IsRefOrPtrConst) |
| : VD(VD), FD(nullptr), PassByRefOrPtr(PassByRefOrPtr), |
| IsRefOrPtrConst(IsRefOrPtrConst) {} |
| CapturedVariable(const FieldDecl *FD, bool PassByRefOrPtr, |
| bool IsRefOrPtrConst) |
| : VD(nullptr), FD(FD), PassByRefOrPtr(PassByRefOrPtr), |
| IsRefOrPtrConst(IsRefOrPtrConst) {} |
| CapturedVariable(QualType ThisType, bool PassByRefOrPtr, bool IsConst) |
| : VD(nullptr), FD(nullptr), ThisType(ThisType), |
| PassByRefOrPtr(PassByRefOrPtr), IsRefOrPtrConst(IsConst) {} |
| |
| static CapturedVariable getThis(QualType T, bool IsConst) { |
| return CapturedVariable(T, /*PassByRefOrPtr=*/true, /*IsConst*/ IsConst); |
| } |
| |
| static CapturedVariable getSelf(QualType T) { |
| auto Result = |
| CapturedVariable(T, /*PassByRefOrPtr=*/false, /*IsConst*/ false); |
| Result.IsThisSelf = true; |
| return Result; |
| } |
| |
| static CapturedVariable getSuper(QualType T) { |
| auto Result = |
| CapturedVariable(T, /*PassByRefOrPtr=*/false, /*IsConst*/ false); |
| Result.IsThisSuper = true; |
| return Result; |
| } |
| |
| StringRef getName() const { |
| return VD ? VD->getName() |
| : FD ? FD->getName() : IsThisSuper ? "superObject" : "object"; |
| } |
| StringRef getExpr() const { |
| return ThisType.isNull() |
| ? getName() |
| : IsThisSelf ? "self" : IsThisSuper ? "super.self" : "*this"; |
| } |
| QualType getType() const { |
| return VD ? VD->getType() : FD ? FD->getType() : ThisType; |
| } |
| }; |
| } // end anonymous namespace |
| |
| static std::pair<SourceLocation, MethodDeclarationPlacement> |
| computeAppropriateExtractionLocationForMethodDeclaration( |
| const CXXMethodDecl *D) { |
| const CXXRecordDecl *RD = D->getParent(); |
| // Try to put the new declaration after the last method, or just before the |
| // end of the class. |
| SourceLocation Loc; |
| for (const CXXMethodDecl *M : RD->methods()) { |
| if (M->isImplicit()) |
| continue; |
| Loc = M->getLocEnd(); |
| } |
| return Loc.isValid() ? std::make_pair(Loc, MethodDeclarationPlacement::After) |
| : std::make_pair(RD->getLocEnd(), |
| MethodDeclarationPlacement::Before); |
| } |
| |
| static bool isInHeader(SourceLocation Loc, const SourceManager &SM) { |
| // Base the header decision on the filename. |
| StringRef Extension = llvm::sys::path::extension(SM.getFilename(Loc)); |
| if (Extension.empty()) |
| return false; |
| return llvm::StringSwitch<bool>(Extension.drop_front()) |
| .Case("h", true) |
| .Case("hpp", true) |
| .Case("hh", true) |
| .Case("h++", true) |
| .Case("hxx", true) |
| .Case("inl", true) |
| .Case("def", true) |
| .Default(false); |
| } |
| |
| llvm::Expected<RefactoringResult> |
| ExtractOperation::performExpressionExtraction(ASTContext &Context, |
| PrintingPolicy &PP) { |
| assert(isExpressionExtraction() && "Not an expression extraction"); |
| std::vector<RefactoringReplacement> Replacements; |
| const Expr *E = cast<Expr>(S); |
| QualType VarType = findExpressionLexicalType(FunctionLikeParentDecl, E, |
| E->getType(), PP, Context); |
| StringRef VarName = "extractedExpr"; |
| auto CreatedSymbol = |
| llvm::make_unique<RefactoringResultAssociatedSymbol>(SymbolName(VarName)); |
| |
| SourceRange ExtractedTokenRange = CandidateExtractionInfo[0].Range; |
| SourceRange ExtractedCharRange = SourceRange( |
| ExtractedTokenRange.getBegin(), |
| getPreciseTokenLocEnd(ExtractedTokenRange.getEnd(), |
| Context.getSourceManager(), Context.getLangOpts())); |
| |
| // Create the variable that will hold the value of the duplicate expression. |
| std::string VariableDeclarationString; |
| llvm::raw_string_ostream OS(VariableDeclarationString); |
| VarType.print(OS, PP, /*PlaceHolder*/ VarName); |
| // FIXME: We should hook into the TypePrinter when moving over to llvm.org |
| // instead and get the offset from it. |
| unsigned NameOffset = StringRef(OS.str()).find(VarName); |
| OS << " = "; |
| OS << Lexer::getSourceText(CharSourceRange::getCharRange(ExtractedCharRange), |
| Context.getSourceManager(), Context.getLangOpts()); |
| OS << ";\n"; |
| |
| // Variable declaration. |
| SourceLocation InsertionLoc = |
| extract::locationForExtractedVariableDeclaration( |
| E, FunctionLikeParentDecl, Context.getSourceManager()); |
| Replacements.push_back(RefactoringReplacement( |
| SourceRange(InsertionLoc, InsertionLoc), OS.str(), CreatedSymbol.get(), |
| RefactoringReplacement::AssociatedSymbolLocation( |
| llvm::makeArrayRef(NameOffset), /*IsDeclaration=*/true))); |
| // Replace the expression with the variable. |
| Replacements.push_back( |
| RefactoringReplacement(ExtractedCharRange, VarName, CreatedSymbol.get(), |
| /*NameOffset=*/llvm::makeArrayRef(unsigned(0)))); |
| |
| RefactoringResult Result(std::move(Replacements)); |
| Result.AssociatedSymbols.push_back(std::move(CreatedSymbol)); |
| return std::move(Result); |
| } |
| |
| llvm::Expected<RefactoringResult> ExtractOperation::perform( |
| ASTContext &Context, const Preprocessor &ThePreprocessor, |
| const RefactoringOptionSet &Options, unsigned SelectedCandidateIndex) { |
| std::vector<RefactoringReplacement> Replacements; |
| SourceManager &SM = Context.getSourceManager(); |
| const LangOptions &LangOpts = Context.getLangOpts(); |
| Rewriter SourceRewriter(SM, LangOpts); |
| PrintingPolicy PP = getPrintingPolicy(Context, ThePreprocessor); |
| PP.UseStdFunctionForLambda = true; |
| PP.SuppressStrongLifetime = true; |
| PP.SuppressLifetimeQualifiers = true; |
| PP.SuppressUnwrittenScope = true; |
| |
| if (isExpressionExtraction()) |
| return performExpressionExtraction(Context, PP); |
| |
| const Stmt *S = |
| CandidateExtractionInfo[SelectedCandidateIndex].AnalyzedStatement |
| ? CandidateExtractionInfo[SelectedCandidateIndex].AnalyzedStatement |
| : this->S; |
| |
| const auto *EnclosingObjCMethod = |
| dyn_cast<ObjCMethodDecl>(FunctionLikeParentDecl); |
| |
| // Find the variables that are captured by the extracted code. |
| ExtractedCodeVisitor Visitor(/*SelfDecl=*/EnclosingObjCMethod |
| ? EnclosingObjCMethod->getSelfDecl() |
| : nullptr); |
| if (ExtractedStmtRange) { |
| for (const Stmt *S : *ExtractedStmtRange) |
| Visitor.InspectExtractedStmt(const_cast<Stmt *>(S), Context); |
| } else |
| Visitor.InspectExtractedStmt(const_cast<Stmt *>(S), Context); |
| // Compute the return type. |
| bool IsExpr = isLexicalExpression(S, ParentStmt); |
| QualType ReturnType; |
| if (IsExpr || Visitor.HasReturnInExtracted) { |
| if (const auto *E = dyn_cast<Expr>(S)) { |
| assert(!ExtractedStmtRange); |
| ReturnType = findExpressionLexicalType(FunctionLikeParentDecl, E, |
| E->getType(), PP, Context); |
| } else |
| ReturnType = getFunctionLikeParentDeclReturnType(FunctionLikeParentDecl); |
| } else |
| ReturnType = Context.VoidTy; |
| // Sort the captured variables. |
| std::vector<CapturedVariable> CapturedVariables; |
| llvm::SmallPtrSet<const VarDecl *, 4> VariablesDefinedInExtractedCode; |
| CapturedVariables.reserve(Visitor.CapturedVariables.size() + |
| Visitor.CapturedFields.size()); |
| for (const auto &I : Visitor.CapturedVariables) { |
| if (I.getSecond().IsDefined) { |
| VariablesDefinedInExtractedCode.insert(I.getFirst()); |
| continue; |
| } |
| CapturedVariables.push_back( |
| CapturedVariable(I.getFirst(), I.getSecond().isPassedByRefOrPtr(), |
| I.getSecond().isRefOrPtrConst())); |
| } |
| // Take a look at the variables that are defined in the extracted code. |
| VariableDefinedInExtractedCodeUseAfterExtractionFinder |
| UsedAfterExtractionFinder(ExtractedStmtRange ? *ExtractedStmtRange->Last |
| : S, |
| VariablesDefinedInExtractedCode); |
| UsedAfterExtractionFinder.TraverseStmt( |
| const_cast<Stmt *>(getEnclosingDeclBody(FunctionLikeParentDecl))); |
| struct RedeclaredVariable { |
| const VarDecl *VD; |
| int OrderingPriority; |
| }; |
| llvm::SmallVector<RedeclaredVariable, 4> RedeclaredVariables; |
| bool CanUseReturnForVariablesUsedAfterwards = |
| !isa<Expr>(S) && ReturnType->isVoidType() && |
| UsedAfterExtractionFinder.VariablesUsedAfterExtraction.size() == 1; |
| if (CanUseReturnForVariablesUsedAfterwards) { |
| // Avoid using the return value for the variable that's used afterwards as |
| // another variable might shadow it at the point of a 'return' that we |
| // have to rewrite to 'return var'. |
| const VarDecl *VD = |
| *UsedAfterExtractionFinder.VariablesUsedAfterExtraction.begin(); |
| if (ExtractedStmtRange) { |
| for (const Stmt *S : *ExtractedStmtRange) { |
| if (PossibleShadowingVariableFinder::hasShadowingVar(VD, S)) { |
| CanUseReturnForVariablesUsedAfterwards = false; |
| break; |
| } |
| } |
| } else |
| CanUseReturnForVariablesUsedAfterwards = |
| !PossibleShadowingVariableFinder::hasShadowingVar(VD, S); |
| } |
| if (CanUseReturnForVariablesUsedAfterwards) { |
| for (const auto &I : Visitor.CapturedVariables) { |
| if (!I.getSecond().IsDefined || |
| !UsedAfterExtractionFinder.VariablesUsedAfterExtraction.count( |
| I.getFirst())) |
| continue; |
| RedeclaredVariables.push_back( |
| {I.getFirst(), I.getSecond().DefineOrderingPriority}); |
| ReturnType = I.getFirst()->getType(); |
| // Const qualifier can be dropped as we don't want to declare the return |
| // type as 'const'. |
| if (ReturnType.isConstQualified()) |
| ReturnType.removeLocalConst(); |
| break; |
| } |
| if (Visitor.HasReturnInExtracted) { |
| ReturnRewriter ReturnsRewriter(SourceRewriter, |
| RedeclaredVariables.front().VD->getName()); |
| if (ExtractedStmtRange) { |
| for (const Stmt *S : *ExtractedStmtRange) |
| ReturnsRewriter.TraverseStmt(const_cast<Stmt *>(S)); |
| } else |
| ReturnsRewriter.TraverseStmt(const_cast<Stmt *>(S)); |
| } |
| } else { |
| for (const auto &I : Visitor.CapturedVariables) { |
| if (!I.getSecond().IsDefined || |
| !UsedAfterExtractionFinder.VariablesUsedAfterExtraction.count( |
| I.getFirst())) |
| continue; |
| RedeclaredVariables.push_back( |
| {I.getFirst(), I.getSecond().DefineOrderingPriority}); |
| if (!I.getSecond().IsUsed) |
| continue; |
| // Pass the variable that's defined in the extracted code but used |
| // afterwards as a parameter only when it's actually used in the extracted |
| // code. |
| CapturedVariables.push_back(CapturedVariable(I.getFirst(), |
| /*PassByRefOrPtr=*/true, |
| /*IsRefOrPtrConst=*/false)); |
| } |
| std::sort(RedeclaredVariables.begin(), RedeclaredVariables.end(), |
| [](const RedeclaredVariable &X, const RedeclaredVariable &Y) { |
| return X.OrderingPriority < Y.OrderingPriority; |
| }); |
| DefinedInExtractedCodeDeclStmtRewriter DeclRewriter( |
| SourceRewriter, UsedAfterExtractionFinder.VariablesUsedAfterExtraction, |
| PP); |
| if (ExtractedStmtRange) { |
| for (const Stmt *S : *ExtractedStmtRange) |
| DeclRewriter.TraverseStmt(const_cast<Stmt *>(S)); |
| } else |
| DeclRewriter.TraverseStmt(const_cast<Stmt *>(S)); |
| } |
| // Capture any fields if necessary. |
| bool IsThisConstInCapturedFieldUses = true; |
| if (!isMethodExtraction()) { |
| for (const auto &I : Visitor.CapturedFields) { |
| if (I.getSecond().isPassedByRefOrPtr() && |
| !I.getSecond().isRefOrPtrConst()) |
| IsThisConstInCapturedFieldUses = false; |
| // Private fields that use explicit 'this' should be captured using 'this' |
| // even if they might end up being inaccessible in the extracted function. |
| if (I.getSecond().IsFieldCapturedWithThis) |
| continue; |
| CapturedVariables.push_back( |
| CapturedVariable(I.getFirst(), I.getSecond().isPassedByRefOrPtr(), |
| I.getSecond().isRefOrPtrConst())); |
| } |
| } |
| std::sort(CapturedVariables.begin(), CapturedVariables.end(), |
| [](const CapturedVariable &X, const CapturedVariable &Y) { |
| return X.getName() < Y.getName(); |
| }); |
| // 'This'/'self' should be passed-in first. |
| if (!isMethodExtraction() && Visitor.CaptureThis) { |
| CapturedVariables.insert( |
| CapturedVariables.begin(), |
| CapturedVariable::getThis( |
| Visitor.ThisRecordType, |
| IsThisConstInCapturedFieldUses && |
| Visitor.IsThisConstForNonCapturedFieldUses)); |
| CapturedThisReferenceRewriter ThisRewriter(SourceRewriter); |
| if (ExtractedStmtRange) { |
| for (const Stmt *S : *ExtractedStmtRange) |
| ThisRewriter.TraverseStmt(const_cast<Stmt *>(S)); |
| } else |
| ThisRewriter.TraverseStmt(const_cast<Stmt *>(S)); |
| CapturedThisPointerRewriter PtrThisRewriter( |
| SourceRewriter, ThisRewriter.RewrittenExpressions); |
| if (ExtractedStmtRange) { |
| for (const Stmt *S : *ExtractedStmtRange) |
| PtrThisRewriter.TraverseStmt(const_cast<Stmt *>(S)); |
| } else |
| PtrThisRewriter.TraverseStmt(const_cast<Stmt *>(S)); |
| } else if (!isMethodExtraction() && Visitor.CaptureSelf && |
| EnclosingObjCMethod) { |
| if (EnclosingObjCMethod->isInstanceMethod()) { |
| // Instance methods rewrite 'self' into an 'object' parameter. |
| CapturedVariables.insert(CapturedVariables.begin(), |
| CapturedVariable::getSelf(Visitor.SelfType)); |
| CapturedSelfRewriter SelfRewriter(SourceRewriter, |
| EnclosingObjCMethod->getSelfDecl()); |
| if (ExtractedStmtRange) { |
| for (const Stmt *S : *ExtractedStmtRange) |
| SelfRewriter.TraverseStmt(const_cast<Stmt *>(S)); |
| } else |
| SelfRewriter.TraverseStmt(const_cast<Stmt *>(S)); |
| } else { |
| // Class methods rewrite 'self' into the class name and don't pass 'self' |
| // as a parameter. |
| CapturedClassSelfRewriter SelfRewriter( |
| SourceRewriter, EnclosingObjCMethod->getClassInterface()->getName(), |
| EnclosingObjCMethod->getSelfDecl()); |
| if (ExtractedStmtRange) { |
| for (const Stmt *S : *ExtractedStmtRange) |
| SelfRewriter.TraverseStmt(const_cast<Stmt *>(S)); |
| } else |
| SelfRewriter.TraverseStmt(const_cast<Stmt *>(S)); |
| } |
| } |
| if (!isMethodExtraction() && Visitor.CaptureSuper && EnclosingObjCMethod) { |
| if (EnclosingObjCMethod->isInstanceMethod()) |
| // Instance methods rewrite 'super' into an 'superObject' parameter. |
| CapturedVariables.insert(Visitor.CaptureSelf |
| ? CapturedVariables.begin() + 1 |
| : CapturedVariables.begin(), |
| CapturedVariable::getSuper(Visitor.SuperType)); |
| CapturedSuperRewriter SuperRewriter( |
| SourceRewriter, EnclosingObjCMethod->isInstanceMethod() |
| ? "superObject" |
| : EnclosingObjCMethod->getClassInterface() |
| ->getSuperClass() |
| ->getName()); |
| if (ExtractedStmtRange) { |
| for (const Stmt *S : *ExtractedStmtRange) |
| SuperRewriter.TraverseStmt(const_cast<Stmt *>(S)); |
| } else |
| SuperRewriter.TraverseStmt(const_cast<Stmt *>(S)); |
| } |
| |
| // Compute the parameter types. |
| for (auto &Var : CapturedVariables) { |
| QualType T = Var.getType(); |
| |
| // Array types are passed into the extracted function using a pointer. |
| if (const auto *AT = Context.getAsArrayType(T)) |
| T = Context.getPointerType(AT->getElementType()); |
| |
| // Captured records and other mutated variables are passed into the |
| // extracted function either using a reference (C++) or a pointer. |
| if ((T->isRecordType() || Var.PassByRefOrPtr) && !T->isReferenceType()) { |
| // Add a 'const' qualifier to the record when it's not mutated in the |
| // extracted code or when we are taking the address of the captured |
| // variable for just a 'const' use. |
| if (!Var.PassByRefOrPtr || Var.IsRefOrPtrConst) |
| T.addConst(); |
| |
| if (LangOpts.CPlusPlus) |
| T = Context.getLValueReferenceType(T); |
| else { |
| T = Context.getPointerType(T); |
| CapturedVariableCaptureByPointerRewriter UseRewriter(Var.VD, |
| SourceRewriter); |
| if (ExtractedStmtRange) { |
| for (const Stmt *S : *ExtractedStmtRange) |
| UseRewriter.TraverseStmt(const_cast<Stmt *>(S)); |
| } else |
| UseRewriter.TraverseStmt(const_cast<Stmt *>(S)); |
| Var.TakeAddress = true; |
| } |
| } |
| // Const qualifier can be dropped as we don't want to declare the parameter |
| // as 'const'. |
| else if (T.isLocalConstQualified()) |
| T.removeLocalConst(); |
| |
| Var.ParameterType = T; |
| } |
| |
| // TODO: Choose a better name if there are collisions. |
| StringRef ExtractedName = "extracted"; |
| llvm::SmallVector<StringRef, 4> ExtractedNamePieces; |
| ExtractedNamePieces.push_back(ExtractedName); |
| if (isMethodExtraction() && EnclosingObjCMethod && |
| !CapturedVariables.empty()) { |
| for (const auto &Var : llvm::makeArrayRef(CapturedVariables).drop_front()) |
| ExtractedNamePieces.push_back(Var.getName()); |
| } |
| std::unique_ptr<RefactoringResultAssociatedSymbol> CreatedSymbol = |
| llvm::make_unique<RefactoringResultAssociatedSymbol>( |
| SymbolName(ExtractedNamePieces)); |
| |
| SourceLocation FunctionExtractionLoc = computeFunctionExtractionLocation( |
| FunctionLikeParentDecl, isMethodExtraction()); |
| FunctionExtractionLoc = |
| getLocationOfPrecedingComment(FunctionExtractionLoc, SM, LangOpts); |
| |
| // Create the replacement that contains the new function. |
| auto PrintFunctionHeader = |
| [&](llvm::raw_string_ostream &OS, |
| bool IsDefinition = |
| true) -> RefactoringReplacement::AssociatedSymbolLocation { |
| if (isMethodExtraction() && EnclosingObjCMethod) { |
| OS << (EnclosingObjCMethod->isClassMethod() ? '+' : '-') << " ("; |
| ReturnType.print(OS, PP); |
| OS << ')'; |
| llvm::SmallVector<unsigned, 4> NameOffsets; |
| NameOffsets.push_back(OS.str().size()); |
| OS << ExtractedName; |
| bool IsFirst = true; |
| for (const auto &Var : CapturedVariables) { |
| if (!IsFirst) { |
| OS << ' '; |
| NameOffsets.push_back(OS.str().size()); |
| OS << Var.getName(); |
| } |
| IsFirst = false; |
| OS << ":("; |
| Var.ParameterType.print(OS, PP); |
| OS << ')' << Var.getName(); |
| } |
| return RefactoringReplacement::AssociatedSymbolLocation( |
| NameOffsets, /*IsDeclaration=*/true); |
| } |
| auto *FD = dyn_cast<FunctionDecl>(FunctionLikeParentDecl); |
| if (isMethodExtraction() && IsDefinition && |
| !FD->getDescribedFunctionTemplate()) { |
| // Print the class template parameter lists for an out-of-line method. |
| for (unsigned I = 0, |
| NumTemplateParams = FD->getNumTemplateParameterLists(); |
| I < NumTemplateParams; ++I) { |
| FD->getTemplateParameterList(I)->print(OS, PP, Context); |
| OS << "\n"; |
| } |
| } |
| if (isMethodExtraction() && isEnclosingMethodStatic(FunctionLikeParentDecl)) |
| OS << "static "; |
| else if (!isMethodExtraction()) |
| OS << (isInHeader(FunctionExtractionLoc, SM) ? "inline " : "static "); |
| ReturnType.print(OS, PP); |
| OS << ' '; |
| if (isMethodExtraction() && IsDefinition) |
| printEnclosingMethodScope(FunctionLikeParentDecl, OS, PP); |
| unsigned NameOffset = OS.str().size(); |
| OS << ExtractedName << '('; |
| bool IsFirst = true; |
| for (const auto &Var : CapturedVariables) { |
| if (!IsFirst) |
| OS << ", "; |
| IsFirst = false; |
| Var.ParameterType.print(OS, PP, /*PlaceHolder=*/Var.getName()); |
| } |
| OS << ')'; |
| if (isMethodExtraction() && isEnclosingMethodConst(FunctionLikeParentDecl)) |
| OS << " const"; |
| return RefactoringReplacement::AssociatedSymbolLocation( |
| NameOffset, /*IsDeclaration=*/true); |
| ; |
| }; |
| |
| if (isMethodExtraction() && |
| isEnclosingMethodOutOfLine(FunctionLikeParentDecl)) { |
| // The location of the declaration should be either before the original |
| // declararation, or, if this method has not declaration, somewhere |
| // appropriate in the class. |
| MethodDeclarationPlacement Placement; |
| SourceLocation DeclarationLoc; |
| if (FunctionLikeParentDecl->getCanonicalDecl() != FunctionLikeParentDecl) { |
| DeclarationLoc = computeFunctionExtractionLocation( |
| FunctionLikeParentDecl->getCanonicalDecl(), isMethodExtraction()); |
| Placement = MethodDeclarationPlacement::Before; |
| } else { |
| auto LocAndPlacement = |
| computeAppropriateExtractionLocationForMethodDeclaration( |
| cast<CXXMethodDecl>(FunctionLikeParentDecl)); |
| DeclarationLoc = LocAndPlacement.first; |
| Placement = LocAndPlacement.second; |
| } |
| if (Placement == MethodDeclarationPlacement::Before) |
| DeclarationLoc = |
| getLocationOfPrecedingComment(DeclarationLoc, SM, LangOpts); |
| else |
| DeclarationLoc = getLastLineLocationUnlessItHasOtherTokens( |
| getPreciseTokenLocEnd(DeclarationLoc, SM, LangOpts), SM, LangOpts); |
| // Add a replacement for the method declaration if necessary. |
| std::string DeclarationString; |
| llvm::raw_string_ostream OS(DeclarationString); |
| if (Placement == MethodDeclarationPlacement::After) |
| OS << "\n\n"; |
| RefactoringReplacement::AssociatedSymbolLocation SymbolLoc = |
| PrintFunctionHeader(OS, /*IsDefinition=*/false); |
| OS << ";\n"; |
| if (Placement == MethodDeclarationPlacement::Before) |
| OS << "\n"; |
| Replacements.push_back(RefactoringReplacement( |
| SourceRange(DeclarationLoc, DeclarationLoc), std::move(OS.str()), |
| CreatedSymbol.get(), SymbolLoc)); |
| } |
| std::string ExtractedCode; |
| llvm::raw_string_ostream ExtractedOS(ExtractedCode); |
| RefactoringReplacement::AssociatedSymbolLocation SymbolLoc = |
| PrintFunctionHeader(ExtractedOS); |
| ExtractedOS << " {\n"; |
| if (IsExpr && !ReturnType->isVoidType()) |
| ExtractedOS << "return "; |
| SourceRange ExtractedTokenRange = |
| CandidateExtractionInfo[SelectedCandidateIndex].Range; |
| auto Semicolons = computeSemicolonExtractionPolicy( |
| ExtractedStmtRange ? *(ExtractedStmtRange->Last) : S, ExtractedTokenRange, |
| SM, LangOpts); |
| ExtractedOS << SourceRewriter.getRewrittenText(ExtractedTokenRange); |
| if (Semicolons.IsNeededInExtractedFunction) |
| ExtractedOS << ';'; |
| if (CanUseReturnForVariablesUsedAfterwards) |
| ExtractedOS << "\nreturn " << RedeclaredVariables.front().VD->getName() |
| << ";"; |
| ExtractedOS << "\n}\n\n"; |
| Replacements.push_back(RefactoringReplacement( |
| SourceRange(FunctionExtractionLoc, FunctionExtractionLoc), |
| std::move(ExtractedOS.str()), CreatedSymbol.get(), SymbolLoc)); |
| |
| // Create a replacements that removes the extracted code in favor of the |
| // function call. |
| std::string InsertedCode; |
| llvm::raw_string_ostream InsertedOS(InsertedCode); |
| // We might have to declare variables that were declared in the extracted code |
| // but still used afterwards. |
| if (CanUseReturnForVariablesUsedAfterwards) { |
| const auto &Var = RedeclaredVariables.front(); |
| Var.VD->getType().print(InsertedOS, PP); |
| InsertedOS << ' ' << Var.VD->getName() << " = "; |
| } else { |
| for (const auto &Var : RedeclaredVariables) { |
| Var.VD->getType().print(InsertedOS, PP); |
| InsertedOS << ' ' << Var.VD->getName() << ";\n"; |
| } |
| } |
| InsertedOS << CandidateExtractionInfo[SelectedCandidateIndex].PreInsertedText; |
| llvm::SmallVector<unsigned, 4> NameOffsets; |
| if (isMethodExtraction() && EnclosingObjCMethod) { |
| InsertedOS << "[self "; |
| NameOffsets.push_back(InsertedOS.str().size()); |
| InsertedOS << ExtractedName; |
| bool IsFirst = true; |
| for (const auto &Var : CapturedVariables) { |
| if (!IsFirst) { |
| InsertedOS << ' '; |
| NameOffsets.push_back(InsertedOS.str().size()); |
| InsertedOS << Var.getName(); |
| } |
| IsFirst = false; |
| InsertedOS << ':'; |
| if (Var.TakeAddress) |
| InsertedOS << '&'; |
| InsertedOS << Var.getExpr(); |
| } |
| InsertedOS << ']'; |
| } else { |
| NameOffsets.push_back(InsertedOS.str().size()); |
| InsertedOS << ExtractedName << '('; |
| bool IsFirst = true; |
| for (const auto &Var : CapturedVariables) { |
| if (!IsFirst) |
| InsertedOS << ", "; |
| IsFirst = false; |
| if (Var.TakeAddress) |
| InsertedOS << '&'; |
| InsertedOS << Var.getExpr(); |
| } |
| InsertedOS << ')'; |
| } |
| if (Semicolons.IsNeededInOriginalFunction) |
| InsertedOS << ';'; |
| SourceRange ExtractedCharRange = SourceRange( |
| ExtractedTokenRange.getBegin(), |
| getPreciseTokenLocEnd(ExtractedTokenRange.getEnd(), SM, LangOpts)); |
| Replacements.push_back(RefactoringReplacement( |
| ExtractedCharRange, std::move(InsertedOS.str()), CreatedSymbol.get(), |
| llvm::makeArrayRef(NameOffsets))); |
| |
| RefactoringResult Result(std::move(Replacements)); |
| Result.AssociatedSymbols.push_back(std::move(CreatedSymbol)); |
| return std::move(Result); |
| } |