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 "Logger.h" 51 #include "ParsedAST.h" 52 #include "Selection.h" 53 #include "SourceCode.h" 54 #include "refactor/Tweak.h" 55 #include "clang/AST/ASTContext.h" 56 #include "clang/AST/Decl.h" 57 #include "clang/AST/DeclTemplate.h" 58 #include "clang/AST/RecursiveASTVisitor.h" 59 #include "clang/AST/Stmt.h" 60 #include "clang/Basic/LangOptions.h" 61 #include "clang/Basic/SourceLocation.h" 62 #include "clang/Basic/SourceManager.h" 63 #include "clang/Lex/Lexer.h" 64 #include "clang/Tooling/Core/Replacement.h" 65 #include "clang/Tooling/Refactoring/Extract/SourceExtraction.h" 66 #include "llvm/ADT/None.h" 67 #include "llvm/ADT/Optional.h" 68 #include "llvm/ADT/SmallVector.h" 69 #include "llvm/ADT/StringRef.h" 70 #include "llvm/ADT/iterator_range.h" 71 #include "llvm/Support/Casting.h" 72 #include "llvm/Support/Error.h" 73 74 namespace clang { 75 namespace clangd { 76 namespace { 77 78 using Node = SelectionTree::Node; 79 80 // ExtractionZone is the part of code that is being extracted. 81 // EnclosingFunction is the function/method inside which the zone lies. 82 // We split the file into 4 parts relative to extraction zone. 83 enum class ZoneRelative { 84 Before, // Before Zone and inside EnclosingFunction. 85 Inside, // Inside Zone. 86 After, // After Zone and inside EnclosingFunction. 87 OutsideFunc // Outside EnclosingFunction. 88 }; 89 90 // A RootStmt is a statement that's fully selected including all it's children 91 // and it's parent is unselected. 92 // Check if a node is a root statement. 93 bool isRootStmt(const Node *N) { 94 if (!N->ASTNode.get<Stmt>()) 95 return false; 96 // Root statement cannot be partially selected. 97 if (N->Selected == SelectionTree::Partial) 98 return false; 99 // Only DeclStmt can be an unselected RootStmt since VarDecls claim the entire 100 // selection range in selectionTree. 101 if (N->Selected == SelectionTree::Unselected && !N->ASTNode.get<DeclStmt>()) 102 return false; 103 return true; 104 } 105 106 // Returns the (unselected) parent of all RootStmts given the commonAncestor. 107 // We only support extraction of RootStmts since it allows us to extract without 108 // having to change the selection range. Also, this means that any scope that 109 // begins in selection range, ends in selection range and any scope that begins 110 // outside the selection range, ends outside as well. 111 const Node *getParentOfRootStmts(const Node *CommonAnc) { 112 if (!CommonAnc) 113 return nullptr; 114 switch (CommonAnc->Selected) { 115 case SelectionTree::Selection::Unselected: 116 // Ensure all Children are RootStmts. 117 return llvm::all_of(CommonAnc->Children, isRootStmt) ? CommonAnc : nullptr; 118 case SelectionTree::Selection::Partial: 119 // Treat Partially selected VarDecl as completely selected since 120 // SelectionTree doesn't always select VarDecls correctly. 121 // FIXME: Remove this after D66872 is upstream) 122 if (!CommonAnc->ASTNode.get<VarDecl>()) 123 return nullptr; 124 LLVM_FALLTHROUGH; 125 case SelectionTree::Selection::Complete: 126 // If the Common Ancestor is completely selected, then it's a root statement 127 // and its parent will be unselected. 128 const Node *Parent = CommonAnc->Parent; 129 // If parent is a DeclStmt, even though it's unselected, we consider it a 130 // root statement and return its parent. This is done because the VarDecls 131 // claim the entire selection range of the Declaration and DeclStmt is 132 // always unselected. 133 return Parent->ASTNode.get<DeclStmt>() ? Parent->Parent : Parent; 134 } 135 llvm_unreachable("Unhandled SelectionTree::Selection enum"); 136 } 137 138 // The ExtractionZone class forms a view of the code wrt Zone. 139 struct ExtractionZone { 140 // Parent of RootStatements being extracted. 141 const Node *Parent = nullptr; 142 // The half-open file range of the code being extracted. 143 SourceRange ZoneRange; 144 // The function inside which our zone resides. 145 const FunctionDecl *EnclosingFunction = nullptr; 146 // The half-open file range of the enclosing function. 147 SourceRange EnclosingFuncRange; 148 SourceLocation getInsertionPoint() const { 149 return EnclosingFuncRange.getBegin(); 150 } 151 bool isRootStmt(const Stmt *S) const; 152 // The last root statement is important to decide where we need to insert a 153 // semicolon after the extraction. 154 const Node *getLastRootStmt() const { return Parent->Children.back(); } 155 void generateRootStmts(); 156 157 private: 158 llvm::DenseSet<const Stmt *> RootStmts; 159 }; 160 161 bool ExtractionZone::isRootStmt(const Stmt *S) const { 162 return RootStmts.find(S) != RootStmts.end(); 163 } 164 165 // Generate RootStmts set 166 void ExtractionZone::generateRootStmts() { 167 for (const Node *Child : Parent->Children) 168 RootStmts.insert(Child->ASTNode.get<Stmt>()); 169 } 170 171 // Finds the function in which the zone lies. 172 const FunctionDecl *findEnclosingFunction(const Node *CommonAnc) { 173 // Walk up the SelectionTree until we find a function Decl 174 for (const Node *CurNode = CommonAnc; CurNode; CurNode = CurNode->Parent) { 175 // Don't extract from lambdas 176 if (CurNode->ASTNode.get<LambdaExpr>()) 177 return nullptr; 178 if (const FunctionDecl *Func = CurNode->ASTNode.get<FunctionDecl>()) { 179 // FIXME: Support extraction from methods. 180 if (isa<CXXMethodDecl>(Func)) 181 return nullptr; 182 // FIXME: Support extraction from templated functions. 183 if (Func->isTemplated()) 184 return nullptr; 185 return Func; 186 } 187 } 188 return nullptr; 189 } 190 191 // Zone Range is the union of SourceRanges of all child Nodes in Parent since 192 // all child Nodes are RootStmts 193 llvm::Optional<SourceRange> findZoneRange(const Node *Parent, 194 const SourceManager &SM, 195 const LangOptions &LangOpts) { 196 SourceRange SR; 197 if (auto BeginFileRange = toHalfOpenFileRange( 198 SM, LangOpts, Parent->Children.front()->ASTNode.getSourceRange())) 199 SR.setBegin(BeginFileRange->getBegin()); 200 else 201 return llvm::None; 202 if (auto EndFileRange = toHalfOpenFileRange( 203 SM, LangOpts, Parent->Children.back()->ASTNode.getSourceRange())) 204 SR.setEnd(EndFileRange->getEnd()); 205 else 206 return llvm::None; 207 return SR; 208 } 209 210 // Compute the range spanned by the enclosing function. 211 // FIXME: check if EnclosingFunction has any attributes as the AST doesn't 212 // always store the source range of the attributes and thus we end up extracting 213 // between the attributes and the EnclosingFunction. 214 llvm::Optional<SourceRange> 215 computeEnclosingFuncRange(const FunctionDecl *EnclosingFunction, 216 const SourceManager &SM, 217 const LangOptions &LangOpts) { 218 return toHalfOpenFileRange(SM, LangOpts, EnclosingFunction->getSourceRange()); 219 } 220 221 // FIXME: Check we're not extracting from the initializer/condition of a control 222 // flow structure. 223 // FIXME: Check that we don't extract the compound statement of the 224 // enclosingFunction. 225 llvm::Optional<ExtractionZone> findExtractionZone(const Node *CommonAnc, 226 const SourceManager &SM, 227 const LangOptions &LangOpts) { 228 ExtractionZone ExtZone; 229 ExtZone.Parent = getParentOfRootStmts(CommonAnc); 230 if (!ExtZone.Parent || ExtZone.Parent->Children.empty()) 231 return llvm::None; 232 // Don't extract expressions. 233 // FIXME: We should extract expressions that are "statements" i.e. not 234 // subexpressions 235 if (ExtZone.Parent->Children.size() == 1 && 236 ExtZone.getLastRootStmt()->ASTNode.get<Expr>()) 237 return llvm::None; 238 ExtZone.EnclosingFunction = findEnclosingFunction(ExtZone.Parent); 239 if (!ExtZone.EnclosingFunction) 240 return llvm::None; 241 if (auto FuncRange = 242 computeEnclosingFuncRange(ExtZone.EnclosingFunction, SM, LangOpts)) 243 ExtZone.EnclosingFuncRange = *FuncRange; 244 if (auto ZoneRange = findZoneRange(ExtZone.Parent, SM, LangOpts)) 245 ExtZone.ZoneRange = *ZoneRange; 246 if (ExtZone.EnclosingFuncRange.isInvalid() || ExtZone.ZoneRange.isInvalid()) 247 return llvm::None; 248 ExtZone.generateRootStmts(); 249 return ExtZone; 250 } 251 252 // Stores information about the extracted function and provides methods for 253 // rendering it. 254 struct NewFunction { 255 struct Parameter { 256 std::string Name; 257 QualType TypeInfo; 258 bool PassByReference; 259 unsigned OrderPriority; // Lower value parameters are preferred first. 260 std::string render(const DeclContext *Context) const; 261 bool operator<(const Parameter &Other) const { 262 return OrderPriority < Other.OrderPriority; 263 } 264 }; 265 std::string Name = "extracted"; 266 std::string ReturnType; 267 std::vector<Parameter> Parameters; 268 SourceRange BodyRange; 269 SourceLocation InsertionPoint; 270 const DeclContext *EnclosingFuncContext; 271 // Decides whether the extracted function body and the function call need a 272 // semicolon after extraction. 273 tooling::ExtractionSemicolonPolicy SemicolonPolicy; 274 NewFunction(tooling::ExtractionSemicolonPolicy SemicolonPolicy) 275 : SemicolonPolicy(SemicolonPolicy) {} 276 // Render the call for this function. 277 std::string renderCall() const; 278 // Render the definition for this function. 279 std::string renderDefinition(const SourceManager &SM) const; 280 281 private: 282 std::string renderParametersForDefinition() const; 283 std::string renderParametersForCall() const; 284 // Generate the function body. 285 std::string getFuncBody(const SourceManager &SM) const; 286 }; 287 288 std::string NewFunction::renderParametersForDefinition() const { 289 std::string Result; 290 bool NeedCommaBefore = false; 291 for (const Parameter &P : Parameters) { 292 if (NeedCommaBefore) 293 Result += ", "; 294 NeedCommaBefore = true; 295 Result += P.render(EnclosingFuncContext); 296 } 297 return Result; 298 } 299 300 std::string NewFunction::renderParametersForCall() const { 301 std::string Result; 302 bool NeedCommaBefore = false; 303 for (const Parameter &P : Parameters) { 304 if (NeedCommaBefore) 305 Result += ", "; 306 NeedCommaBefore = true; 307 Result += P.Name; 308 } 309 return Result; 310 } 311 312 std::string NewFunction::renderCall() const { 313 return Name + "(" + renderParametersForCall() + ")" + 314 (SemicolonPolicy.isNeededInOriginalFunction() ? ";" : ""); 315 } 316 317 std::string NewFunction::renderDefinition(const SourceManager &SM) const { 318 return ReturnType + " " + Name + "(" + renderParametersForDefinition() + ")" + 319 " {\n" + getFuncBody(SM) + "\n}\n"; 320 } 321 322 std::string NewFunction::getFuncBody(const SourceManager &SM) const { 323 // FIXME: Generate tooling::Replacements instead of std::string to 324 // - hoist decls 325 // - add return statement 326 // - Add semicolon 327 return toSourceCode(SM, BodyRange).str() + 328 (SemicolonPolicy.isNeededInExtractedFunction() ? ";" : ""); 329 } 330 331 std::string NewFunction::Parameter::render(const DeclContext *Context) const { 332 return printType(TypeInfo, *Context) + (PassByReference ? " &" : " ") + Name; 333 } 334 335 // Stores captured information about Extraction Zone. 336 struct CapturedZoneInfo { 337 struct DeclInformation { 338 const Decl *TheDecl; 339 ZoneRelative DeclaredIn; 340 // index of the declaration or first reference. 341 unsigned DeclIndex; 342 bool IsReferencedInZone = false; 343 bool IsReferencedInPostZone = false; 344 // FIXME: Capture mutation information 345 DeclInformation(const Decl *TheDecl, ZoneRelative DeclaredIn, 346 unsigned DeclIndex) 347 : TheDecl(TheDecl), DeclaredIn(DeclaredIn), DeclIndex(DeclIndex){}; 348 // Marks the occurence of a reference for this declaration 349 void markOccurence(ZoneRelative ReferenceLoc); 350 }; 351 // Maps Decls to their DeclInfo 352 llvm::DenseMap<const Decl *, DeclInformation> DeclInfoMap; 353 // True if there is a return statement in zone. 354 bool HasReturnStmt = false; 355 // Control flow is broken if we are extracting a break/continue without a 356 // corresponding parent loop/switch 357 bool BrokenControlFlow = false; 358 // FIXME: capture TypeAliasDecl and UsingDirectiveDecl 359 // FIXME: Capture type information as well. 360 DeclInformation *createDeclInfo(const Decl *D, ZoneRelative RelativeLoc); 361 DeclInformation *getDeclInfoFor(const Decl *D); 362 }; 363 364 CapturedZoneInfo::DeclInformation * 365 CapturedZoneInfo::createDeclInfo(const Decl *D, ZoneRelative RelativeLoc) { 366 // The new Decl's index is the size of the map so far. 367 auto InsertionResult = DeclInfoMap.insert( 368 {D, DeclInformation(D, RelativeLoc, DeclInfoMap.size())}); 369 // Return the newly created DeclInfo 370 return &InsertionResult.first->second; 371 } 372 373 CapturedZoneInfo::DeclInformation * 374 CapturedZoneInfo::getDeclInfoFor(const Decl *D) { 375 // If the Decl doesn't exist, we 376 auto Iter = DeclInfoMap.find(D); 377 if (Iter == DeclInfoMap.end()) 378 return nullptr; 379 return &Iter->second; 380 } 381 382 void CapturedZoneInfo::DeclInformation::markOccurence( 383 ZoneRelative ReferenceLoc) { 384 switch (ReferenceLoc) { 385 case ZoneRelative::Inside: 386 IsReferencedInZone = true; 387 break; 388 case ZoneRelative::After: 389 IsReferencedInPostZone = true; 390 break; 391 default: 392 break; 393 } 394 } 395 396 bool isLoop(const Stmt *S) { 397 return isa<ForStmt>(S) || isa<DoStmt>(S) || isa<WhileStmt>(S) || 398 isa<CXXForRangeStmt>(S); 399 } 400 401 // Captures information from Extraction Zone 402 CapturedZoneInfo captureZoneInfo(const ExtractionZone &ExtZone) { 403 // We use the ASTVisitor instead of using the selection tree since we need to 404 // find references in the PostZone as well. 405 // FIXME: Check which statements we don't allow to extract. 406 class ExtractionZoneVisitor 407 : public clang::RecursiveASTVisitor<ExtractionZoneVisitor> { 408 public: 409 ExtractionZoneVisitor(const ExtractionZone &ExtZone) : ExtZone(ExtZone) { 410 TraverseDecl(const_cast<FunctionDecl *>(ExtZone.EnclosingFunction)); 411 } 412 413 bool TraverseStmt(Stmt *S) { 414 if (!S) 415 return true; 416 bool IsRootStmt = ExtZone.isRootStmt(const_cast<const Stmt *>(S)); 417 // If we are starting traversal of a RootStmt, we are somewhere inside 418 // ExtractionZone 419 if (IsRootStmt) 420 CurrentLocation = ZoneRelative::Inside; 421 addToLoopSwitchCounters(S, 1); 422 // Traverse using base class's TraverseStmt 423 RecursiveASTVisitor::TraverseStmt(S); 424 addToLoopSwitchCounters(S, -1); 425 // We set the current location as after since next stmt will either be a 426 // RootStmt (handled at the beginning) or after extractionZone 427 if (IsRootStmt) 428 CurrentLocation = ZoneRelative::After; 429 return true; 430 } 431 432 // Add Increment to CurNumberOf{Loops,Switch} if statement is 433 // {Loop,Switch} and inside Extraction Zone. 434 void addToLoopSwitchCounters(Stmt *S, int Increment) { 435 if (CurrentLocation != ZoneRelative::Inside) 436 return; 437 if (isLoop(S)) 438 CurNumberOfNestedLoops += Increment; 439 else if (isa<SwitchStmt>(S)) 440 CurNumberOfSwitch += Increment; 441 } 442 443 // Decrement CurNumberOf{NestedLoops,Switch} if statement is {Loop,Switch} 444 // and inside Extraction Zone. 445 void decrementLoopSwitchCounters(Stmt *S) { 446 if (CurrentLocation != ZoneRelative::Inside) 447 return; 448 if (isLoop(S)) 449 CurNumberOfNestedLoops--; 450 else if (isa<SwitchStmt>(S)) 451 CurNumberOfSwitch--; 452 } 453 454 bool VisitDecl(Decl *D) { 455 Info.createDeclInfo(D, CurrentLocation); 456 return true; 457 } 458 459 bool VisitDeclRefExpr(DeclRefExpr *DRE) { 460 // Find the corresponding Decl and mark it's occurence. 461 const Decl *D = DRE->getDecl(); 462 auto *DeclInfo = Info.getDeclInfoFor(D); 463 // If no Decl was found, the Decl must be outside the enclosingFunc. 464 if (!DeclInfo) 465 DeclInfo = Info.createDeclInfo(D, ZoneRelative::OutsideFunc); 466 DeclInfo->markOccurence(CurrentLocation); 467 // FIXME: check if reference mutates the Decl being referred. 468 return true; 469 } 470 471 bool VisitReturnStmt(ReturnStmt *Return) { 472 if (CurrentLocation == ZoneRelative::Inside) 473 Info.HasReturnStmt = true; 474 return true; 475 } 476 477 bool VisitBreakStmt(BreakStmt *Break) { 478 // Control flow is broken if break statement is selected without any 479 // parent loop or switch statement. 480 if (CurrentLocation == ZoneRelative::Inside && 481 !(CurNumberOfNestedLoops || CurNumberOfSwitch)) 482 Info.BrokenControlFlow = true; 483 return true; 484 } 485 486 bool VisitContinueStmt(ContinueStmt *Continue) { 487 // Control flow is broken if Continue statement is selected without any 488 // parent loop 489 if (CurrentLocation == ZoneRelative::Inside && !CurNumberOfNestedLoops) 490 Info.BrokenControlFlow = true; 491 return true; 492 } 493 CapturedZoneInfo Info; 494 const ExtractionZone &ExtZone; 495 ZoneRelative CurrentLocation = ZoneRelative::Before; 496 // Number of {loop,switch} statements that are currently in the traversal 497 // stack inside Extraction Zone. Used to check for broken control flow. 498 unsigned CurNumberOfNestedLoops = 0; 499 unsigned CurNumberOfSwitch = 0; 500 }; 501 ExtractionZoneVisitor Visitor(ExtZone); 502 return std::move(Visitor.Info); 503 } 504 505 // Adds parameters to ExtractedFunc. 506 // Returns true if able to find the parameters successfully and no hoisting 507 // needed. 508 // FIXME: Check if the declaration has a local/anonymous type 509 bool createParameters(NewFunction &ExtractedFunc, 510 const CapturedZoneInfo &CapturedInfo) { 511 for (const auto &KeyVal : CapturedInfo.DeclInfoMap) { 512 const auto &DeclInfo = KeyVal.second; 513 // If a Decl was Declared in zone and referenced in post zone, it 514 // needs to be hoisted (we bail out in that case). 515 // FIXME: Support Decl Hoisting. 516 if (DeclInfo.DeclaredIn == ZoneRelative::Inside && 517 DeclInfo.IsReferencedInPostZone) 518 return false; 519 if (!DeclInfo.IsReferencedInZone) 520 continue; // no need to pass as parameter, not referenced 521 if (DeclInfo.DeclaredIn == ZoneRelative::Inside || 522 DeclInfo.DeclaredIn == ZoneRelative::OutsideFunc) 523 continue; // no need to pass as parameter, still accessible. 524 // Parameter specific checks. 525 const ValueDecl *VD = dyn_cast_or_null<ValueDecl>(DeclInfo.TheDecl); 526 // Can't parameterise if the Decl isn't a ValueDecl or is a FunctionDecl 527 // (this includes the case of recursive call to EnclosingFunc in Zone). 528 if (!VD || isa<FunctionDecl>(DeclInfo.TheDecl)) 529 return false; 530 // Parameter qualifiers are same as the Decl's qualifiers. 531 QualType TypeInfo = VD->getType().getNonReferenceType(); 532 // FIXME: Need better qualifier checks: check mutated status for 533 // Decl(e.g. was it assigned, passed as nonconst argument, etc) 534 // FIXME: check if parameter will be a non l-value reference. 535 // FIXME: We don't want to always pass variables of types like int, 536 // pointers, etc by reference. 537 bool IsPassedByReference = true; 538 // We use the index of declaration as the ordering priority for parameters. 539 ExtractedFunc.Parameters.push_back( 540 {VD->getName(), TypeInfo, IsPassedByReference, DeclInfo.DeclIndex}); 541 } 542 llvm::sort(ExtractedFunc.Parameters); 543 return true; 544 } 545 546 // Clangd uses open ranges while ExtractionSemicolonPolicy (in Clang Tooling) 547 // uses closed ranges. Generates the semicolon policy for the extraction and 548 // extends the ZoneRange if necessary. 549 tooling::ExtractionSemicolonPolicy 550 getSemicolonPolicy(ExtractionZone &ExtZone, const SourceManager &SM, 551 const LangOptions &LangOpts) { 552 // Get closed ZoneRange. 553 SourceRange FuncBodyRange = {ExtZone.ZoneRange.getBegin(), 554 ExtZone.ZoneRange.getEnd().getLocWithOffset(-1)}; 555 auto SemicolonPolicy = tooling::ExtractionSemicolonPolicy::compute( 556 ExtZone.getLastRootStmt()->ASTNode.get<Stmt>(), FuncBodyRange, SM, 557 LangOpts); 558 // Update ZoneRange. 559 ExtZone.ZoneRange.setEnd(FuncBodyRange.getEnd().getLocWithOffset(1)); 560 return SemicolonPolicy; 561 } 562 563 // Generate return type for ExtractedFunc. Return false if unable to do so. 564 bool generateReturnProperties(NewFunction &ExtractedFunc, 565 const CapturedZoneInfo &CapturedInfo) { 566 567 // FIXME: Use Existing Return statements (if present) 568 // FIXME: Generate new return statement if needed. 569 if (CapturedInfo.HasReturnStmt) 570 return false; 571 ExtractedFunc.ReturnType = "void"; 572 return true; 573 } 574 575 // FIXME: add support for adding other function return types besides void. 576 // FIXME: assign the value returned by non void extracted function. 577 llvm::Expected<NewFunction> getExtractedFunction(ExtractionZone &ExtZone, 578 const SourceManager &SM, 579 const LangOptions &LangOpts) { 580 CapturedZoneInfo CapturedInfo = captureZoneInfo(ExtZone); 581 // Bail out if any break of continue exists 582 if (CapturedInfo.BrokenControlFlow) 583 return llvm::createStringError(llvm::inconvertibleErrorCode(), 584 +"Cannot extract break/continue without " 585 "corresponding loop/switch statement."); 586 NewFunction ExtractedFunc(getSemicolonPolicy(ExtZone, SM, LangOpts)); 587 ExtractedFunc.BodyRange = ExtZone.ZoneRange; 588 ExtractedFunc.InsertionPoint = ExtZone.getInsertionPoint(); 589 ExtractedFunc.EnclosingFuncContext = 590 ExtZone.EnclosingFunction->getDeclContext(); 591 if (!createParameters(ExtractedFunc, CapturedInfo) || 592 !generateReturnProperties(ExtractedFunc, CapturedInfo)) 593 return llvm::createStringError(llvm::inconvertibleErrorCode(), 594 +"Too complex to extract."); 595 return ExtractedFunc; 596 } 597 598 class ExtractFunction : public Tweak { 599 public: 600 const char *id() const override final; 601 bool prepare(const Selection &Inputs) override; 602 Expected<Effect> apply(const Selection &Inputs) override; 603 std::string title() const override { return "Extract to function"; } 604 Intent intent() const override { return Refactor; } 605 606 private: 607 ExtractionZone ExtZone; 608 }; 609 610 REGISTER_TWEAK(ExtractFunction) 611 tooling::Replacement replaceWithFuncCall(const NewFunction &ExtractedFunc, 612 const SourceManager &SM, 613 const LangOptions &LangOpts) { 614 std::string FuncCall = ExtractedFunc.renderCall(); 615 return tooling::Replacement( 616 SM, CharSourceRange(ExtractedFunc.BodyRange, false), FuncCall, LangOpts); 617 } 618 619 tooling::Replacement createFunctionDefinition(const NewFunction &ExtractedFunc, 620 const SourceManager &SM) { 621 std::string FunctionDef = ExtractedFunc.renderDefinition(SM); 622 return tooling::Replacement(SM, ExtractedFunc.InsertionPoint, 0, FunctionDef); 623 } 624 625 bool ExtractFunction::prepare(const Selection &Inputs) { 626 const Node *CommonAnc = Inputs.ASTSelection.commonAncestor(); 627 const SourceManager &SM = Inputs.AST.getSourceManager(); 628 const LangOptions &LangOpts = Inputs.AST.getASTContext().getLangOpts(); 629 if (auto MaybeExtZone = findExtractionZone(CommonAnc, SM, LangOpts)) { 630 ExtZone = std::move(*MaybeExtZone); 631 return true; 632 } 633 return false; 634 } 635 636 Expected<Tweak::Effect> ExtractFunction::apply(const Selection &Inputs) { 637 const SourceManager &SM = Inputs.AST.getSourceManager(); 638 const LangOptions &LangOpts = Inputs.AST.getASTContext().getLangOpts(); 639 auto ExtractedFunc = getExtractedFunction(ExtZone, SM, LangOpts); 640 // FIXME: Add more types of errors. 641 if (!ExtractedFunc) 642 return ExtractedFunc.takeError(); 643 tooling::Replacements Result; 644 if (auto Err = Result.add(createFunctionDefinition(*ExtractedFunc, SM))) 645 return std::move(Err); 646 if (auto Err = Result.add(replaceWithFuncCall(*ExtractedFunc, SM, LangOpts))) 647 return std::move(Err); 648 return Effect::mainFileEdit(SM, std::move(Result)); 649 } 650 651 } // namespace 652 } // namespace clangd 653 } // namespace clang 654