1 //===--- ExtractFunction.cpp -------------------------------------*- C++-*-===// 2 // 3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4 // See https://llvm.org/LICENSE.txt for license information. 5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6 // 7 //===----------------------------------------------------------------------===// 8 // 9 // Extracts statements to a new function and replaces the statements with a 10 // call to the new function. 11 // Before: 12 // void f(int a) { 13 // [[if(a < 5) 14 // a = 5;]] 15 // } 16 // After: 17 // void extracted(int &a) { 18 // if(a < 5) 19 // a = 5; 20 // } 21 // void f(int a) { 22 // extracted(a); 23 // } 24 // 25 // - Only extract statements 26 // - Extracts from non-templated free functions only. 27 // - Parameters are const only if the declaration was const 28 // - Always passed by l-value reference 29 // - Void return type 30 // - Cannot extract declarations that will be needed in the original function 31 // after extraction. 32 // - Checks for broken control flow (break/continue without loop/switch) 33 // 34 // 1. ExtractFunction is the tweak subclass 35 // - Prepare does basic analysis of the selection and is therefore fast. 36 // Successful prepare doesn't always mean we can apply the tweak. 37 // - Apply does a more detailed analysis and can be slower. In case of 38 // failure, we let the user know that we are unable to perform extraction. 39 // 2. ExtractionZone store information about the range being extracted and the 40 // enclosing function. 41 // 3. NewFunction stores properties of the extracted function and provides 42 // methods for rendering it. 43 // 4. CapturedZoneInfo uses a RecursiveASTVisitor to capture information about 44 // the extraction like declarations, existing return statements, etc. 45 // 5. getExtractedFunction is responsible for analyzing the CapturedZoneInfo and 46 // creating a NewFunction. 47 //===----------------------------------------------------------------------===// 48 49 #include "AST.h" 50 #include "FindTarget.h" 51 #include "ParsedAST.h" 52 #include "Selection.h" 53 #include "SourceCode.h" 54 #include "refactor/Tweak.h" 55 #include "support/Logger.h" 56 #include "clang/AST/ASTContext.h" 57 #include "clang/AST/Decl.h" 58 #include "clang/AST/DeclBase.h" 59 #include "clang/AST/NestedNameSpecifier.h" 60 #include "clang/AST/RecursiveASTVisitor.h" 61 #include "clang/AST/Stmt.h" 62 #include "clang/Basic/LangOptions.h" 63 #include "clang/Basic/SourceLocation.h" 64 #include "clang/Basic/SourceManager.h" 65 #include "clang/Tooling/Core/Replacement.h" 66 #include "clang/Tooling/Refactoring/Extract/SourceExtraction.h" 67 #include "llvm/ADT/None.h" 68 #include "llvm/ADT/Optional.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" 75 #include "llvm/Support/raw_os_ostream.h" 76 77 namespace clang { 78 namespace clangd { 79 namespace { 80 81 using Node = SelectionTree::Node; 82 83 // ExtractionZone is the part of code that is being extracted. 84 // EnclosingFunction is the function/method inside which the zone lies. 85 // We split the file into 4 parts relative to extraction zone. 86 enum class ZoneRelative { 87 Before, // Before Zone and inside EnclosingFunction. 88 Inside, // Inside Zone. 89 After, // After Zone and inside EnclosingFunction. 90 OutsideFunc // Outside EnclosingFunction. 91 }; 92 93 enum FunctionDeclKind { 94 InlineDefinition, 95 ForwardDeclaration, 96 OutOfLineDefinition 97 }; 98 99 // A RootStmt is a statement that's fully selected including all it's children 100 // and it's parent is unselected. 101 // Check if a node is a root statement. 102 bool isRootStmt(const Node *N) { 103 if (!N->ASTNode.get<Stmt>()) 104 return false; 105 // Root statement cannot be partially selected. 106 if (N->Selected == SelectionTree::Partial) 107 return false; 108 // Only DeclStmt can be an unselected RootStmt since VarDecls claim the entire 109 // selection range in selectionTree. 110 if (N->Selected == SelectionTree::Unselected && !N->ASTNode.get<DeclStmt>()) 111 return false; 112 return true; 113 } 114 115 // Returns the (unselected) parent of all RootStmts given the commonAncestor. 116 // Returns null if: 117 // 1. any node is partially selected 118 // 2. If all completely selected nodes don't have the same common parent 119 // 3. Any child of Parent isn't a RootStmt. 120 // Returns null if any child is not a RootStmt. 121 // We only support extraction of RootStmts since it allows us to extract without 122 // having to change the selection range. Also, this means that any scope that 123 // begins in selection range, ends in selection range and any scope that begins 124 // outside the selection range, ends outside as well. 125 const Node *getParentOfRootStmts(const Node *CommonAnc) { 126 if (!CommonAnc) 127 return nullptr; 128 const Node *Parent = nullptr; 129 switch (CommonAnc->Selected) { 130 case SelectionTree::Selection::Unselected: 131 // Typically a block, with the { and } unselected, could also be ForStmt etc 132 // Ensure all Children are RootStmts. 133 Parent = CommonAnc; 134 break; 135 case SelectionTree::Selection::Partial: 136 // Only a fully-selected single statement can be selected. 137 return nullptr; 138 case SelectionTree::Selection::Complete: 139 // If the Common Ancestor is completely selected, then it's a root statement 140 // and its parent will be unselected. 141 Parent = CommonAnc->Parent; 142 // If parent is a DeclStmt, even though it's unselected, we consider it a 143 // root statement and return its parent. This is done because the VarDecls 144 // claim the entire selection range of the Declaration and DeclStmt is 145 // always unselected. 146 if (Parent->ASTNode.get<DeclStmt>()) 147 Parent = Parent->Parent; 148 break; 149 } 150 // Ensure all Children are RootStmts. 151 return llvm::all_of(Parent->Children, isRootStmt) ? Parent : nullptr; 152 } 153 154 // The ExtractionZone class forms a view of the code wrt Zone. 155 struct ExtractionZone { 156 // Parent of RootStatements being extracted. 157 const Node *Parent = nullptr; 158 // The half-open file range of the code being extracted. 159 SourceRange ZoneRange; 160 // The function inside which our zone resides. 161 const FunctionDecl *EnclosingFunction = nullptr; 162 // The half-open file range of the enclosing function. 163 SourceRange EnclosingFuncRange; 164 // Set of statements that form the ExtractionZone. 165 llvm::DenseSet<const Stmt *> RootStmts; 166 167 SourceLocation getInsertionPoint() const { 168 return EnclosingFuncRange.getBegin(); 169 } 170 bool isRootStmt(const Stmt *S) const; 171 // The last root statement is important to decide where we need to insert a 172 // semicolon after the extraction. 173 const Node *getLastRootStmt() const { return Parent->Children.back(); } 174 175 // Checks if declarations inside extraction zone are accessed afterwards. 176 // 177 // This performs a partial AST traversal proportional to the size of the 178 // enclosing function, so it is possibly expensive. 179 bool requiresHoisting(const SourceManager &SM, 180 const HeuristicResolver *Resolver) const { 181 // First find all the declarations that happened inside extraction zone. 182 llvm::SmallSet<const Decl *, 1> DeclsInExtZone; 183 for (auto *RootStmt : RootStmts) { 184 findExplicitReferences( 185 RootStmt, 186 [&DeclsInExtZone](const ReferenceLoc &Loc) { 187 if (!Loc.IsDecl) 188 return; 189 DeclsInExtZone.insert(Loc.Targets.front()); 190 }, 191 Resolver); 192 } 193 // Early exit without performing expensive traversal below. 194 if (DeclsInExtZone.empty()) 195 return false; 196 // Then make sure they are not used outside the zone. 197 for (const auto *S : EnclosingFunction->getBody()->children()) { 198 if (SM.isBeforeInTranslationUnit(S->getSourceRange().getEnd(), 199 ZoneRange.getEnd())) 200 continue; 201 bool HasPostUse = false; 202 findExplicitReferences( 203 S, 204 [&](const ReferenceLoc &Loc) { 205 if (HasPostUse || 206 SM.isBeforeInTranslationUnit(Loc.NameLoc, ZoneRange.getEnd())) 207 return; 208 HasPostUse = llvm::any_of(Loc.Targets, 209 [&DeclsInExtZone](const Decl *Target) { 210 return DeclsInExtZone.contains(Target); 211 }); 212 }, 213 Resolver); 214 if (HasPostUse) 215 return true; 216 } 217 return false; 218 } 219 }; 220 221 // Whether the code in the extraction zone is guaranteed to return, assuming 222 // no broken control flow (unbound break/continue). 223 // This is a very naive check (does it end with a return stmt). 224 // Doing some rudimentary control flow analysis would cover more cases. 225 bool alwaysReturns(const ExtractionZone &EZ) { 226 const Stmt *Last = EZ.getLastRootStmt()->ASTNode.get<Stmt>(); 227 // Unwrap enclosing (unconditional) compound statement. 228 while (const auto *CS = llvm::dyn_cast<CompoundStmt>(Last)) { 229 if (CS->body_empty()) 230 return false; 231 Last = CS->body_back(); 232 } 233 return llvm::isa<ReturnStmt>(Last); 234 } 235 236 bool ExtractionZone::isRootStmt(const Stmt *S) const { 237 return RootStmts.contains(S); 238 } 239 240 // Finds the function in which the zone lies. 241 const FunctionDecl *findEnclosingFunction(const Node *CommonAnc) { 242 // Walk up the SelectionTree until we find a function Decl 243 for (const Node *CurNode = CommonAnc; CurNode; CurNode = CurNode->Parent) { 244 // Don't extract from lambdas 245 if (CurNode->ASTNode.get<LambdaExpr>()) 246 return nullptr; 247 if (const FunctionDecl *Func = CurNode->ASTNode.get<FunctionDecl>()) { 248 // FIXME: Support extraction from templated functions. 249 if (Func->isTemplated()) 250 return nullptr; 251 return Func; 252 } 253 } 254 return nullptr; 255 } 256 257 // Zone Range is the union of SourceRanges of all child Nodes in Parent since 258 // all child Nodes are RootStmts 259 llvm::Optional<SourceRange> findZoneRange(const Node *Parent, 260 const SourceManager &SM, 261 const LangOptions &LangOpts) { 262 SourceRange SR; 263 if (auto BeginFileRange = toHalfOpenFileRange( 264 SM, LangOpts, Parent->Children.front()->ASTNode.getSourceRange())) 265 SR.setBegin(BeginFileRange->getBegin()); 266 else 267 return llvm::None; 268 if (auto EndFileRange = toHalfOpenFileRange( 269 SM, LangOpts, Parent->Children.back()->ASTNode.getSourceRange())) 270 SR.setEnd(EndFileRange->getEnd()); 271 else 272 return llvm::None; 273 return SR; 274 } 275 276 // Compute the range spanned by the enclosing function. 277 // FIXME: check if EnclosingFunction has any attributes as the AST doesn't 278 // always store the source range of the attributes and thus we end up extracting 279 // between the attributes and the EnclosingFunction. 280 llvm::Optional<SourceRange> 281 computeEnclosingFuncRange(const FunctionDecl *EnclosingFunction, 282 const SourceManager &SM, 283 const LangOptions &LangOpts) { 284 return toHalfOpenFileRange(SM, LangOpts, EnclosingFunction->getSourceRange()); 285 } 286 287 // returns true if Child can be a single RootStmt being extracted from 288 // EnclosingFunc. 289 bool validSingleChild(const Node *Child, const FunctionDecl *EnclosingFunc) { 290 // Don't extract expressions. 291 // FIXME: We should extract expressions that are "statements" i.e. not 292 // subexpressions 293 if (Child->ASTNode.get<Expr>()) 294 return false; 295 // Extracting the body of EnclosingFunc would remove it's definition. 296 assert(EnclosingFunc->hasBody() && 297 "We should always be extracting from a function body."); 298 if (Child->ASTNode.get<Stmt>() == EnclosingFunc->getBody()) 299 return false; 300 return true; 301 } 302 303 // FIXME: Check we're not extracting from the initializer/condition of a control 304 // flow structure. 305 llvm::Optional<ExtractionZone> findExtractionZone(const Node *CommonAnc, 306 const SourceManager &SM, 307 const LangOptions &LangOpts) { 308 ExtractionZone ExtZone; 309 ExtZone.Parent = getParentOfRootStmts(CommonAnc); 310 if (!ExtZone.Parent || ExtZone.Parent->Children.empty()) 311 return llvm::None; 312 ExtZone.EnclosingFunction = findEnclosingFunction(ExtZone.Parent); 313 if (!ExtZone.EnclosingFunction) 314 return llvm::None; 315 // When there is a single RootStmt, we must check if it's valid for 316 // extraction. 317 if (ExtZone.Parent->Children.size() == 1 && 318 !validSingleChild(ExtZone.getLastRootStmt(), ExtZone.EnclosingFunction)) 319 return llvm::None; 320 if (auto FuncRange = 321 computeEnclosingFuncRange(ExtZone.EnclosingFunction, SM, LangOpts)) 322 ExtZone.EnclosingFuncRange = *FuncRange; 323 if (auto ZoneRange = findZoneRange(ExtZone.Parent, SM, LangOpts)) 324 ExtZone.ZoneRange = *ZoneRange; 325 if (ExtZone.EnclosingFuncRange.isInvalid() || ExtZone.ZoneRange.isInvalid()) 326 return llvm::None; 327 328 for (const Node *Child : ExtZone.Parent->Children) 329 ExtZone.RootStmts.insert(Child->ASTNode.get<Stmt>()); 330 331 return ExtZone; 332 } 333 334 // Stores information about the extracted function and provides methods for 335 // rendering it. 336 struct NewFunction { 337 struct Parameter { 338 std::string Name; 339 QualType TypeInfo; 340 bool PassByReference; 341 unsigned OrderPriority; // Lower value parameters are preferred first. 342 std::string render(const DeclContext *Context) const; 343 bool operator<(const Parameter &Other) const { 344 return OrderPriority < Other.OrderPriority; 345 } 346 }; 347 std::string Name = "extracted"; 348 QualType ReturnType; 349 std::vector<Parameter> Parameters; 350 SourceRange BodyRange; 351 SourceLocation DefinitionPoint; 352 llvm::Optional<SourceLocation> ForwardDeclarationPoint; 353 const CXXRecordDecl *EnclosingClass = nullptr; 354 const NestedNameSpecifier *DefinitionQualifier = nullptr; 355 const DeclContext *SemanticDC = nullptr; 356 const DeclContext *SyntacticDC = nullptr; 357 const DeclContext *ForwardDeclarationSyntacticDC = nullptr; 358 bool CallerReturnsValue = false; 359 bool Static = false; 360 ConstexprSpecKind Constexpr = ConstexprSpecKind::Unspecified; 361 bool Const = false; 362 363 // Decides whether the extracted function body and the function call need a 364 // semicolon after extraction. 365 tooling::ExtractionSemicolonPolicy SemicolonPolicy; 366 const LangOptions *LangOpts; 367 NewFunction(tooling::ExtractionSemicolonPolicy SemicolonPolicy, 368 const LangOptions *LangOpts) 369 : SemicolonPolicy(SemicolonPolicy), LangOpts(LangOpts) {} 370 // Render the call for this function. 371 std::string renderCall() const; 372 // Render the definition for this function. 373 std::string renderDeclaration(FunctionDeclKind K, 374 const DeclContext &SemanticDC, 375 const DeclContext &SyntacticDC, 376 const SourceManager &SM) const; 377 378 private: 379 std::string 380 renderParametersForDeclaration(const DeclContext &Enclosing) const; 381 std::string renderParametersForCall() const; 382 std::string renderSpecifiers(FunctionDeclKind K) const; 383 std::string renderQualifiers() const; 384 std::string renderDeclarationName(FunctionDeclKind K) const; 385 // Generate the function body. 386 std::string getFuncBody(const SourceManager &SM) const; 387 }; 388 389 std::string NewFunction::renderParametersForDeclaration( 390 const DeclContext &Enclosing) const { 391 std::string Result; 392 bool NeedCommaBefore = false; 393 for (const Parameter &P : Parameters) { 394 if (NeedCommaBefore) 395 Result += ", "; 396 NeedCommaBefore = true; 397 Result += P.render(&Enclosing); 398 } 399 return Result; 400 } 401 402 std::string NewFunction::renderParametersForCall() const { 403 std::string Result; 404 bool NeedCommaBefore = false; 405 for (const Parameter &P : Parameters) { 406 if (NeedCommaBefore) 407 Result += ", "; 408 NeedCommaBefore = true; 409 Result += P.Name; 410 } 411 return Result; 412 } 413 414 std::string NewFunction::renderSpecifiers(FunctionDeclKind K) const { 415 std::string Attributes; 416 417 if (Static && K != FunctionDeclKind::OutOfLineDefinition) { 418 Attributes += "static "; 419 } 420 421 switch (Constexpr) { 422 case ConstexprSpecKind::Unspecified: 423 case ConstexprSpecKind::Constinit: 424 break; 425 case ConstexprSpecKind::Constexpr: 426 Attributes += "constexpr "; 427 break; 428 case ConstexprSpecKind::Consteval: 429 Attributes += "consteval "; 430 break; 431 } 432 433 return Attributes; 434 } 435 436 std::string NewFunction::renderQualifiers() const { 437 std::string Attributes; 438 439 if (Const) { 440 Attributes += " const"; 441 } 442 443 return Attributes; 444 } 445 446 std::string NewFunction::renderDeclarationName(FunctionDeclKind K) const { 447 if (DefinitionQualifier == nullptr || K != OutOfLineDefinition) { 448 return Name; 449 } 450 451 std::string QualifierName; 452 llvm::raw_string_ostream Oss(QualifierName); 453 DefinitionQualifier->print(Oss, *LangOpts); 454 return llvm::formatv("{0}{1}", QualifierName, Name); 455 } 456 457 std::string NewFunction::renderCall() const { 458 return std::string( 459 llvm::formatv("{0}{1}({2}){3}", CallerReturnsValue ? "return " : "", Name, 460 renderParametersForCall(), 461 (SemicolonPolicy.isNeededInOriginalFunction() ? ";" : ""))); 462 } 463 464 std::string NewFunction::renderDeclaration(FunctionDeclKind K, 465 const DeclContext &SemanticDC, 466 const DeclContext &SyntacticDC, 467 const SourceManager &SM) const { 468 std::string Declaration = std::string(llvm::formatv( 469 "{0}{1} {2}({3}){4}", renderSpecifiers(K), 470 printType(ReturnType, SyntacticDC), renderDeclarationName(K), 471 renderParametersForDeclaration(SemanticDC), renderQualifiers())); 472 473 switch (K) { 474 case ForwardDeclaration: 475 return std::string(llvm::formatv("{0};\n", Declaration)); 476 case OutOfLineDefinition: 477 case InlineDefinition: 478 return std::string( 479 llvm::formatv("{0} {\n{1}\n}\n", Declaration, getFuncBody(SM))); 480 break; 481 } 482 } 483 484 std::string NewFunction::getFuncBody(const SourceManager &SM) const { 485 // FIXME: Generate tooling::Replacements instead of std::string to 486 // - hoist decls 487 // - add return statement 488 // - Add semicolon 489 return toSourceCode(SM, BodyRange).str() + 490 (SemicolonPolicy.isNeededInExtractedFunction() ? ";" : ""); 491 } 492 493 std::string NewFunction::Parameter::render(const DeclContext *Context) const { 494 return printType(TypeInfo, *Context) + (PassByReference ? " &" : " ") + Name; 495 } 496 497 // Stores captured information about Extraction Zone. 498 struct CapturedZoneInfo { 499 struct DeclInformation { 500 const Decl *TheDecl; 501 ZoneRelative DeclaredIn; 502 // index of the declaration or first reference. 503 unsigned DeclIndex; 504 bool IsReferencedInZone = false; 505 bool IsReferencedInPostZone = false; 506 // FIXME: Capture mutation information 507 DeclInformation(const Decl *TheDecl, ZoneRelative DeclaredIn, 508 unsigned DeclIndex) 509 : TheDecl(TheDecl), DeclaredIn(DeclaredIn), DeclIndex(DeclIndex){}; 510 // Marks the occurence of a reference for this declaration 511 void markOccurence(ZoneRelative ReferenceLoc); 512 }; 513 // Maps Decls to their DeclInfo 514 llvm::DenseMap<const Decl *, DeclInformation> DeclInfoMap; 515 bool HasReturnStmt = false; // Are there any return statements in the zone? 516 bool AlwaysReturns = false; // Does the zone always return? 517 // Control flow is broken if we are extracting a break/continue without a 518 // corresponding parent loop/switch 519 bool BrokenControlFlow = false; 520 // FIXME: capture TypeAliasDecl and UsingDirectiveDecl 521 // FIXME: Capture type information as well. 522 DeclInformation *createDeclInfo(const Decl *D, ZoneRelative RelativeLoc); 523 DeclInformation *getDeclInfoFor(const Decl *D); 524 }; 525 526 CapturedZoneInfo::DeclInformation * 527 CapturedZoneInfo::createDeclInfo(const Decl *D, ZoneRelative RelativeLoc) { 528 // The new Decl's index is the size of the map so far. 529 auto InsertionResult = DeclInfoMap.insert( 530 {D, DeclInformation(D, RelativeLoc, DeclInfoMap.size())}); 531 // Return the newly created DeclInfo 532 return &InsertionResult.first->second; 533 } 534 535 CapturedZoneInfo::DeclInformation * 536 CapturedZoneInfo::getDeclInfoFor(const Decl *D) { 537 // If the Decl doesn't exist, we 538 auto Iter = DeclInfoMap.find(D); 539 if (Iter == DeclInfoMap.end()) 540 return nullptr; 541 return &Iter->second; 542 } 543 544 void CapturedZoneInfo::DeclInformation::markOccurence( 545 ZoneRelative ReferenceLoc) { 546 switch (ReferenceLoc) { 547 case ZoneRelative::Inside: 548 IsReferencedInZone = true; 549 break; 550 case ZoneRelative::After: 551 IsReferencedInPostZone = true; 552 break; 553 default: 554 break; 555 } 556 } 557 558 bool isLoop(const Stmt *S) { 559 return isa<ForStmt>(S) || isa<DoStmt>(S) || isa<WhileStmt>(S) || 560 isa<CXXForRangeStmt>(S); 561 } 562 563 // Captures information from Extraction Zone 564 CapturedZoneInfo captureZoneInfo(const ExtractionZone &ExtZone) { 565 // We use the ASTVisitor instead of using the selection tree since we need to 566 // find references in the PostZone as well. 567 // FIXME: Check which statements we don't allow to extract. 568 class ExtractionZoneVisitor 569 : public clang::RecursiveASTVisitor<ExtractionZoneVisitor> { 570 public: 571 ExtractionZoneVisitor(const ExtractionZone &ExtZone) : ExtZone(ExtZone) { 572 TraverseDecl(const_cast<FunctionDecl *>(ExtZone.EnclosingFunction)); 573 } 574 575 bool TraverseStmt(Stmt *S) { 576 if (!S) 577 return true; 578 bool IsRootStmt = ExtZone.isRootStmt(const_cast<const Stmt *>(S)); 579 // If we are starting traversal of a RootStmt, we are somewhere inside 580 // ExtractionZone 581 if (IsRootStmt) 582 CurrentLocation = ZoneRelative::Inside; 583 addToLoopSwitchCounters(S, 1); 584 // Traverse using base class's TraverseStmt 585 RecursiveASTVisitor::TraverseStmt(S); 586 addToLoopSwitchCounters(S, -1); 587 // We set the current location as after since next stmt will either be a 588 // RootStmt (handled at the beginning) or after extractionZone 589 if (IsRootStmt) 590 CurrentLocation = ZoneRelative::After; 591 return true; 592 } 593 594 // Add Increment to CurNumberOf{Loops,Switch} if statement is 595 // {Loop,Switch} and inside Extraction Zone. 596 void addToLoopSwitchCounters(Stmt *S, int Increment) { 597 if (CurrentLocation != ZoneRelative::Inside) 598 return; 599 if (isLoop(S)) 600 CurNumberOfNestedLoops += Increment; 601 else if (isa<SwitchStmt>(S)) 602 CurNumberOfSwitch += Increment; 603 } 604 605 bool VisitDecl(Decl *D) { 606 Info.createDeclInfo(D, CurrentLocation); 607 return true; 608 } 609 610 bool VisitDeclRefExpr(DeclRefExpr *DRE) { 611 // Find the corresponding Decl and mark it's occurrence. 612 const Decl *D = DRE->getDecl(); 613 auto *DeclInfo = Info.getDeclInfoFor(D); 614 // If no Decl was found, the Decl must be outside the enclosingFunc. 615 if (!DeclInfo) 616 DeclInfo = Info.createDeclInfo(D, ZoneRelative::OutsideFunc); 617 DeclInfo->markOccurence(CurrentLocation); 618 // FIXME: check if reference mutates the Decl being referred. 619 return true; 620 } 621 622 bool VisitReturnStmt(ReturnStmt *Return) { 623 if (CurrentLocation == ZoneRelative::Inside) 624 Info.HasReturnStmt = true; 625 return true; 626 } 627 628 bool VisitBreakStmt(BreakStmt *Break) { 629 // Control flow is broken if break statement is selected without any 630 // parent loop or switch statement. 631 if (CurrentLocation == ZoneRelative::Inside && 632 !(CurNumberOfNestedLoops || CurNumberOfSwitch)) 633 Info.BrokenControlFlow = true; 634 return true; 635 } 636 637 bool VisitContinueStmt(ContinueStmt *Continue) { 638 // Control flow is broken if Continue statement is selected without any 639 // parent loop 640 if (CurrentLocation == ZoneRelative::Inside && !CurNumberOfNestedLoops) 641 Info.BrokenControlFlow = true; 642 return true; 643 } 644 CapturedZoneInfo Info; 645 const ExtractionZone &ExtZone; 646 ZoneRelative CurrentLocation = ZoneRelative::Before; 647 // Number of {loop,switch} statements that are currently in the traversal 648 // stack inside Extraction Zone. Used to check for broken control flow. 649 unsigned CurNumberOfNestedLoops = 0; 650 unsigned CurNumberOfSwitch = 0; 651 }; 652 ExtractionZoneVisitor Visitor(ExtZone); 653 CapturedZoneInfo Result = std::move(Visitor.Info); 654 Result.AlwaysReturns = alwaysReturns(ExtZone); 655 return Result; 656 } 657 658 // Adds parameters to ExtractedFunc. 659 // Returns true if able to find the parameters successfully and no hoisting 660 // needed. 661 // FIXME: Check if the declaration has a local/anonymous type 662 bool createParameters(NewFunction &ExtractedFunc, 663 const CapturedZoneInfo &CapturedInfo) { 664 for (const auto &KeyVal : CapturedInfo.DeclInfoMap) { 665 const auto &DeclInfo = KeyVal.second; 666 // If a Decl was Declared in zone and referenced in post zone, it 667 // needs to be hoisted (we bail out in that case). 668 // FIXME: Support Decl Hoisting. 669 if (DeclInfo.DeclaredIn == ZoneRelative::Inside && 670 DeclInfo.IsReferencedInPostZone) 671 return false; 672 if (!DeclInfo.IsReferencedInZone) 673 continue; // no need to pass as parameter, not referenced 674 if (DeclInfo.DeclaredIn == ZoneRelative::Inside || 675 DeclInfo.DeclaredIn == ZoneRelative::OutsideFunc) 676 continue; // no need to pass as parameter, still accessible. 677 // Parameter specific checks. 678 const ValueDecl *VD = dyn_cast_or_null<ValueDecl>(DeclInfo.TheDecl); 679 // Can't parameterise if the Decl isn't a ValueDecl or is a FunctionDecl 680 // (this includes the case of recursive call to EnclosingFunc in Zone). 681 if (!VD || isa<FunctionDecl>(DeclInfo.TheDecl)) 682 return false; 683 // Parameter qualifiers are same as the Decl's qualifiers. 684 QualType TypeInfo = VD->getType().getNonReferenceType(); 685 // FIXME: Need better qualifier checks: check mutated status for 686 // Decl(e.g. was it assigned, passed as nonconst argument, etc) 687 // FIXME: check if parameter will be a non l-value reference. 688 // FIXME: We don't want to always pass variables of types like int, 689 // pointers, etc by reference. 690 bool IsPassedByReference = true; 691 // We use the index of declaration as the ordering priority for parameters. 692 ExtractedFunc.Parameters.push_back({std::string(VD->getName()), TypeInfo, 693 IsPassedByReference, 694 DeclInfo.DeclIndex}); 695 } 696 llvm::sort(ExtractedFunc.Parameters); 697 return true; 698 } 699 700 // Clangd uses open ranges while ExtractionSemicolonPolicy (in Clang Tooling) 701 // uses closed ranges. Generates the semicolon policy for the extraction and 702 // extends the ZoneRange if necessary. 703 tooling::ExtractionSemicolonPolicy 704 getSemicolonPolicy(ExtractionZone &ExtZone, const SourceManager &SM, 705 const LangOptions &LangOpts) { 706 // Get closed ZoneRange. 707 SourceRange FuncBodyRange = {ExtZone.ZoneRange.getBegin(), 708 ExtZone.ZoneRange.getEnd().getLocWithOffset(-1)}; 709 auto SemicolonPolicy = tooling::ExtractionSemicolonPolicy::compute( 710 ExtZone.getLastRootStmt()->ASTNode.get<Stmt>(), FuncBodyRange, SM, 711 LangOpts); 712 // Update ZoneRange. 713 ExtZone.ZoneRange.setEnd(FuncBodyRange.getEnd().getLocWithOffset(1)); 714 return SemicolonPolicy; 715 } 716 717 // Generate return type for ExtractedFunc. Return false if unable to do so. 718 bool generateReturnProperties(NewFunction &ExtractedFunc, 719 const FunctionDecl &EnclosingFunc, 720 const CapturedZoneInfo &CapturedInfo) { 721 // If the selected code always returns, we preserve those return statements. 722 // The return type should be the same as the enclosing function. 723 // (Others are possible if there are conversions, but this seems clearest). 724 if (CapturedInfo.HasReturnStmt) { 725 // If the return is conditional, neither replacing the code with 726 // `extracted()` nor `return extracted()` is correct. 727 if (!CapturedInfo.AlwaysReturns) 728 return false; 729 QualType Ret = EnclosingFunc.getReturnType(); 730 // Once we support members, it'd be nice to support e.g. extracting a method 731 // of Foo<T> that returns T. But it's not clear when that's safe. 732 if (Ret->isDependentType()) 733 return false; 734 ExtractedFunc.ReturnType = Ret; 735 return true; 736 } 737 // FIXME: Generate new return statement if needed. 738 ExtractedFunc.ReturnType = EnclosingFunc.getParentASTContext().VoidTy; 739 return true; 740 } 741 742 void captureMethodInfo(NewFunction &ExtractedFunc, 743 const CXXMethodDecl *Method) { 744 ExtractedFunc.Static = Method->isStatic(); 745 ExtractedFunc.Const = Method->isConst(); 746 ExtractedFunc.EnclosingClass = Method->getParent(); 747 } 748 749 // FIXME: add support for adding other function return types besides void. 750 // FIXME: assign the value returned by non void extracted function. 751 llvm::Expected<NewFunction> getExtractedFunction(ExtractionZone &ExtZone, 752 const SourceManager &SM, 753 const LangOptions &LangOpts) { 754 CapturedZoneInfo CapturedInfo = captureZoneInfo(ExtZone); 755 // Bail out if any break of continue exists 756 if (CapturedInfo.BrokenControlFlow) 757 return error("Cannot extract break/continue without corresponding " 758 "loop/switch statement."); 759 NewFunction ExtractedFunc(getSemicolonPolicy(ExtZone, SM, LangOpts), 760 &LangOpts); 761 762 ExtractedFunc.SyntacticDC = 763 ExtZone.EnclosingFunction->getLexicalDeclContext(); 764 ExtractedFunc.SemanticDC = ExtZone.EnclosingFunction->getDeclContext(); 765 ExtractedFunc.DefinitionQualifier = ExtZone.EnclosingFunction->getQualifier(); 766 ExtractedFunc.Constexpr = ExtZone.EnclosingFunction->getConstexprKind(); 767 768 if (const auto *Method = 769 llvm::dyn_cast<CXXMethodDecl>(ExtZone.EnclosingFunction)) 770 captureMethodInfo(ExtractedFunc, Method); 771 772 if (ExtZone.EnclosingFunction->isOutOfLine()) { 773 // FIXME: Put the extracted method in a private section if it's a class or 774 // maybe in an anonymous namespace 775 const auto *FirstOriginalDecl = 776 ExtZone.EnclosingFunction->getCanonicalDecl(); 777 auto DeclPos = 778 toHalfOpenFileRange(SM, LangOpts, FirstOriginalDecl->getSourceRange()); 779 if (!DeclPos) 780 return error("Declaration is inside a macro"); 781 ExtractedFunc.ForwardDeclarationPoint = DeclPos.getValue().getBegin(); 782 ExtractedFunc.ForwardDeclarationSyntacticDC = ExtractedFunc.SemanticDC; 783 } 784 785 ExtractedFunc.BodyRange = ExtZone.ZoneRange; 786 ExtractedFunc.DefinitionPoint = ExtZone.getInsertionPoint(); 787 788 ExtractedFunc.CallerReturnsValue = CapturedInfo.AlwaysReturns; 789 if (!createParameters(ExtractedFunc, CapturedInfo) || 790 !generateReturnProperties(ExtractedFunc, *ExtZone.EnclosingFunction, 791 CapturedInfo)) 792 return error("Too complex to extract."); 793 return ExtractedFunc; 794 } 795 796 class ExtractFunction : public Tweak { 797 public: 798 const char *id() const override final; 799 bool prepare(const Selection &Inputs) override; 800 Expected<Effect> apply(const Selection &Inputs) override; 801 std::string title() const override { return "Extract to function"; } 802 llvm::StringLiteral kind() const override { 803 return CodeAction::REFACTOR_KIND; 804 } 805 806 private: 807 ExtractionZone ExtZone; 808 }; 809 810 REGISTER_TWEAK(ExtractFunction) 811 tooling::Replacement replaceWithFuncCall(const NewFunction &ExtractedFunc, 812 const SourceManager &SM, 813 const LangOptions &LangOpts) { 814 std::string FuncCall = ExtractedFunc.renderCall(); 815 return tooling::Replacement( 816 SM, CharSourceRange(ExtractedFunc.BodyRange, false), FuncCall, LangOpts); 817 } 818 819 tooling::Replacement createFunctionDefinition(const NewFunction &ExtractedFunc, 820 const SourceManager &SM) { 821 FunctionDeclKind DeclKind = InlineDefinition; 822 if (ExtractedFunc.ForwardDeclarationPoint.hasValue()) 823 DeclKind = OutOfLineDefinition; 824 std::string FunctionDef = ExtractedFunc.renderDeclaration( 825 DeclKind, *ExtractedFunc.SemanticDC, *ExtractedFunc.SyntacticDC, SM); 826 827 return tooling::Replacement(SM, ExtractedFunc.DefinitionPoint, 0, 828 FunctionDef); 829 } 830 831 tooling::Replacement createForwardDeclaration(const NewFunction &ExtractedFunc, 832 const SourceManager &SM) { 833 std::string FunctionDecl = ExtractedFunc.renderDeclaration( 834 ForwardDeclaration, *ExtractedFunc.SemanticDC, 835 *ExtractedFunc.ForwardDeclarationSyntacticDC, SM); 836 SourceLocation DeclPoint = ExtractedFunc.ForwardDeclarationPoint.getValue(); 837 838 return tooling::Replacement(SM, DeclPoint, 0, FunctionDecl); 839 } 840 841 // Returns true if ExtZone contains any ReturnStmts. 842 bool hasReturnStmt(const ExtractionZone &ExtZone) { 843 class ReturnStmtVisitor 844 : public clang::RecursiveASTVisitor<ReturnStmtVisitor> { 845 public: 846 bool VisitReturnStmt(ReturnStmt *Return) { 847 Found = true; 848 return false; // We found the answer, abort the scan. 849 } 850 bool Found = false; 851 }; 852 853 ReturnStmtVisitor V; 854 for (const Stmt *RootStmt : ExtZone.RootStmts) { 855 V.TraverseStmt(const_cast<Stmt *>(RootStmt)); 856 if (V.Found) 857 break; 858 } 859 return V.Found; 860 } 861 862 bool ExtractFunction::prepare(const Selection &Inputs) { 863 const LangOptions &LangOpts = Inputs.AST->getLangOpts(); 864 if (!LangOpts.CPlusPlus) 865 return false; 866 const Node *CommonAnc = Inputs.ASTSelection.commonAncestor(); 867 const SourceManager &SM = Inputs.AST->getSourceManager(); 868 auto MaybeExtZone = findExtractionZone(CommonAnc, SM, LangOpts); 869 if (!MaybeExtZone || 870 (hasReturnStmt(*MaybeExtZone) && !alwaysReturns(*MaybeExtZone))) 871 return false; 872 873 // FIXME: Get rid of this check once we support hoisting. 874 if (MaybeExtZone->requiresHoisting(SM, Inputs.AST->getHeuristicResolver())) 875 return false; 876 877 ExtZone = std::move(*MaybeExtZone); 878 return true; 879 } 880 881 Expected<Tweak::Effect> ExtractFunction::apply(const Selection &Inputs) { 882 const SourceManager &SM = Inputs.AST->getSourceManager(); 883 const LangOptions &LangOpts = Inputs.AST->getLangOpts(); 884 auto ExtractedFunc = getExtractedFunction(ExtZone, SM, LangOpts); 885 // FIXME: Add more types of errors. 886 if (!ExtractedFunc) 887 return ExtractedFunc.takeError(); 888 tooling::Replacements Edit; 889 if (auto Err = Edit.add(createFunctionDefinition(*ExtractedFunc, SM))) 890 return std::move(Err); 891 if (auto Err = Edit.add(replaceWithFuncCall(*ExtractedFunc, SM, LangOpts))) 892 return std::move(Err); 893 894 if (auto FwdLoc = ExtractedFunc->ForwardDeclarationPoint) { 895 // If the fwd-declaration goes in the same file, merge into Replacements. 896 // Otherwise it needs to be a separate file edit. 897 if (SM.isWrittenInSameFile(ExtractedFunc->DefinitionPoint, *FwdLoc)) { 898 if (auto Err = Edit.add(createForwardDeclaration(*ExtractedFunc, SM))) 899 return std::move(Err); 900 } else { 901 auto MultiFileEffect = Effect::mainFileEdit(SM, std::move(Edit)); 902 if (!MultiFileEffect) 903 return MultiFileEffect.takeError(); 904 905 tooling::Replacements OtherEdit( 906 createForwardDeclaration(*ExtractedFunc, SM)); 907 if (auto PathAndEdit = Tweak::Effect::fileEdit(SM, SM.getFileID(*FwdLoc), 908 OtherEdit)) 909 MultiFileEffect->ApplyEdits.try_emplace(PathAndEdit->first, 910 PathAndEdit->second); 911 else 912 return PathAndEdit.takeError(); 913 return MultiFileEffect; 914 } 915 } 916 return Effect::mainFileEdit(SM, std::move(Edit)); 917 } 918 919 } // namespace 920 } // namespace clangd 921 } // namespace clang 922