62#include "clang/AST/ASTContext.h"
63#include "clang/AST/Decl.h"
64#include "clang/AST/DeclBase.h"
65#include "clang/AST/ExprCXX.h"
66#include "clang/AST/NestedNameSpecifier.h"
67#include "clang/AST/RecursiveASTVisitor.h"
68#include "clang/AST/Stmt.h"
69#include "clang/Basic/LangOptions.h"
70#include "clang/Basic/SourceLocation.h"
71#include "clang/Basic/SourceManager.h"
72#include "clang/Tooling/Core/Replacement.h"
73#include "clang/Tooling/Refactoring/Extract/SourceExtraction.h"
74#include "llvm/ADT/STLExtras.h"
75#include "llvm/ADT/SmallSet.h"
76#include "llvm/ADT/SmallVector.h"
77#include "llvm/ADT/StringRef.h"
78#include "llvm/Support/Casting.h"
79#include "llvm/Support/Error.h"
91enum class ZoneRelative {
98enum FunctionDeclKind {
105enum class ParamPassKind {
120bool isUnselectedRootStmtCandidate(
const Node *N) {
121 return N->ASTNode.get<DeclStmt>() || N->ASTNode.get<CXXOperatorCallExpr>();
141bool isConditionClause(
const Stmt *Parent,
const Stmt *Child) {
142 if (
const auto *If = llvm::dyn_cast<IfStmt>(Parent))
143 return Child == If->getCond() ||
144 Child == If->getConditionVariableDeclStmt();
145 if (
const auto *For = llvm::dyn_cast<ForStmt>(Parent))
146 return Child == For->getCond() ||
147 Child == For->getConditionVariableDeclStmt();
148 if (
const auto *While = llvm::dyn_cast<WhileStmt>(Parent))
149 return Child == While->getCond() ||
150 Child == While->getConditionVariableDeclStmt();
151 if (
const auto *Do = llvm::dyn_cast<DoStmt>(Parent))
152 return Child == Do->getCond();
153 if (
const auto *Switch = llvm::dyn_cast<SwitchStmt>(Parent))
154 return Child == Switch->getCond() ||
155 Child == Switch->getConditionVariableDeclStmt();
156 if (
const auto *ForRange = llvm::dyn_cast<CXXForRangeStmt>(Parent))
157 return Child == ForRange->getRangeInit();
164bool isRootStmt(
const Node *N) {
165 const Stmt *S = N->ASTNode.get<Stmt>();
172 !isUnselectedRootStmtCandidate(N))
174 if (
const Stmt *Parent = N->Parent ? N->Parent->ASTNode.get<Stmt>() :
nullptr)
175 if (isConditionClause(Parent, S))
200const Node *getEnclosingStmt(
const Node *Child) {
201 const Node *Parent = Child->Parent;
202 if (Parent->ASTNode.get<DeclStmt>())
203 Parent = Parent->Parent;
204 if (!Parent->ASTNode.get<Stmt>() || Parent->ASTNode.get<Expr>())
219const Node *getParentOfRootStmts(
const Node *CommonAnc) {
222 const Node *Parent =
nullptr;
223 switch (CommonAnc->Selected) {
231 if (isUnselectedRootStmtCandidate(CommonAnc)) {
232 Parent = getEnclosingStmt(CommonAnc);
242 Parent = getEnclosingStmt(CommonAnc);
248 return llvm::all_of(Parent->Children, isRootStmt) ? Parent :
nullptr;
252struct ExtractionZone {
254 const Node *Parent =
nullptr;
256 SourceRange ZoneRange;
258 const FunctionDecl *EnclosingFunction =
nullptr;
260 SourceRange EnclosingFuncRange;
262 llvm::DenseSet<const Stmt *> RootStmts;
264 SourceLocation getInsertionPoint()
const {
265 return EnclosingFuncRange.getBegin();
267 bool isRootStmt(
const Stmt *S)
const;
270 const Node *getLastRootStmt()
const {
return Parent->Children.back(); }
276 bool requiresHoisting(
const SourceManager &SM,
277 const HeuristicResolver *Resolver)
const {
279 llvm::SmallPtrSet<const Decl *, 1> DeclsInExtZone;
280 for (
auto *RootStmt : RootStmts) {
283 [&DeclsInExtZone](
const ReferenceLoc &Loc) {
286 DeclsInExtZone.insert(Loc.Targets.front());
291 if (DeclsInExtZone.empty())
294 for (
const auto *S : EnclosingFunction->getBody()->children()) {
295 if (SM.isBeforeInTranslationUnit(S->getSourceRange().getEnd(),
298 bool HasPostUse =
false;
301 [&](
const ReferenceLoc &Loc) {
303 SM.isBeforeInTranslationUnit(Loc.NameLoc, ZoneRange.getEnd()))
305 HasPostUse = llvm::any_of(Loc.Targets,
306 [&DeclsInExtZone](
const Decl *Target) {
307 return DeclsInExtZone.contains(Target);
322bool alwaysReturns(
const ExtractionZone &EZ) {
323 const Stmt *Last = EZ.getLastRootStmt()->ASTNode.get<Stmt>();
325 while (
const auto *CS = llvm::dyn_cast<CompoundStmt>(Last)) {
326 if (CS->body_empty())
328 Last = CS->body_back();
330 return llvm::isa<ReturnStmt>(Last);
333bool ExtractionZone::isRootStmt(
const Stmt *S)
const {
334 return RootStmts.contains(S);
338const FunctionDecl *findEnclosingFunction(
const Node *CommonAnc) {
340 for (
const Node *CurNode = CommonAnc; CurNode; CurNode = CurNode->Parent) {
342 if (CurNode->ASTNode.get<LambdaExpr>())
344 if (
const FunctionDecl *Func = CurNode->ASTNode.get<FunctionDecl>()) {
346 if (Func->isTemplated())
348 if (!Func->getBody())
350 for (
const auto *S : Func->getBody()->children()) {
365std::optional<SourceRange> findZoneRange(
const Node *Parent,
366 const SourceManager &SM,
367 const LangOptions &LangOpts) {
370 SM, LangOpts, Parent->Children.front()->ASTNode.getSourceRange()))
371 SR.setBegin(BeginFileRange->getBegin());
375 SM, LangOpts, Parent->Children.back()->ASTNode.getSourceRange()))
376 SR.setEnd(EndFileRange->getEnd());
386std::optional<SourceRange>
387computeEnclosingFuncRange(
const FunctionDecl *EnclosingFunction,
388 const SourceManager &SM,
389 const LangOptions &LangOpts) {
402bool mustBeForwarded(
const Decl *D) {
403 if (!isa<VarDecl, BindingDecl>(D))
405 return D->getDeclContext()->isFunctionOrMethod();
415const Decl *bareForwardedDecl(
const Expr *E) {
416 const auto *DRE = dyn_cast<DeclRefExpr>(E->IgnoreParenImpCasts());
417 if (!DRE || !mustBeForwarded(DRE->getDecl()))
419 return DRE->getDecl();
429llvm::SmallVector<const Expr *, 4> relevantCallOperands(
const CallExpr *
Call) {
430 llvm::SmallVector<const Expr *, 4> Operands;
431 if (
const auto *MCE = dyn_cast<CXXMemberCallExpr>(
Call)) {
436 if (
const Expr *ImplicitObject = MCE->getImplicitObjectArgument()) {
442 if (!isa<CXXThisExpr>(ImplicitObject->IgnoreParenImpCasts()))
443 Operands.push_back(ImplicitObject);
450 if (!MCE->getMethodDecl())
451 Operands.push_back(MCE->getCallee());
452 }
else if (!isa<CXXOperatorCallExpr>(
Call)) {
453 const Expr *Callee =
Call->getCallee()->IgnoreParenImpCasts();
454 if (!(isa<DeclRefExpr>(Callee) &&
455 isa<FunctionDecl>(cast<DeclRefExpr>(Callee)->getDecl())))
456 Operands.push_back(
Call->getCallee());
464 auto Args =
Call->arguments();
465 auto FirstDefault = llvm::find_if(
466 Args, [](
const Expr *Arg) {
return isa<CXXDefaultArgExpr>(Arg); });
467 Operands.append(Args.begin(), FirstDefault);
504bool isTrivialExtraction(
const Stmt *S,
const SourceManager &SM) {
505 if (
const auto *RS = dyn_cast<ReturnStmt>(S)) {
506 if (!RS->getRetValue())
508 S = RS->getRetValue()->IgnoreParenImpCasts();
510 const auto *
Call = dyn_cast<CallExpr>(S);
513 llvm::SmallPtrSet<const Decl *, 4> SeenDecls;
514 SourceLocation LastDeclLoc;
515 for (
const Expr *Operand : relevantCallOperands(
Call)) {
516 const Decl *
D = bareForwardedDecl(Operand);
517 if (!D || !SeenDecls.insert(D).second)
519 SourceLocation DeclLoc =
D->getLocation();
520 if (LastDeclLoc.isValid() &&
521 !SM.isBeforeInTranslationUnit(LastDeclLoc, DeclLoc))
523 LastDeclLoc = DeclLoc;
530bool validSingleChild(
const Node *Child,
const FunctionDecl *EnclosingFunc,
531 const SourceManager &SM) {
533 assert(EnclosingFunc->hasBody() &&
534 "We should always be extracting from a function body.");
535 const Stmt *S = Child->ASTNode.get<Stmt>();
536 if (S == EnclosingFunc->getBody())
538 if (isTrivialExtraction(S, SM))
543std::optional<ExtractionZone> findExtractionZone(
const Node *CommonAnc,
544 const SourceManager &SM,
545 const LangOptions &LangOpts) {
546 ExtractionZone ExtZone;
547 ExtZone.Parent = getParentOfRootStmts(CommonAnc);
548 if (!ExtZone.Parent || ExtZone.Parent->Children.empty())
550 ExtZone.EnclosingFunction = findEnclosingFunction(ExtZone.Parent);
551 if (!ExtZone.EnclosingFunction)
555 if (ExtZone.Parent->Children.size() == 1 &&
556 !validSingleChild(ExtZone.getLastRootStmt(), ExtZone.EnclosingFunction,
560 computeEnclosingFuncRange(ExtZone.EnclosingFunction, SM, LangOpts))
561 ExtZone.EnclosingFuncRange = *FuncRange;
562 if (
auto ZoneRange = findZoneRange(ExtZone.Parent, SM, LangOpts))
563 ExtZone.ZoneRange = *ZoneRange;
564 if (ExtZone.EnclosingFuncRange.isInvalid() || ExtZone.ZoneRange.isInvalid())
567 for (
const Node *Child : ExtZone.Parent->Children)
568 ExtZone.RootStmts.insert(Child->ASTNode.get<Stmt>());
580 unsigned OrderPriority;
581 std::string render(
const DeclContext &Context)
const;
582 bool operator<(
const Parameter &Other)
const {
583 return OrderPriority < Other.OrderPriority;
586 std::string Name =
"extracted";
588 std::vector<Parameter> Parameters;
589 SourceRange BodyRange;
590 SourceLocation DefinitionPoint;
591 std::optional<SourceLocation> ForwardDeclarationPoint;
592 const CXXRecordDecl *EnclosingClass =
nullptr;
593 NestedNameSpecifier DefinitionQualifier = std::nullopt;
594 const DeclContext *SemanticDC =
nullptr;
595 const DeclContext *SyntacticDC =
nullptr;
596 const DeclContext *ForwardDeclarationSyntacticDC =
nullptr;
597 bool CallerReturnsValue =
false;
599 ConstexprSpecKind Constexpr = ConstexprSpecKind::Unspecified;
606 struct PointerRewriteSite {
615 std::optional<SourceLocation> DotLoc;
617 std::vector<PointerRewriteSite> PointerRewriteSites;
621 tooling::ExtractionSemicolonPolicy SemicolonPolicy;
622 const LangOptions *LangOpts;
623 NewFunction(tooling::ExtractionSemicolonPolicy SemicolonPolicy,
624 const LangOptions *LangOpts)
625 : SemicolonPolicy(SemicolonPolicy), LangOpts(LangOpts) {}
627 std::string renderCall()
const;
629 llvm::Expected<std::string> renderDeclaration(FunctionDeclKind K,
630 const DeclContext &SemanticDC,
631 const DeclContext &SyntacticDC,
632 const SourceManager &SM)
const;
636 renderParametersForDeclaration(
const DeclContext &Enclosing)
const;
637 std::string renderParametersForCall()
const;
638 std::string renderSpecifiers(FunctionDeclKind K)
const;
639 std::string renderQualifiers()
const;
640 std::string renderDeclarationName(FunctionDeclKind K)
const;
642 llvm::Expected<std::string> getFuncBody(
const SourceManager &SM)
const;
645std::string NewFunction::renderParametersForDeclaration(
646 const DeclContext &Enclosing)
const {
648 bool NeedCommaBefore =
false;
649 for (
const Parameter &P : Parameters) {
652 NeedCommaBefore =
true;
653 Result +=
P.render(Enclosing);
658std::string NewFunction::renderParametersForCall()
const {
660 bool NeedCommaBefore =
false;
661 for (
const Parameter &P : Parameters) {
664 NeedCommaBefore =
true;
665 if (
P.Kind == ParamPassKind::Pointer)
672std::string NewFunction::renderSpecifiers(FunctionDeclKind K)
const {
673 std::string Attributes;
675 if (Static && K != FunctionDeclKind::OutOfLineDefinition) {
676 Attributes +=
"static ";
680 case ConstexprSpecKind::Unspecified:
681 case ConstexprSpecKind::Constinit:
683 case ConstexprSpecKind::Constexpr:
684 Attributes +=
"constexpr ";
686 case ConstexprSpecKind::Consteval:
687 Attributes +=
"consteval ";
694std::string NewFunction::renderQualifiers()
const {
695 std::string Attributes;
698 Attributes +=
" const";
704std::string NewFunction::renderDeclarationName(FunctionDeclKind K)
const {
705 if (!DefinitionQualifier || K != OutOfLineDefinition)
708 std::string QualifierName;
709 llvm::raw_string_ostream Oss(QualifierName);
710 DefinitionQualifier.print(Oss, *LangOpts);
711 return llvm::formatv(
"{0}{1}", QualifierName, Name);
714std::string NewFunction::renderCall()
const {
716 llvm::formatv(
"{0}{1}({2}){3}", CallerReturnsValue ?
"return " :
"", Name,
717 renderParametersForCall(),
718 (SemicolonPolicy.isNeededInOriginalFunction() ?
";" :
"")));
721llvm::Expected<std::string> NewFunction::renderDeclaration(
722 FunctionDeclKind K,
const DeclContext &SemanticDC,
723 const DeclContext &SyntacticDC,
const SourceManager &SM)
const {
724 std::string
Declaration = std::string(llvm::formatv(
725 "{0}{1} {2}({3}){4}", renderSpecifiers(K),
726 printType(ReturnType, SyntacticDC), renderDeclarationName(K),
727 renderParametersForDeclaration(SemanticDC), renderQualifiers()));
730 case ForwardDeclaration:
731 return std::string(llvm::formatv(
"{0};\n", Declaration));
732 case OutOfLineDefinition:
733 case InlineDefinition: {
734 llvm::Expected<std::string> Body = getFuncBody(SM);
736 return Body.takeError();
737 return std::string(llvm::formatv(
"{0} {\n{1}\n}\n", Declaration, *Body));
740 llvm_unreachable(
"Unsupported FunctionDeclKind enum");
743llvm::Expected<std::string>
744NewFunction::getFuncBody(
const SourceManager &SM)
const {
750 if (PointerRewriteSites.empty())
751 return Body + (SemicolonPolicy.isNeededInExtractedFunction() ?
";" :
"");
759 tooling::Replacements Repls;
760 unsigned BodyBeginOffset = SM.getFileOffset(BodyRange.getBegin());
761 for (
const auto &Site : PointerRewriteSites) {
762 unsigned SiteOffset = SM.getFileOffset(Site.Loc) - BodyBeginOffset;
766 unsigned DotOffset = SM.getFileOffset(*Site.DotLoc) - BodyBeginOffset;
767 if (
auto Err = Repls.add(tooling::Replacement(
"", DotOffset, 1,
"->")))
768 return std::move(Err);
770 if (
auto Err = Repls.add(tooling::Replacement(
"", SiteOffset, 0,
"(*")))
771 return std::move(Err);
772 if (
auto Err = Repls.add(
773 tooling::Replacement(
"", SiteOffset + Site.NameLength, 0,
")")))
774 return std::move(Err);
777 llvm::Expected<std::string> NewBody =
778 tooling::applyAllReplacements(Body, Repls);
780 return NewBody.takeError();
781 return *NewBody + (SemicolonPolicy.isNeededInExtractedFunction() ?
";" :
"");
784std::string NewFunction::Parameter::render(
const DeclContext &Context)
const {
800 QualType RenderedType =
801 Kind == ParamPassKind::Reference
802 ? Context.getParentASTContext().getLValueReferenceType(TypeInfo)
804 return printType(RenderedType, Context, Name);
808struct CapturedZoneInfo {
809 struct DeclInformation {
811 ZoneRelative DeclaredIn;
814 bool IsReferencedInZone =
false;
815 bool IsReferencedInPostZone =
false;
819 bool IsPossiblyMutated =
false;
833 std::optional<SourceLocation> DotLoc;
835 llvm::SmallVector<Occurrence, 1> ZoneOccurrences;
846 bool HasUnsafeTypeQueryUseInZone =
false;
847 DeclInformation(
const Decl *TheDecl, ZoneRelative DeclaredIn,
849 : TheDecl(TheDecl), DeclaredIn(DeclaredIn), DeclIndex(DeclIndex){};
851 void markOccurence(ZoneRelative ReferenceLoc);
854 llvm::DenseMap<const Decl *, DeclInformation> DeclInfoMap;
855 bool HasReturnStmt =
false;
856 bool AlwaysReturns =
false;
859 bool BrokenControlFlow =
false;
862 DeclInformation *createDeclInfo(
const Decl *D, ZoneRelative RelativeLoc);
863 DeclInformation *getDeclInfoFor(
const Decl *D);
866CapturedZoneInfo::DeclInformation *
867CapturedZoneInfo::createDeclInfo(
const Decl *D, ZoneRelative RelativeLoc) {
869 auto InsertionResult = DeclInfoMap.insert(
870 {
D, DeclInformation(D, RelativeLoc, DeclInfoMap.size())});
872 return &InsertionResult.first->second;
875CapturedZoneInfo::DeclInformation *
876CapturedZoneInfo::getDeclInfoFor(
const Decl *D) {
878 auto Iter = DeclInfoMap.find(D);
879 if (Iter == DeclInfoMap.end())
881 return &Iter->second;
884void CapturedZoneInfo::DeclInformation::markOccurence(
885 ZoneRelative ReferenceLoc) {
886 switch (ReferenceLoc) {
887 case ZoneRelative::Inside:
888 IsReferencedInZone =
true;
890 case ZoneRelative::After:
891 IsReferencedInPostZone =
true;
898bool isLoop(
const Stmt *S) {
899 return isa<ForStmt>(S) || isa<DoStmt>(S) || isa<WhileStmt>(S) ||
900 isa<CXXForRangeStmt>(S);
930void collectUnderlyingDecls(
const Expr *E,
931 llvm::SmallVectorImpl<const Decl *> &Decls) {
934 E = E->IgnoreParenCasts();
935 if (
const auto *DRE = dyn_cast<DeclRefExpr>(E)) {
936 Decls.push_back(DRE->getDecl());
939 if (
const auto *ME = dyn_cast<MemberExpr>(E)) {
940 if (!ME->getBase()->getType()->isPointerType())
941 collectUnderlyingDecls(ME->getBase(), Decls);
944 if (
const auto *ASE = dyn_cast<ArraySubscriptExpr>(E)) {
945 if (!ASE->getBase()->getType()->isPointerType())
946 collectUnderlyingDecls(ASE->getBase(), Decls);
949 if (
const auto *BO = dyn_cast<BinaryOperator>(E)) {
955 if (BO->getOpcode() == BO_PtrMemD)
956 collectUnderlyingDecls(BO->getLHS(), Decls);
959 if (
const auto *CO = dyn_cast<AbstractConditionalOperator>(E)) {
960 collectUnderlyingDecls(CO->getTrueExpr(), Decls);
961 collectUnderlyingDecls(CO->getFalseExpr(), Decls);
966CapturedZoneInfo captureZoneInfo(
const ExtractionZone &ExtZone) {
970 class ExtractionZoneVisitor
971 :
public clang::RecursiveASTVisitor<ExtractionZoneVisitor> {
973 ExtractionZoneVisitor(
const ExtractionZone &ExtZone) : ExtZone(ExtZone) {
974 TraverseDecl(
const_cast<FunctionDecl *
>(ExtZone.EnclosingFunction));
977 bool TraverseStmt(Stmt *S) {
980 bool IsRootStmt = ExtZone.isRootStmt(
const_cast<const Stmt *
>(S));
984 CurrentLocation = ZoneRelative::Inside;
985 addToLoopSwitchCounters(S, 1);
987 RecursiveASTVisitor::TraverseStmt(S);
988 addToLoopSwitchCounters(S, -1);
992 CurrentLocation = ZoneRelative::After;
998 void addToLoopSwitchCounters(Stmt *S,
int Increment) {
999 if (CurrentLocation != ZoneRelative::Inside)
1002 CurNumberOfNestedLoops += Increment;
1003 else if (isa<SwitchStmt>(S))
1004 CurNumberOfSwitch += Increment;
1007 bool VisitDecl(Decl *D) {
1008 Info.createDeclInfo(D, CurrentLocation);
1019 std::optional<SourceLocation> PendingMemberDotLoc;
1021 bool VisitMemberExpr(MemberExpr *ME) {
1022 if (!ME->isArrow() && isa<DeclRefExpr>(ME->getBase()->IgnoreParens()))
1023 PendingMemberDotLoc = ME->getOperatorLoc();
1032 void markUnsafeTypeQueryOperand(
const Expr *E) {
1033 if (CurrentLocation != ZoneRelative::Inside)
1035 if (
const auto *DRE = dyn_cast<DeclRefExpr>(E->IgnoreParens()))
1036 if (
auto *DeclInfo =
Info.getDeclInfoFor(DRE->getDecl()))
1037 DeclInfo->HasUnsafeTypeQueryUseInZone =
true;
1042 bool VisitUnaryExprOrTypeTraitExpr(UnaryExprOrTypeTraitExpr *E) {
1043 if (!E->isArgumentType())
1044 markUnsafeTypeQueryOperand(E->getArgumentExpr());
1051 bool VisitTypeOfExprTypeLoc(TypeOfExprTypeLoc TL) {
1052 markUnsafeTypeQueryOperand(TL.getUnderlyingExpr());
1055 bool VisitDecltypeTypeLoc(DecltypeTypeLoc TL) {
1056 markUnsafeTypeQueryOperand(TL.getUnderlyingExpr());
1060 bool VisitDeclRefExpr(DeclRefExpr *DRE) {
1062 const Decl *
D = DRE->getDecl();
1063 auto *DeclInfo =
Info.getDeclInfoFor(D);
1066 DeclInfo =
Info.createDeclInfo(D, ZoneRelative::OutsideFunc);
1067 DeclInfo->markOccurence(CurrentLocation);
1068 if (CurrentLocation == ZoneRelative::Inside)
1069 DeclInfo->ZoneOccurrences.push_back(
1070 {DRE->getLocation(), PendingMemberDotLoc});
1071 PendingMemberDotLoc.reset();
1086 void markPossiblyMutated(
const Decl *D) {
1087 if (!D || CurrentLocation != ZoneRelative::Inside)
1089 if (
auto *DeclInfo =
Info.getDeclInfoFor(D))
1090 DeclInfo->IsPossiblyMutated =
true;
1092 void markPossiblyMutated(
const Expr *E) {
1093 llvm::SmallVector<const Decl *, 2> Decls;
1094 collectUnderlyingDecls(E, Decls);
1095 for (
const Decl *D : Decls)
1096 markPossiblyMutated(D);
1099 bool VisitBinaryOperator(BinaryOperator *BO) {
1100 if (BO->isAssignmentOp())
1101 markPossiblyMutated(BO->getLHS());
1105 bool VisitUnaryOperator(UnaryOperator *UO) {
1108 if (UO->isIncrementDecrementOp() || UO->getOpcode() == UO_AddrOf)
1109 markPossiblyMutated(UO->getSubExpr());
1113 bool VisitExplicitCastExpr(ExplicitCastExpr *ECE) {
1116 if (ECE->getType()->isReferenceType() &&
1117 !ECE->getType()->getPointeeType().isConstQualified())
1118 markPossiblyMutated(ECE->getSubExpr());
1126 void markPossiblyMutatedCallee(
const Expr *Object,
1127 const CXXMethodDecl *Method) {
1128 if (Method && !
Method->isStatic() && !
Method->isConst())
1129 markPossiblyMutated(Object);
1136 void markPossiblyMutatedArgs(ArrayRef<const Expr *> Args,
1137 const FunctionDecl *Callee) {
1138 for (
unsigned I = 0; I < Args.size(); ++I) {
1139 if (!Callee || I >= Callee->getNumParams() ||
1140 (Callee->getParamDecl(I)->getType()->isReferenceType() &&
1141 !Callee->getParamDecl(I)
1144 .isConstQualified()))
1145 markPossiblyMutated(Args[I]);
1149 bool VisitCXXMemberCallExpr(CXXMemberCallExpr *MCE) {
1150 markPossiblyMutatedCallee(MCE->getImplicitObjectArgument(),
1151 MCE->getMethodDecl());
1155 markPossiblyMutatedArgs(
1156 llvm::ArrayRef<const Expr *>(MCE->getArgs(), MCE->getNumArgs()),
1157 MCE->getMethodDecl());
1161 bool VisitCXXOperatorCallExpr(CXXOperatorCallExpr *OCE) {
1168 if (OCE->getNumArgs() == 0)
1171 dyn_cast_or_null<CXXMethodDecl>(OCE->getCalleeDecl());
1173 markPossiblyMutatedCallee(OCE->getArg(0), Method);
1174 markPossiblyMutatedArgs(llvm::ArrayRef<const Expr *>(
1175 OCE->getArgs() + 1, OCE->getNumArgs() - 1),
1178 markPossiblyMutatedArgs(
1179 llvm::ArrayRef<const Expr *>(OCE->getArgs(), OCE->getNumArgs()),
1180 OCE->getDirectCallee());
1185 bool VisitCallExpr(CallExpr *CE) {
1193 if (isa<CXXMemberCallExpr>(CE) || isa<CXXOperatorCallExpr>(CE))
1195 markPossiblyMutatedArgs(
1196 llvm::ArrayRef<const Expr *>(CE->getArgs(), CE->getNumArgs()),
1197 CE->getDirectCallee());
1201 bool VisitCXXConstructExpr(CXXConstructExpr *CCE) {
1202 markPossiblyMutatedArgs(
1203 llvm::ArrayRef<const Expr *>(CCE->getArgs(), CCE->getNumArgs()),
1204 CCE->getConstructor());
1208 bool VisitVarDecl(VarDecl *VD) {
1215 if (VD->getType()->isReferenceType() &&
1216 !VD->getType()->getPointeeType().isConstQualified() && VD->hasInit())
1217 markPossiblyMutated(VD->getInit()->IgnoreParens());
1221 bool VisitLambdaExpr(LambdaExpr *LE) {
1225 for (
const LambdaCapture &C : LE->captures())
1226 if (
C.capturesVariable() &&
C.getCaptureKind() == LCK_ByRef)
1227 markPossiblyMutated(
C.getCapturedVar());
1231 bool VisitCXXForRangeStmt(CXXForRangeStmt *FRS) {
1239 QualType LoopVarType = FRS->getLoopVariable()->getType();
1240 if ((LoopVarType->isReferenceType() &&
1241 !LoopVarType->getPointeeType().isConstQualified()) ||
1242 (LoopVarType->isPointerType() &&
1243 !LoopVarType->getPointeeType().isConstQualified()))
1244 if (
const Expr *RangeInit = FRS->getRangeInit())
1245 markPossiblyMutated(RangeInit);
1249 bool VisitReturnStmt(ReturnStmt *Return) {
1250 if (CurrentLocation == ZoneRelative::Inside) {
1251 Info.HasReturnStmt =
true;
1259 if (
const Expr *RV = Return->getRetValue())
1260 markPossiblyMutated(RV);
1265 bool VisitBreakStmt(BreakStmt *Break) {
1268 if (CurrentLocation == ZoneRelative::Inside &&
1269 !(CurNumberOfNestedLoops || CurNumberOfSwitch))
1270 Info.BrokenControlFlow =
true;
1274 bool VisitContinueStmt(ContinueStmt *Continue) {
1277 if (CurrentLocation == ZoneRelative::Inside && !CurNumberOfNestedLoops)
1278 Info.BrokenControlFlow =
true;
1281 CapturedZoneInfo
Info;
1282 const ExtractionZone &ExtZone;
1283 ZoneRelative CurrentLocation = ZoneRelative::Before;
1286 unsigned CurNumberOfNestedLoops = 0;
1287 unsigned CurNumberOfSwitch = 0;
1289 ExtractionZoneVisitor Visitor(ExtZone);
1290 CapturedZoneInfo Result = std::move(Visitor.Info);
1291 Result.AlwaysReturns = alwaysReturns(ExtZone);
1299bool createParameters(NewFunction &ExtractedFunc,
1300 const CapturedZoneInfo &CapturedInfo,
1301 const ASTContext &Context,
const LangOptions &LangOpts) {
1302 for (
const auto &KeyVal : CapturedInfo.DeclInfoMap) {
1303 const auto &DeclInfo = KeyVal.second;
1307 if (DeclInfo.DeclaredIn == ZoneRelative::Inside &&
1308 DeclInfo.IsReferencedInPostZone)
1310 if (!DeclInfo.IsReferencedInZone)
1312 if (DeclInfo.DeclaredIn == ZoneRelative::Inside ||
1313 DeclInfo.DeclaredIn == ZoneRelative::OutsideFunc)
1316 const ValueDecl *VD = dyn_cast_or_null<ValueDecl>(DeclInfo.TheDecl);
1319 if (!VD || isa<FunctionDecl>(DeclInfo.TheDecl))
1322 QualType FullTypeInfo = VD->getType();
1323 QualType TypeInfo = FullTypeInfo.getNonReferenceType();
1334 if (TypeInfo->isVariablyModifiedType() &&
1335 (LangOpts.CPlusPlus || !TypeInfo->isArrayType()))
1339 ParamPassKind Kind = ParamPassKind::Reference;
1340 if (!DeclInfo.IsPossiblyMutated) {
1341 auto WordSize = Context.getTypeSizeInChars(Context.VoidPtrTy);
1344 if (TypeInfo->isScalarType() && !TypeInfo.isVolatileQualified() &&
1345 !FullTypeInfo->isReferenceType() &&
1346 Context.getTypeSizeInChars(TypeInfo) <= 2 * WordSize) {
1347 Kind = ParamPassKind::Value;
1348 }
else if (!TypeInfo->isArrayType() && !TypeInfo->isFunctionType()) {
1357 TypeInfo.addConst();
1372 if (Kind == ParamPassKind::Reference && !LangOpts.CPlusPlus) {
1373 if (TypeInfo->isArrayType()) {
1377 if (DeclInfo.HasUnsafeTypeQueryUseInZone)
1379 QualType Decayed = Context.getArrayDecayedType(TypeInfo);
1393 if (Decayed->getPointeeType()->isVariablyModifiedType())
1396 Kind = ParamPassKind::Value;
1405 DeclInfo.ZoneOccurrences,
1406 [](
const CapturedZoneInfo::DeclInformation::Occurrence &O) {
1407 return O.Loc.isMacroID() ||
1408 (O.DotLoc && O.DotLoc->isMacroID());
1411 TypeInfo = Context.getPointerType(TypeInfo);
1412 Kind = ParamPassKind::Pointer;
1413 unsigned NameLength = VD->getName().size();
1414 for (
const auto &Occ : DeclInfo.ZoneOccurrences)
1415 ExtractedFunc.PointerRewriteSites.push_back(
1416 {Occ.Loc, NameLength, Occ.DotLoc});
1421 ExtractedFunc.Parameters.push_back(
1422 {std::string(VD->getName()), TypeInfo, Kind, DeclInfo.DeclIndex});
1424 llvm::sort(ExtractedFunc.Parameters);
1431tooling::ExtractionSemicolonPolicy
1432getSemicolonPolicy(ExtractionZone &ExtZone,
const SourceManager &SM,
1433 const LangOptions &LangOpts) {
1435 SourceRange FuncBodyRange = {ExtZone.ZoneRange.getBegin(),
1436 ExtZone.ZoneRange.getEnd().getLocWithOffset(-1)};
1437 auto SemicolonPolicy = tooling::ExtractionSemicolonPolicy::compute(
1438 ExtZone.getLastRootStmt()->ASTNode.get<Stmt>(), FuncBodyRange, SM,
1441 ExtZone.ZoneRange.setEnd(FuncBodyRange.getEnd().getLocWithOffset(1));
1442 return SemicolonPolicy;
1446bool generateReturnProperties(NewFunction &ExtractedFunc,
1447 const FunctionDecl &EnclosingFunc,
1448 const CapturedZoneInfo &CapturedInfo) {
1452 if (CapturedInfo.HasReturnStmt) {
1455 if (!CapturedInfo.AlwaysReturns)
1457 QualType Ret = EnclosingFunc.getReturnType();
1460 if (Ret->isDependentType())
1462 ExtractedFunc.ReturnType = Ret;
1466 ExtractedFunc.ReturnType = EnclosingFunc.getParentASTContext().VoidTy;
1470void captureMethodInfo(NewFunction &ExtractedFunc,
1471 const CXXMethodDecl *Method) {
1472 ExtractedFunc.Static =
Method->isStatic();
1473 ExtractedFunc.Const =
Method->isConst();
1474 ExtractedFunc.EnclosingClass =
Method->getParent();
1479llvm::Expected<NewFunction> getExtractedFunction(ExtractionZone &ExtZone,
1480 const SourceManager &SM,
1481 const LangOptions &LangOpts) {
1482 CapturedZoneInfo CapturedInfo = captureZoneInfo(ExtZone);
1484 if (CapturedInfo.BrokenControlFlow)
1485 return error(
"Cannot extract break/continue without corresponding "
1486 "loop/switch statement.");
1487 NewFunction ExtractedFunc(getSemicolonPolicy(ExtZone, SM, LangOpts),
1490 ExtractedFunc.SyntacticDC =
1491 ExtZone.EnclosingFunction->getLexicalDeclContext();
1492 ExtractedFunc.SemanticDC = ExtZone.EnclosingFunction->getDeclContext();
1493 ExtractedFunc.DefinitionQualifier = ExtZone.EnclosingFunction->getQualifier();
1494 ExtractedFunc.Constexpr = ExtZone.EnclosingFunction->getConstexprKind();
1503 ExtractedFunc.Static =
1504 ExtZone.EnclosingFunction->getCanonicalDecl()->getStorageClass() ==
1506 if (
const auto *Method =
1507 llvm::dyn_cast<CXXMethodDecl>(ExtZone.EnclosingFunction))
1508 captureMethodInfo(ExtractedFunc, Method);
1510 if (ExtZone.EnclosingFunction->isOutOfLine()) {
1513 const auto *FirstOriginalDecl =
1514 ExtZone.EnclosingFunction->getCanonicalDecl();
1518 return error(
"Declaration is inside a macro");
1519 ExtractedFunc.ForwardDeclarationPoint = DeclPos->getBegin();
1520 ExtractedFunc.ForwardDeclarationSyntacticDC = ExtractedFunc.SemanticDC;
1523 ExtractedFunc.BodyRange = ExtZone.ZoneRange;
1524 ExtractedFunc.DefinitionPoint = ExtZone.getInsertionPoint();
1526 ExtractedFunc.CallerReturnsValue = CapturedInfo.AlwaysReturns;
1527 if (!createParameters(ExtractedFunc, CapturedInfo,
1528 ExtZone.EnclosingFunction->getASTContext(), LangOpts) ||
1529 !generateReturnProperties(ExtractedFunc, *ExtZone.EnclosingFunction,
1531 return error(
"Too complex to extract.");
1532 return ExtractedFunc;
1535class ExtractFunction :
public Tweak {
1537 const char *id() const final;
1538 bool prepare(const Selection &Inputs) override;
1539 Expected<Effect> apply(const Selection &Inputs) override;
1540 std::
string title()
const override {
return "Extract to function"; }
1541 llvm::StringLiteral kind()
const override {
1542 return CodeAction::REFACTOR_KIND;
1546 ExtractionZone ExtZone;
1550tooling::Replacement replaceWithFuncCall(
const NewFunction &ExtractedFunc,
1551 const SourceManager &SM,
1552 const LangOptions &LangOpts) {
1553 std::string FuncCall = ExtractedFunc.renderCall();
1554 return tooling::Replacement(
1555 SM, CharSourceRange(ExtractedFunc.BodyRange,
false), FuncCall, LangOpts);
1558llvm::Expected<tooling::Replacement>
1559createFunctionDefinition(
const NewFunction &ExtractedFunc,
1560 const SourceManager &SM) {
1561 FunctionDeclKind DeclKind = InlineDefinition;
1562 if (ExtractedFunc.ForwardDeclarationPoint)
1563 DeclKind = OutOfLineDefinition;
1564 llvm::Expected<std::string> FunctionDef = ExtractedFunc.renderDeclaration(
1565 DeclKind, *ExtractedFunc.SemanticDC, *ExtractedFunc.SyntacticDC, SM);
1567 return FunctionDef.takeError();
1569 return tooling::Replacement(SM, ExtractedFunc.DefinitionPoint, 0,
1573llvm::Expected<tooling::Replacement>
1574createForwardDeclaration(
const NewFunction &ExtractedFunc,
1575 const SourceManager &SM) {
1576 llvm::Expected<std::string> FunctionDecl = ExtractedFunc.renderDeclaration(
1577 ForwardDeclaration, *ExtractedFunc.SemanticDC,
1578 *ExtractedFunc.ForwardDeclarationSyntacticDC, SM);
1580 return FunctionDecl.takeError();
1581 SourceLocation DeclPoint = *ExtractedFunc.ForwardDeclarationPoint;
1583 return tooling::Replacement(SM, DeclPoint, 0, *FunctionDecl);
1587bool hasReturnStmt(
const ExtractionZone &ExtZone) {
1588 class ReturnStmtVisitor
1589 :
public clang::RecursiveASTVisitor<ReturnStmtVisitor> {
1591 bool VisitReturnStmt(ReturnStmt *Return) {
1598 ReturnStmtVisitor V;
1599 for (
const Stmt *RootStmt : ExtZone.RootStmts) {
1600 V.TraverseStmt(
const_cast<Stmt *
>(RootStmt));
1607bool ExtractFunction::prepare(
const Selection &Inputs) {
1608 const LangOptions &LangOpts = Inputs.AST->getLangOpts();
1609 const Node *CommonAnc = Inputs.ASTSelection.commonAncestor();
1610 const SourceManager &SM = Inputs.AST->getSourceManager();
1611 auto MaybeExtZone = findExtractionZone(CommonAnc, SM, LangOpts);
1612 if (!MaybeExtZone ||
1613 (hasReturnStmt(*MaybeExtZone) && !alwaysReturns(*MaybeExtZone)))
1617 if (MaybeExtZone->requiresHoisting(SM, Inputs.AST->getHeuristicResolver()))
1620 ExtZone = std::move(*MaybeExtZone);
1624Expected<Tweak::Effect> ExtractFunction::apply(
const Selection &Inputs) {
1625 const SourceManager &SM = Inputs.AST->getSourceManager();
1626 const LangOptions &LangOpts = Inputs.AST->getLangOpts();
1627 auto ExtractedFunc = getExtractedFunction(ExtZone, SM, LangOpts);
1630 return ExtractedFunc.takeError();
1631 tooling::Replacements Edit;
1632 auto FuncDef = createFunctionDefinition(*ExtractedFunc, SM);
1634 return FuncDef.takeError();
1635 if (
auto Err = Edit.add(*FuncDef))
1636 return std::move(Err);
1637 if (
auto Err = Edit.add(replaceWithFuncCall(*ExtractedFunc, SM, LangOpts)))
1638 return std::move(Err);
1640 if (
auto FwdLoc = ExtractedFunc->ForwardDeclarationPoint) {
1643 if (SM.isWrittenInSameFile(ExtractedFunc->DefinitionPoint, *FwdLoc)) {
1644 auto FwdDecl = createForwardDeclaration(*ExtractedFunc, SM);
1646 return FwdDecl.takeError();
1647 if (
auto Err = Edit.add(*FwdDecl))
1648 return std::move(Err);
1650 auto MultiFileEffect = Effect::mainFileEdit(SM, std::move(Edit));
1651 if (!MultiFileEffect)
1652 return MultiFileEffect.takeError();
1654 auto FwdDecl = createForwardDeclaration(*ExtractedFunc, SM);
1656 return FwdDecl.takeError();
1657 tooling::Replacements OtherEdit(*FwdDecl);
1658 if (
auto PathAndEdit =
1659 Tweak::Effect::fileEdit(SM, SM.getFileID(*FwdLoc), OtherEdit))
1660 MultiFileEffect->ApplyEdits.try_emplace(PathAndEdit->first,
1661 PathAndEdit->second);
1663 return PathAndEdit.takeError();
1664 return MultiFileEffect;
1667 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++ -*-===//