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