57#include "clang/AST/ASTContext.h"
58#include "clang/AST/Decl.h"
59#include "clang/AST/DeclBase.h"
60#include "clang/AST/ExprCXX.h"
61#include "clang/AST/NestedNameSpecifier.h"
62#include "clang/AST/RecursiveASTVisitor.h"
63#include "clang/AST/Stmt.h"
64#include "clang/Basic/LangOptions.h"
65#include "clang/Basic/SourceLocation.h"
66#include "clang/Basic/SourceManager.h"
67#include "clang/Tooling/Core/Replacement.h"
68#include "clang/Tooling/Refactoring/Extract/SourceExtraction.h"
69#include "llvm/ADT/STLExtras.h"
70#include "llvm/ADT/SmallSet.h"
71#include "llvm/ADT/SmallVector.h"
72#include "llvm/ADT/StringRef.h"
73#include "llvm/Support/Casting.h"
74#include "llvm/Support/Error.h"
86enum class ZoneRelative {
93enum FunctionDeclKind {
104bool isUnselectedRootStmtCandidate(
const Node *N) {
105 return N->ASTNode.get<DeclStmt>() || N->ASTNode.get<CXXOperatorCallExpr>();
125bool isConditionClause(
const Stmt *Parent,
const Stmt *Child) {
126 if (
const auto *If = llvm::dyn_cast<IfStmt>(Parent))
127 return Child == If->getCond() ||
128 Child == If->getConditionVariableDeclStmt();
129 if (
const auto *For = llvm::dyn_cast<ForStmt>(Parent))
130 return Child == For->getCond() ||
131 Child == For->getConditionVariableDeclStmt();
132 if (
const auto *While = llvm::dyn_cast<WhileStmt>(Parent))
133 return Child == While->getCond() ||
134 Child == While->getConditionVariableDeclStmt();
135 if (
const auto *Do = llvm::dyn_cast<DoStmt>(Parent))
136 return Child == Do->getCond();
137 if (
const auto *Switch = llvm::dyn_cast<SwitchStmt>(Parent))
138 return Child == Switch->getCond() ||
139 Child == Switch->getConditionVariableDeclStmt();
140 if (
const auto *ForRange = llvm::dyn_cast<CXXForRangeStmt>(Parent))
141 return Child == ForRange->getRangeInit();
148bool isRootStmt(
const Node *N) {
149 const Stmt *S = N->ASTNode.get<Stmt>();
156 !isUnselectedRootStmtCandidate(N))
158 if (
const Stmt *Parent = N->Parent ? N->Parent->ASTNode.get<Stmt>() :
nullptr)
159 if (isConditionClause(Parent, S))
184const Node *getEnclosingStmt(
const Node *Child) {
185 const Node *Parent = Child->Parent;
186 if (Parent->ASTNode.get<DeclStmt>())
187 Parent = Parent->Parent;
188 if (!Parent->ASTNode.get<Stmt>() || Parent->ASTNode.get<Expr>())
203const Node *getParentOfRootStmts(
const Node *CommonAnc) {
206 const Node *Parent =
nullptr;
207 switch (CommonAnc->Selected) {
215 if (isUnselectedRootStmtCandidate(CommonAnc)) {
216 Parent = getEnclosingStmt(CommonAnc);
226 Parent = getEnclosingStmt(CommonAnc);
232 return llvm::all_of(Parent->Children, isRootStmt) ? Parent :
nullptr;
236struct ExtractionZone {
238 const Node *Parent =
nullptr;
240 SourceRange ZoneRange;
242 const FunctionDecl *EnclosingFunction =
nullptr;
244 SourceRange EnclosingFuncRange;
246 llvm::DenseSet<const Stmt *> RootStmts;
248 SourceLocation getInsertionPoint()
const {
249 return EnclosingFuncRange.getBegin();
251 bool isRootStmt(
const Stmt *S)
const;
254 const Node *getLastRootStmt()
const {
return Parent->Children.back(); }
260 bool requiresHoisting(
const SourceManager &SM,
261 const HeuristicResolver *Resolver)
const {
263 llvm::SmallPtrSet<const Decl *, 1> DeclsInExtZone;
264 for (
auto *RootStmt : RootStmts) {
267 [&DeclsInExtZone](
const ReferenceLoc &Loc) {
270 DeclsInExtZone.insert(Loc.Targets.front());
275 if (DeclsInExtZone.empty())
278 for (
const auto *S : EnclosingFunction->getBody()->children()) {
279 if (SM.isBeforeInTranslationUnit(S->getSourceRange().getEnd(),
282 bool HasPostUse =
false;
285 [&](
const ReferenceLoc &Loc) {
287 SM.isBeforeInTranslationUnit(Loc.NameLoc, ZoneRange.getEnd()))
289 HasPostUse = llvm::any_of(Loc.Targets,
290 [&DeclsInExtZone](
const Decl *Target) {
291 return DeclsInExtZone.contains(Target);
306bool alwaysReturns(
const ExtractionZone &EZ) {
307 const Stmt *Last = EZ.getLastRootStmt()->ASTNode.get<Stmt>();
309 while (
const auto *CS = llvm::dyn_cast<CompoundStmt>(Last)) {
310 if (CS->body_empty())
312 Last = CS->body_back();
314 return llvm::isa<ReturnStmt>(Last);
317bool ExtractionZone::isRootStmt(
const Stmt *S)
const {
318 return RootStmts.contains(S);
322const FunctionDecl *findEnclosingFunction(
const Node *CommonAnc) {
324 for (
const Node *CurNode = CommonAnc; CurNode; CurNode = CurNode->Parent) {
326 if (CurNode->ASTNode.get<LambdaExpr>())
328 if (
const FunctionDecl *Func = CurNode->ASTNode.get<FunctionDecl>()) {
330 if (Func->isTemplated())
332 if (!Func->getBody())
334 for (
const auto *S : Func->getBody()->children()) {
349std::optional<SourceRange> findZoneRange(
const Node *Parent,
350 const SourceManager &SM,
351 const LangOptions &LangOpts) {
354 SM, LangOpts, Parent->Children.front()->ASTNode.getSourceRange()))
355 SR.setBegin(BeginFileRange->getBegin());
359 SM, LangOpts, Parent->Children.back()->ASTNode.getSourceRange()))
360 SR.setEnd(EndFileRange->getEnd());
370std::optional<SourceRange>
371computeEnclosingFuncRange(
const FunctionDecl *EnclosingFunction,
372 const SourceManager &SM,
373 const LangOptions &LangOpts) {
379bool validSingleChild(
const Node *Child,
const FunctionDecl *EnclosingFunc) {
381 assert(EnclosingFunc->hasBody() &&
382 "We should always be extracting from a function body.");
383 if (Child->ASTNode.get<Stmt>() == EnclosingFunc->getBody())
388std::optional<ExtractionZone> findExtractionZone(
const Node *CommonAnc,
389 const SourceManager &SM,
390 const LangOptions &LangOpts) {
391 ExtractionZone ExtZone;
392 ExtZone.Parent = getParentOfRootStmts(CommonAnc);
393 if (!ExtZone.Parent || ExtZone.Parent->Children.empty())
395 ExtZone.EnclosingFunction = findEnclosingFunction(ExtZone.Parent);
396 if (!ExtZone.EnclosingFunction)
400 if (ExtZone.Parent->Children.size() == 1 &&
401 !validSingleChild(ExtZone.getLastRootStmt(), ExtZone.EnclosingFunction))
404 computeEnclosingFuncRange(ExtZone.EnclosingFunction, SM, LangOpts))
405 ExtZone.EnclosingFuncRange = *FuncRange;
406 if (
auto ZoneRange = findZoneRange(ExtZone.Parent, SM, LangOpts))
407 ExtZone.ZoneRange = *ZoneRange;
408 if (ExtZone.EnclosingFuncRange.isInvalid() || ExtZone.ZoneRange.isInvalid())
411 for (
const Node *Child : ExtZone.Parent->Children)
412 ExtZone.RootStmts.insert(Child->ASTNode.get<Stmt>());
423 bool PassByReference;
424 unsigned OrderPriority;
425 std::string render(
const DeclContext *Context)
const;
426 bool operator<(
const Parameter &Other)
const {
427 return OrderPriority < Other.OrderPriority;
430 std::string Name =
"extracted";
432 std::vector<Parameter> Parameters;
433 SourceRange BodyRange;
434 SourceLocation DefinitionPoint;
435 std::optional<SourceLocation> ForwardDeclarationPoint;
436 const CXXRecordDecl *EnclosingClass =
nullptr;
437 NestedNameSpecifier DefinitionQualifier = std::nullopt;
438 const DeclContext *SemanticDC =
nullptr;
439 const DeclContext *SyntacticDC =
nullptr;
440 const DeclContext *ForwardDeclarationSyntacticDC =
nullptr;
441 bool CallerReturnsValue =
false;
443 ConstexprSpecKind Constexpr = ConstexprSpecKind::Unspecified;
448 tooling::ExtractionSemicolonPolicy SemicolonPolicy;
449 const LangOptions *LangOpts;
450 NewFunction(tooling::ExtractionSemicolonPolicy SemicolonPolicy,
451 const LangOptions *LangOpts)
452 : SemicolonPolicy(SemicolonPolicy), LangOpts(LangOpts) {}
454 std::string renderCall()
const;
456 std::string renderDeclaration(FunctionDeclKind K,
457 const DeclContext &SemanticDC,
458 const DeclContext &SyntacticDC,
459 const SourceManager &SM)
const;
463 renderParametersForDeclaration(
const DeclContext &Enclosing)
const;
464 std::string renderParametersForCall()
const;
465 std::string renderSpecifiers(FunctionDeclKind K)
const;
466 std::string renderQualifiers()
const;
467 std::string renderDeclarationName(FunctionDeclKind K)
const;
469 std::string getFuncBody(
const SourceManager &SM)
const;
472std::string NewFunction::renderParametersForDeclaration(
473 const DeclContext &Enclosing)
const {
475 bool NeedCommaBefore =
false;
476 for (
const Parameter &P : Parameters) {
479 NeedCommaBefore =
true;
480 Result +=
P.render(&Enclosing);
485std::string NewFunction::renderParametersForCall()
const {
487 bool NeedCommaBefore =
false;
488 for (
const Parameter &P : Parameters) {
491 NeedCommaBefore =
true;
497std::string NewFunction::renderSpecifiers(FunctionDeclKind K)
const {
498 std::string Attributes;
500 if (Static && K != FunctionDeclKind::OutOfLineDefinition) {
501 Attributes +=
"static ";
505 case ConstexprSpecKind::Unspecified:
506 case ConstexprSpecKind::Constinit:
508 case ConstexprSpecKind::Constexpr:
509 Attributes +=
"constexpr ";
511 case ConstexprSpecKind::Consteval:
512 Attributes +=
"consteval ";
519std::string NewFunction::renderQualifiers()
const {
520 std::string Attributes;
523 Attributes +=
" const";
529std::string NewFunction::renderDeclarationName(FunctionDeclKind K)
const {
530 if (!DefinitionQualifier || K != OutOfLineDefinition)
533 std::string QualifierName;
534 llvm::raw_string_ostream Oss(QualifierName);
535 DefinitionQualifier.print(Oss, *LangOpts);
536 return llvm::formatv(
"{0}{1}", QualifierName, Name);
539std::string NewFunction::renderCall()
const {
541 llvm::formatv(
"{0}{1}({2}){3}", CallerReturnsValue ?
"return " :
"", Name,
542 renderParametersForCall(),
543 (SemicolonPolicy.isNeededInOriginalFunction() ?
";" :
"")));
546std::string NewFunction::renderDeclaration(FunctionDeclKind K,
547 const DeclContext &SemanticDC,
548 const DeclContext &SyntacticDC,
549 const SourceManager &SM)
const {
550 std::string
Declaration = std::string(llvm::formatv(
551 "{0}{1} {2}({3}){4}", renderSpecifiers(K),
552 printType(ReturnType, SyntacticDC), renderDeclarationName(K),
553 renderParametersForDeclaration(SemanticDC), renderQualifiers()));
556 case ForwardDeclaration:
557 return std::string(llvm::formatv(
"{0};\n", Declaration));
558 case OutOfLineDefinition:
559 case InlineDefinition:
561 llvm::formatv(
"{0} {\n{1}\n}\n", Declaration, getFuncBody(SM)));
564 llvm_unreachable(
"Unsupported FunctionDeclKind enum");
567std::string NewFunction::getFuncBody(
const SourceManager &SM)
const {
573 (SemicolonPolicy.isNeededInExtractedFunction() ?
";" :
"");
576std::string NewFunction::Parameter::render(
const DeclContext *Context)
const {
577 return printType(TypeInfo, *Context) + (PassByReference ?
" &" :
" ") + Name;
581struct CapturedZoneInfo {
582 struct DeclInformation {
584 ZoneRelative DeclaredIn;
587 bool IsReferencedInZone =
false;
588 bool IsReferencedInPostZone =
false;
592 bool IsPossiblyMutated =
false;
593 DeclInformation(
const Decl *TheDecl, ZoneRelative DeclaredIn,
595 : TheDecl(TheDecl), DeclaredIn(DeclaredIn), DeclIndex(DeclIndex){};
597 void markOccurence(ZoneRelative ReferenceLoc);
600 llvm::DenseMap<const Decl *, DeclInformation> DeclInfoMap;
601 bool HasReturnStmt =
false;
602 bool AlwaysReturns =
false;
605 bool BrokenControlFlow =
false;
608 DeclInformation *createDeclInfo(
const Decl *D, ZoneRelative RelativeLoc);
609 DeclInformation *getDeclInfoFor(
const Decl *D);
612CapturedZoneInfo::DeclInformation *
613CapturedZoneInfo::createDeclInfo(
const Decl *D, ZoneRelative RelativeLoc) {
615 auto InsertionResult = DeclInfoMap.insert(
616 {
D, DeclInformation(D, RelativeLoc, DeclInfoMap.size())});
618 return &InsertionResult.first->second;
621CapturedZoneInfo::DeclInformation *
622CapturedZoneInfo::getDeclInfoFor(
const Decl *D) {
624 auto Iter = DeclInfoMap.find(D);
625 if (Iter == DeclInfoMap.end())
627 return &Iter->second;
630void CapturedZoneInfo::DeclInformation::markOccurence(
631 ZoneRelative ReferenceLoc) {
632 switch (ReferenceLoc) {
633 case ZoneRelative::Inside:
634 IsReferencedInZone =
true;
636 case ZoneRelative::After:
637 IsReferencedInPostZone =
true;
644bool isLoop(
const Stmt *S) {
645 return isa<ForStmt>(S) || isa<DoStmt>(S) || isa<WhileStmt>(S) ||
646 isa<CXXForRangeStmt>(S);
676void collectUnderlyingDecls(
const Expr *E,
677 llvm::SmallVectorImpl<const Decl *> &Decls) {
680 E = E->IgnoreParenCasts();
681 if (
const auto *DRE = dyn_cast<DeclRefExpr>(E)) {
682 Decls.push_back(DRE->getDecl());
685 if (
const auto *ME = dyn_cast<MemberExpr>(E)) {
686 if (!ME->getBase()->getType()->isPointerType())
687 collectUnderlyingDecls(ME->getBase(), Decls);
690 if (
const auto *ASE = dyn_cast<ArraySubscriptExpr>(E)) {
691 if (!ASE->getBase()->getType()->isPointerType())
692 collectUnderlyingDecls(ASE->getBase(), Decls);
695 if (
const auto *BO = dyn_cast<BinaryOperator>(E)) {
701 if (BO->getOpcode() == BO_PtrMemD)
702 collectUnderlyingDecls(BO->getLHS(), Decls);
705 if (
const auto *CO = dyn_cast<AbstractConditionalOperator>(E)) {
706 collectUnderlyingDecls(CO->getTrueExpr(), Decls);
707 collectUnderlyingDecls(CO->getFalseExpr(), Decls);
712CapturedZoneInfo captureZoneInfo(
const ExtractionZone &ExtZone) {
716 class ExtractionZoneVisitor
717 :
public clang::RecursiveASTVisitor<ExtractionZoneVisitor> {
719 ExtractionZoneVisitor(
const ExtractionZone &ExtZone) : ExtZone(ExtZone) {
720 TraverseDecl(
const_cast<FunctionDecl *
>(ExtZone.EnclosingFunction));
723 bool TraverseStmt(Stmt *S) {
726 bool IsRootStmt = ExtZone.isRootStmt(
const_cast<const Stmt *
>(S));
730 CurrentLocation = ZoneRelative::Inside;
731 addToLoopSwitchCounters(S, 1);
733 RecursiveASTVisitor::TraverseStmt(S);
734 addToLoopSwitchCounters(S, -1);
738 CurrentLocation = ZoneRelative::After;
744 void addToLoopSwitchCounters(Stmt *S,
int Increment) {
745 if (CurrentLocation != ZoneRelative::Inside)
748 CurNumberOfNestedLoops += Increment;
749 else if (isa<SwitchStmt>(S))
750 CurNumberOfSwitch += Increment;
753 bool VisitDecl(Decl *D) {
754 Info.createDeclInfo(D, CurrentLocation);
758 bool VisitDeclRefExpr(DeclRefExpr *DRE) {
760 const Decl *
D = DRE->getDecl();
761 auto *DeclInfo =
Info.getDeclInfoFor(D);
764 DeclInfo =
Info.createDeclInfo(D, ZoneRelative::OutsideFunc);
765 DeclInfo->markOccurence(CurrentLocation);
780 void markPossiblyMutated(
const Decl *D) {
781 if (!D || CurrentLocation != ZoneRelative::Inside)
783 if (
auto *DeclInfo =
Info.getDeclInfoFor(D))
784 DeclInfo->IsPossiblyMutated =
true;
786 void markPossiblyMutated(
const Expr *E) {
787 llvm::SmallVector<const Decl *, 2> Decls;
788 collectUnderlyingDecls(E, Decls);
789 for (
const Decl *D : Decls)
790 markPossiblyMutated(D);
793 bool VisitBinaryOperator(BinaryOperator *BO) {
794 if (BO->isAssignmentOp())
795 markPossiblyMutated(BO->getLHS());
799 bool VisitUnaryOperator(UnaryOperator *UO) {
802 if (UO->isIncrementDecrementOp() || UO->getOpcode() == UO_AddrOf)
803 markPossiblyMutated(UO->getSubExpr());
807 bool VisitExplicitCastExpr(ExplicitCastExpr *ECE) {
810 if (ECE->getType()->isReferenceType() &&
811 !ECE->getType()->getPointeeType().isConstQualified())
812 markPossiblyMutated(ECE->getSubExpr());
820 void markPossiblyMutatedCallee(
const Expr *Object,
821 const CXXMethodDecl *Method) {
822 if (Method && !
Method->isStatic() && !
Method->isConst())
823 markPossiblyMutated(Object);
830 void markPossiblyMutatedArgs(ArrayRef<const Expr *> Args,
831 const FunctionDecl *Callee) {
832 for (
unsigned I = 0; I < Args.size(); ++I) {
833 if (!Callee || I >= Callee->getNumParams() ||
834 (Callee->getParamDecl(I)->getType()->isReferenceType() &&
835 !Callee->getParamDecl(I)
838 .isConstQualified()))
839 markPossiblyMutated(Args[I]);
843 bool VisitCXXMemberCallExpr(CXXMemberCallExpr *MCE) {
844 markPossiblyMutatedCallee(MCE->getImplicitObjectArgument(),
845 MCE->getMethodDecl());
849 markPossiblyMutatedArgs(
850 llvm::ArrayRef<const Expr *>(MCE->getArgs(), MCE->getNumArgs()),
851 MCE->getMethodDecl());
855 bool VisitCXXOperatorCallExpr(CXXOperatorCallExpr *OCE) {
862 if (OCE->getNumArgs() == 0)
865 dyn_cast_or_null<CXXMethodDecl>(OCE->getCalleeDecl());
867 markPossiblyMutatedCallee(OCE->getArg(0), Method);
868 markPossiblyMutatedArgs(llvm::ArrayRef<const Expr *>(
869 OCE->getArgs() + 1, OCE->getNumArgs() - 1),
872 markPossiblyMutatedArgs(
873 llvm::ArrayRef<const Expr *>(OCE->getArgs(), OCE->getNumArgs()),
874 OCE->getDirectCallee());
879 bool VisitCallExpr(CallExpr *CE) {
887 if (isa<CXXMemberCallExpr>(CE) || isa<CXXOperatorCallExpr>(CE))
889 markPossiblyMutatedArgs(
890 llvm::ArrayRef<const Expr *>(CE->getArgs(), CE->getNumArgs()),
891 CE->getDirectCallee());
895 bool VisitCXXConstructExpr(CXXConstructExpr *CCE) {
896 markPossiblyMutatedArgs(
897 llvm::ArrayRef<const Expr *>(CCE->getArgs(), CCE->getNumArgs()),
898 CCE->getConstructor());
902 bool VisitVarDecl(VarDecl *VD) {
909 if (VD->getType()->isReferenceType() &&
910 !VD->getType()->getPointeeType().isConstQualified() && VD->hasInit())
911 markPossiblyMutated(VD->getInit()->IgnoreParens());
915 bool VisitLambdaExpr(LambdaExpr *LE) {
919 for (
const LambdaCapture &C : LE->captures())
920 if (
C.capturesVariable() &&
C.getCaptureKind() == LCK_ByRef)
921 markPossiblyMutated(
C.getCapturedVar());
925 bool VisitCXXForRangeStmt(CXXForRangeStmt *FRS) {
933 QualType LoopVarType = FRS->getLoopVariable()->getType();
934 if ((LoopVarType->isReferenceType() &&
935 !LoopVarType->getPointeeType().isConstQualified()) ||
936 (LoopVarType->isPointerType() &&
937 !LoopVarType->getPointeeType().isConstQualified()))
938 if (
const Expr *RangeInit = FRS->getRangeInit())
939 markPossiblyMutated(RangeInit);
943 bool VisitReturnStmt(ReturnStmt *Return) {
944 if (CurrentLocation == ZoneRelative::Inside) {
945 Info.HasReturnStmt =
true;
953 if (
const Expr *RV = Return->getRetValue())
954 markPossiblyMutated(RV);
959 bool VisitBreakStmt(BreakStmt *Break) {
962 if (CurrentLocation == ZoneRelative::Inside &&
963 !(CurNumberOfNestedLoops || CurNumberOfSwitch))
964 Info.BrokenControlFlow =
true;
968 bool VisitContinueStmt(ContinueStmt *Continue) {
971 if (CurrentLocation == ZoneRelative::Inside && !CurNumberOfNestedLoops)
972 Info.BrokenControlFlow =
true;
975 CapturedZoneInfo
Info;
976 const ExtractionZone &ExtZone;
977 ZoneRelative CurrentLocation = ZoneRelative::Before;
980 unsigned CurNumberOfNestedLoops = 0;
981 unsigned CurNumberOfSwitch = 0;
983 ExtractionZoneVisitor Visitor(ExtZone);
984 CapturedZoneInfo Result = std::move(Visitor.Info);
985 Result.AlwaysReturns = alwaysReturns(ExtZone);
993bool createParameters(NewFunction &ExtractedFunc,
994 const CapturedZoneInfo &CapturedInfo) {
996 for (
const auto &KeyVal : CapturedInfo.DeclInfoMap) {
997 const auto &DeclInfo = KeyVal.second;
1001 if (DeclInfo.DeclaredIn == ZoneRelative::Inside &&
1002 DeclInfo.IsReferencedInPostZone)
1004 if (!DeclInfo.IsReferencedInZone)
1006 if (DeclInfo.DeclaredIn == ZoneRelative::Inside ||
1007 DeclInfo.DeclaredIn == ZoneRelative::OutsideFunc)
1010 const ValueDecl *VD = dyn_cast_or_null<ValueDecl>(DeclInfo.TheDecl);
1013 if (!VD || isa<FunctionDecl>(DeclInfo.TheDecl))
1016 QualType TypeInfo = VD->getType().getNonReferenceType();
1022 if (!DeclInfo.IsPossiblyMutated && !TypeInfo->isArrayType())
1023 TypeInfo.addConst();
1025 bool IsPassedByReference =
true;
1027 ExtractedFunc.Parameters.push_back({std::string(VD->getName()), TypeInfo,
1028 IsPassedByReference,
1029 DeclInfo.DeclIndex});
1031 llvm::sort(ExtractedFunc.Parameters);
1038tooling::ExtractionSemicolonPolicy
1039getSemicolonPolicy(ExtractionZone &ExtZone,
const SourceManager &SM,
1040 const LangOptions &LangOpts) {
1042 SourceRange FuncBodyRange = {ExtZone.ZoneRange.getBegin(),
1043 ExtZone.ZoneRange.getEnd().getLocWithOffset(-1)};
1044 auto SemicolonPolicy = tooling::ExtractionSemicolonPolicy::compute(
1045 ExtZone.getLastRootStmt()->ASTNode.get<Stmt>(), FuncBodyRange, SM,
1048 ExtZone.ZoneRange.setEnd(FuncBodyRange.getEnd().getLocWithOffset(1));
1049 return SemicolonPolicy;
1053bool generateReturnProperties(NewFunction &ExtractedFunc,
1054 const FunctionDecl &EnclosingFunc,
1055 const CapturedZoneInfo &CapturedInfo) {
1059 if (CapturedInfo.HasReturnStmt) {
1062 if (!CapturedInfo.AlwaysReturns)
1064 QualType Ret = EnclosingFunc.getReturnType();
1067 if (Ret->isDependentType())
1069 ExtractedFunc.ReturnType = Ret;
1073 ExtractedFunc.ReturnType = EnclosingFunc.getParentASTContext().VoidTy;
1077void captureMethodInfo(NewFunction &ExtractedFunc,
1078 const CXXMethodDecl *Method) {
1079 ExtractedFunc.Static =
Method->isStatic();
1080 ExtractedFunc.Const =
Method->isConst();
1081 ExtractedFunc.EnclosingClass =
Method->getParent();
1086llvm::Expected<NewFunction> getExtractedFunction(ExtractionZone &ExtZone,
1087 const SourceManager &SM,
1088 const LangOptions &LangOpts) {
1089 CapturedZoneInfo CapturedInfo = captureZoneInfo(ExtZone);
1091 if (CapturedInfo.BrokenControlFlow)
1092 return error(
"Cannot extract break/continue without corresponding "
1093 "loop/switch statement.");
1094 NewFunction ExtractedFunc(getSemicolonPolicy(ExtZone, SM, LangOpts),
1097 ExtractedFunc.SyntacticDC =
1098 ExtZone.EnclosingFunction->getLexicalDeclContext();
1099 ExtractedFunc.SemanticDC = ExtZone.EnclosingFunction->getDeclContext();
1100 ExtractedFunc.DefinitionQualifier = ExtZone.EnclosingFunction->getQualifier();
1101 ExtractedFunc.Constexpr = ExtZone.EnclosingFunction->getConstexprKind();
1103 if (
const auto *Method =
1104 llvm::dyn_cast<CXXMethodDecl>(ExtZone.EnclosingFunction))
1105 captureMethodInfo(ExtractedFunc, Method);
1107 if (ExtZone.EnclosingFunction->isOutOfLine()) {
1110 const auto *FirstOriginalDecl =
1111 ExtZone.EnclosingFunction->getCanonicalDecl();
1115 return error(
"Declaration is inside a macro");
1116 ExtractedFunc.ForwardDeclarationPoint = DeclPos->getBegin();
1117 ExtractedFunc.ForwardDeclarationSyntacticDC = ExtractedFunc.SemanticDC;
1120 ExtractedFunc.BodyRange = ExtZone.ZoneRange;
1121 ExtractedFunc.DefinitionPoint = ExtZone.getInsertionPoint();
1123 ExtractedFunc.CallerReturnsValue = CapturedInfo.AlwaysReturns;
1124 if (!createParameters(ExtractedFunc, CapturedInfo) ||
1125 !generateReturnProperties(ExtractedFunc, *ExtZone.EnclosingFunction,
1127 return error(
"Too complex to extract.");
1128 return ExtractedFunc;
1131class ExtractFunction :
public Tweak {
1133 const char *id() const final;
1134 bool prepare(const Selection &Inputs) override;
1135 Expected<Effect> apply(const Selection &Inputs) override;
1136 std::
string title()
const override {
return "Extract to function"; }
1137 llvm::StringLiteral kind()
const override {
1138 return CodeAction::REFACTOR_KIND;
1142 ExtractionZone ExtZone;
1146tooling::Replacement replaceWithFuncCall(
const NewFunction &ExtractedFunc,
1147 const SourceManager &SM,
1148 const LangOptions &LangOpts) {
1149 std::string FuncCall = ExtractedFunc.renderCall();
1150 return tooling::Replacement(
1151 SM, CharSourceRange(ExtractedFunc.BodyRange,
false), FuncCall, LangOpts);
1154tooling::Replacement createFunctionDefinition(
const NewFunction &ExtractedFunc,
1155 const SourceManager &SM) {
1156 FunctionDeclKind DeclKind = InlineDefinition;
1157 if (ExtractedFunc.ForwardDeclarationPoint)
1158 DeclKind = OutOfLineDefinition;
1159 std::string FunctionDef = ExtractedFunc.renderDeclaration(
1160 DeclKind, *ExtractedFunc.SemanticDC, *ExtractedFunc.SyntacticDC, SM);
1162 return tooling::Replacement(SM, ExtractedFunc.DefinitionPoint, 0,
1166tooling::Replacement createForwardDeclaration(
const NewFunction &ExtractedFunc,
1167 const SourceManager &SM) {
1168 std::string FunctionDecl = ExtractedFunc.renderDeclaration(
1169 ForwardDeclaration, *ExtractedFunc.SemanticDC,
1170 *ExtractedFunc.ForwardDeclarationSyntacticDC, SM);
1171 SourceLocation DeclPoint = *ExtractedFunc.ForwardDeclarationPoint;
1173 return tooling::Replacement(SM, DeclPoint, 0, FunctionDecl);
1177bool hasReturnStmt(
const ExtractionZone &ExtZone) {
1178 class ReturnStmtVisitor
1179 :
public clang::RecursiveASTVisitor<ReturnStmtVisitor> {
1181 bool VisitReturnStmt(ReturnStmt *Return) {
1188 ReturnStmtVisitor V;
1189 for (
const Stmt *RootStmt : ExtZone.RootStmts) {
1190 V.TraverseStmt(
const_cast<Stmt *
>(RootStmt));
1197bool ExtractFunction::prepare(
const Selection &Inputs) {
1198 const LangOptions &LangOpts = Inputs.AST->getLangOpts();
1199 if (!LangOpts.CPlusPlus)
1201 const Node *CommonAnc = Inputs.ASTSelection.commonAncestor();
1202 const SourceManager &SM = Inputs.AST->getSourceManager();
1203 auto MaybeExtZone = findExtractionZone(CommonAnc, SM, LangOpts);
1204 if (!MaybeExtZone ||
1205 (hasReturnStmt(*MaybeExtZone) && !alwaysReturns(*MaybeExtZone)))
1209 if (MaybeExtZone->requiresHoisting(SM, Inputs.AST->getHeuristicResolver()))
1212 ExtZone = std::move(*MaybeExtZone);
1216Expected<Tweak::Effect> ExtractFunction::apply(
const Selection &Inputs) {
1217 const SourceManager &SM = Inputs.AST->getSourceManager();
1218 const LangOptions &LangOpts = Inputs.AST->getLangOpts();
1219 auto ExtractedFunc = getExtractedFunction(ExtZone, SM, LangOpts);
1222 return ExtractedFunc.takeError();
1223 tooling::Replacements Edit;
1224 if (
auto Err = Edit.add(createFunctionDefinition(*ExtractedFunc, SM)))
1225 return std::move(Err);
1226 if (
auto Err = Edit.add(replaceWithFuncCall(*ExtractedFunc, SM, LangOpts)))
1227 return std::move(Err);
1229 if (
auto FwdLoc = ExtractedFunc->ForwardDeclarationPoint) {
1232 if (SM.isWrittenInSameFile(ExtractedFunc->DefinitionPoint, *FwdLoc)) {
1233 if (
auto Err = Edit.add(createForwardDeclaration(*ExtractedFunc, SM)))
1234 return std::move(Err);
1236 auto MultiFileEffect = Effect::mainFileEdit(SM, std::move(Edit));
1237 if (!MultiFileEffect)
1238 return MultiFileEffect.takeError();
1240 tooling::Replacements OtherEdit(
1241 createForwardDeclaration(*ExtractedFunc, SM));
1242 if (
auto PathAndEdit =
1243 Tweak::Effect::fileEdit(SM, SM.getFileID(*FwdLoc), OtherEdit))
1244 MultiFileEffect->ApplyEdits.try_emplace(PathAndEdit->first,
1245 PathAndEdit->second);
1247 return PathAndEdit.takeError();
1248 return MultiFileEffect;
1251 return Effect::mainFileEdit(SM, std::move(Edit));
#define REGISTER_TWEAK(Subclass)
llvm::Error error(std::error_code, std::string &&)
FIXME: Skip testing on windows temporarily due to the different escaping code mode.
@ Info
An information message.
std::optional< SourceRange > toHalfOpenFileRange(const SourceManager &SM, const LangOptions &LangOpts, SourceRange R)
Turns a token range into a half-open range and checks its correctness.
std::string printType(const QualType QT, const DeclContext &CurContext, const llvm::StringRef Placeholder, bool FullyQualify)
Returns a QualType as string.
void findExplicitReferences(const Stmt *S, llvm::function_ref< void(ReferenceLoc)> Out, const HeuristicResolver *Resolver)
Recursively traverse S and report all references explicitly written in the code.
llvm::StringRef toSourceCode(const SourceManager &SM, SourceRange R)
Returns the source code covered by the source range.
bool operator<(const Ref &L, const Ref &R)
@ Parameter
An inlay hint that is for a parameter.
cppcoreguidelines::ProBoundsAvoidUncheckedContainerAccessCheck P
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//