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