blob: d21db54b31a46dfad48ade9b48a86a29668d1623 [file] [log] [blame]
Shaurya Guptabf477342019-08-28 19:34:17 +00001//===--- 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// - Doesn't check 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 "ClangdUnit.h"
51#include "Logger.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
74namespace clang {
75namespace clangd {
76namespace {
77
78using 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.
83enum 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.
93bool 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.
111const 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 }
Simon Pilgrim46b346e2019-08-29 12:37:02 +0000135 llvm_unreachable("Unhandled SelectionTree::Selection enum");
Shaurya Guptabf477342019-08-28 19:34:17 +0000136}
137
138// The ExtractionZone class forms a view of the code wrt Zone.
139struct 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();
156private:
157 llvm::DenseSet<const Stmt *> RootStmts;
158};
159
160bool ExtractionZone::isRootStmt(const Stmt *S) const {
161 return RootStmts.find(S) != RootStmts.end();
162}
163
164// Generate RootStmts set
165void ExtractionZone::generateRootStmts() {
166 for(const Node *Child : Parent->Children)
167 RootStmts.insert(Child->ASTNode.get<Stmt>());
168}
169
170// Finds the function in which the zone lies.
171const FunctionDecl *findEnclosingFunction(const Node *CommonAnc) {
172 // Walk up the SelectionTree until we find a function Decl
173 for (const Node *CurNode = CommonAnc; CurNode; CurNode = CurNode->Parent) {
174 // Don't extract from lambdas
175 if (CurNode->ASTNode.get<LambdaExpr>())
176 return nullptr;
177 if (const FunctionDecl *Func = CurNode->ASTNode.get<FunctionDecl>()) {
178 // FIXME: Support extraction from methods.
179 if (isa<CXXMethodDecl>(Func))
180 return nullptr;
181 // FIXME: Support extraction from templated functions.
182 if(Func->isTemplated())
183 return nullptr;
184 return Func;
185 }
186 }
187 return nullptr;
188}
189
190// Zone Range is the union of SourceRanges of all child Nodes in Parent since
191// all child Nodes are RootStmts
192llvm::Optional<SourceRange> findZoneRange(const Node *Parent,
193 const SourceManager &SM,
194 const LangOptions &LangOpts) {
195 SourceRange SR;
196 if (auto BeginFileRange = toHalfOpenFileRange(
197 SM, LangOpts, Parent->Children.front()->ASTNode.getSourceRange()))
198 SR.setBegin(BeginFileRange->getBegin());
199 else
200 return llvm::None;
201 if (auto EndFileRange = toHalfOpenFileRange(
202 SM, LangOpts, Parent->Children.back()->ASTNode.getSourceRange()))
203 SR.setEnd(EndFileRange->getEnd());
204 else
205 return llvm::None;
206 return SR;
207}
208
209// Compute the range spanned by the enclosing function.
210// FIXME: check if EnclosingFunction has any attributes as the AST doesn't
211// always store the source range of the attributes and thus we end up extracting
212// between the attributes and the EnclosingFunction.
213llvm::Optional<SourceRange>
214computeEnclosingFuncRange(const FunctionDecl *EnclosingFunction,
215 const SourceManager &SM,
216 const LangOptions &LangOpts) {
217 return toHalfOpenFileRange(SM, LangOpts, EnclosingFunction->getSourceRange());
218}
219
220// FIXME: Check we're not extracting from the initializer/condition of a control
221// flow structure.
222// FIXME: Check that we don't extract the compound statement of the
223// enclosingFunction.
224llvm::Optional<ExtractionZone> findExtractionZone(const Node *CommonAnc,
225 const SourceManager &SM,
226 const LangOptions &LangOpts) {
227 ExtractionZone ExtZone;
228 ExtZone.Parent = getParentOfRootStmts(CommonAnc);
229 if (!ExtZone.Parent || ExtZone.Parent->Children.empty())
230 return llvm::None;
231 // Don't extract expressions.
232 // FIXME: We should extract expressions that are "statements" i.e. not
233 // subexpressions
234 if (ExtZone.Parent->Children.size() == 1 &&
235 ExtZone.getLastRootStmt()->ASTNode.get<Expr>())
236 return llvm::None;
237 ExtZone.EnclosingFunction = findEnclosingFunction(ExtZone.Parent);
238 if (!ExtZone.EnclosingFunction)
239 return llvm::None;
240 if (auto FuncRange =
241 computeEnclosingFuncRange(ExtZone.EnclosingFunction, SM, LangOpts))
242 ExtZone.EnclosingFuncRange = *FuncRange;
243 if (auto ZoneRange = findZoneRange(ExtZone.Parent, SM, LangOpts))
244 ExtZone.ZoneRange = *ZoneRange;
245 if (ExtZone.EnclosingFuncRange.isInvalid() || ExtZone.ZoneRange.isInvalid())
246 return llvm::None;
247 ExtZone.generateRootStmts();
248 return ExtZone;
249}
250
251// Stores information about the extracted function and provides methods for
252// rendering it.
253struct NewFunction {
254 struct Parameter {
255 std::string Name;
256 QualType TypeInfo;
257 bool PassByReference;
258 unsigned OrderPriority; // Lower value parameters are preferred first.
259 std::string render(const DeclContext *Context) const;
260 bool operator<(const Parameter &Other) const {
261 return OrderPriority < Other.OrderPriority;
262 }
263 };
264 std::string Name = "extracted";
265 std::string ReturnType;
266 std::vector<Parameter> Parameters;
267 SourceRange BodyRange;
268 SourceLocation InsertionPoint;
269 const DeclContext *EnclosingFuncContext;
270 // Decides whether the extracted function body and the function call need a
271 // semicolon after extraction.
272 tooling::ExtractionSemicolonPolicy SemicolonPolicy;
273 NewFunction(tooling::ExtractionSemicolonPolicy SemicolonPolicy)
274 : SemicolonPolicy(SemicolonPolicy) {}
275 // Render the call for this function.
276 std::string renderCall() const;
277 // Render the definition for this function.
278 std::string renderDefinition(const SourceManager &SM) const;
279
280private:
281 std::string renderParametersForDefinition() const;
282 std::string renderParametersForCall() const;
283 // Generate the function body.
284 std::string getFuncBody(const SourceManager &SM) const;
285};
286
287std::string NewFunction::renderParametersForDefinition() const {
288 std::string Result;
289 bool NeedCommaBefore = false;
290 for (const Parameter &P : Parameters) {
291 if (NeedCommaBefore)
292 Result += ", ";
293 NeedCommaBefore = true;
294 Result += P.render(EnclosingFuncContext);
295 }
296 return Result;
297}
298
299std::string NewFunction::renderParametersForCall() const {
300 std::string Result;
301 bool NeedCommaBefore = false;
302 for (const Parameter &P : Parameters) {
303 if (NeedCommaBefore)
304 Result += ", ";
305 NeedCommaBefore = true;
306 Result += P.Name;
307 }
308 return Result;
309}
310
311std::string NewFunction::renderCall() const {
312 return Name + "(" + renderParametersForCall() + ")" +
313 (SemicolonPolicy.isNeededInOriginalFunction() ? ";" : "");
314}
315
316std::string NewFunction::renderDefinition(const SourceManager &SM) const {
317 return ReturnType + " " + Name + "(" + renderParametersForDefinition() + ")" +
318 " {\n" + getFuncBody(SM) + "\n}\n";
319}
320
321std::string NewFunction::getFuncBody(const SourceManager &SM) const {
322 // FIXME: Generate tooling::Replacements instead of std::string to
323 // - hoist decls
324 // - add return statement
325 // - Add semicolon
326 return toSourceCode(SM, BodyRange).str() +
327 (SemicolonPolicy.isNeededInExtractedFunction() ? ";" : "");
328}
329
330std::string NewFunction::Parameter::render(const DeclContext *Context) const {
331 return printType(TypeInfo, *Context) + (PassByReference ? " &" : " ") + Name;
332}
333
334// Stores captured information about Extraction Zone.
335struct CapturedZoneInfo {
336 struct DeclInformation {
337 const Decl *TheDecl;
338 ZoneRelative DeclaredIn;
339 // index of the declaration or first reference.
340 unsigned DeclIndex;
341 bool IsReferencedInZone = false;
342 bool IsReferencedInPostZone = false;
343 // FIXME: Capture mutation information
344 DeclInformation(const Decl *TheDecl, ZoneRelative DeclaredIn,
345 unsigned DeclIndex)
346 : TheDecl(TheDecl), DeclaredIn(DeclaredIn), DeclIndex(DeclIndex){};
347 // Marks the occurence of a reference for this declaration
348 void markOccurence(ZoneRelative ReferenceLoc);
349 };
350 // Maps Decls to their DeclInfo
351 llvm::DenseMap<const Decl *, DeclInformation> DeclInfoMap;
352 // True if there is a return statement in zone.
353 bool HasReturnStmt = false;
354 // For now we just care whether there exists a break/continue in zone.
355 bool HasBreakOrContinue = false;
356 // FIXME: capture TypeAliasDecl and UsingDirectiveDecl
357 // FIXME: Capture type information as well.
358 DeclInformation *createDeclInfo(const Decl *D, ZoneRelative RelativeLoc);
359 DeclInformation *getDeclInfoFor(const Decl *D);
360};
361
362CapturedZoneInfo::DeclInformation *
363CapturedZoneInfo::createDeclInfo(const Decl *D, ZoneRelative RelativeLoc) {
364 // The new Decl's index is the size of the map so far.
365 auto InsertionResult = DeclInfoMap.insert(
366 {D, DeclInformation(D, RelativeLoc, DeclInfoMap.size())});
367 // Return the newly created DeclInfo
368 return &InsertionResult.first->second;
369}
370
371CapturedZoneInfo::DeclInformation *
372CapturedZoneInfo::getDeclInfoFor(const Decl *D) {
373 // If the Decl doesn't exist, we
374 auto Iter = DeclInfoMap.find(D);
375 if (Iter == DeclInfoMap.end())
376 return nullptr;
377 return &Iter->second;
378}
379
380void CapturedZoneInfo::DeclInformation::markOccurence(
381 ZoneRelative ReferenceLoc) {
382 switch (ReferenceLoc) {
383 case ZoneRelative::Inside:
384 IsReferencedInZone = true;
385 break;
386 case ZoneRelative::After:
387 IsReferencedInPostZone = true;
388 break;
389 default:
390 break;
391 }
392}
393
394// Captures information from Extraction Zone
395CapturedZoneInfo captureZoneInfo(const ExtractionZone &ExtZone) {
396 // We use the ASTVisitor instead of using the selection tree since we need to
397 // find references in the PostZone as well.
398 // FIXME: Check which statements we don't allow to extract.
399 class ExtractionZoneVisitor
400 : public clang::RecursiveASTVisitor<ExtractionZoneVisitor> {
401 public:
402 ExtractionZoneVisitor(const ExtractionZone &ExtZone) : ExtZone(ExtZone) {
403 TraverseDecl(const_cast<FunctionDecl *>(ExtZone.EnclosingFunction));
404 }
405 bool TraverseStmt(Stmt *S) {
406 bool IsRootStmt = ExtZone.isRootStmt(const_cast<const Stmt *>(S));
407 // If we are starting traversal of a RootStmt, we are somewhere inside
408 // ExtractionZone
409 if (IsRootStmt)
410 CurrentLocation = ZoneRelative::Inside;
411 // Traverse using base class's TraverseStmt
412 RecursiveASTVisitor::TraverseStmt(S);
413 // We set the current location as after since next stmt will either be a
414 // RootStmt (handled at the beginning) or after extractionZone
415 if (IsRootStmt)
416 CurrentLocation = ZoneRelative::After;
417 return true;
418 }
419 bool VisitDecl(Decl *D) {
420 Info.createDeclInfo(D, CurrentLocation);
421 return true;
422 }
423 bool VisitDeclRefExpr(DeclRefExpr *DRE) {
424 // Find the corresponding Decl and mark it's occurence.
425 const Decl *D = DRE->getDecl();
426 auto *DeclInfo = Info.getDeclInfoFor(D);
427 // If no Decl was found, the Decl must be outside the enclosingFunc.
428 if (!DeclInfo)
429 DeclInfo = Info.createDeclInfo(D, ZoneRelative::OutsideFunc);
430 DeclInfo->markOccurence(CurrentLocation);
431 // FIXME: check if reference mutates the Decl being referred.
432 return true;
433 }
434 bool VisitReturnStmt(ReturnStmt *Return) {
435 if (CurrentLocation == ZoneRelative::Inside)
436 Info.HasReturnStmt = true;
437 return true;
438 }
439
440 // FIXME: check for broken break/continue only.
441 bool VisitBreakStmt(BreakStmt *Break) {
442 if (CurrentLocation == ZoneRelative::Inside)
443 Info.HasBreakOrContinue = true;
444 return true;
445 }
446 bool VisitContinueStmt(ContinueStmt *Continue) {
447 if (CurrentLocation == ZoneRelative::Inside)
448 Info.HasBreakOrContinue = true;
449 return true;
450 }
451 CapturedZoneInfo Info;
452 const ExtractionZone &ExtZone;
453 ZoneRelative CurrentLocation = ZoneRelative::Before;
454 };
455 ExtractionZoneVisitor Visitor(ExtZone);
456 return std::move(Visitor.Info);
457}
458
459// Adds parameters to ExtractedFunc.
460// Returns true if able to find the parameters successfully and no hoisting
461// needed.
Shaurya Gupta6a801452019-08-29 15:11:59 +0000462// FIXME: Check if the declaration has a local/anonymous type
Shaurya Guptabf477342019-08-28 19:34:17 +0000463bool createParameters(NewFunction &ExtractedFunc,
464 const CapturedZoneInfo &CapturedInfo) {
465 for (const auto &KeyVal : CapturedInfo.DeclInfoMap) {
466 const auto &DeclInfo = KeyVal.second;
467 // If a Decl was Declared in zone and referenced in post zone, it
468 // needs to be hoisted (we bail out in that case).
469 // FIXME: Support Decl Hoisting.
470 if (DeclInfo.DeclaredIn == ZoneRelative::Inside &&
471 DeclInfo.IsReferencedInPostZone)
472 return false;
473 if (!DeclInfo.IsReferencedInZone)
474 continue; // no need to pass as parameter, not referenced
475 if (DeclInfo.DeclaredIn == ZoneRelative::Inside ||
476 DeclInfo.DeclaredIn == ZoneRelative::OutsideFunc)
477 continue; // no need to pass as parameter, still accessible.
478 // Parameter specific checks.
479 const ValueDecl *VD = dyn_cast_or_null<ValueDecl>(DeclInfo.TheDecl);
480 // Can't parameterise if the Decl isn't a ValueDecl or is a FunctionDecl
481 // (this includes the case of recursive call to EnclosingFunc in Zone).
482 if (!VD || isa<FunctionDecl>(DeclInfo.TheDecl))
483 return false;
484 // Parameter qualifiers are same as the Decl's qualifiers.
485 QualType TypeInfo = VD->getType().getNonReferenceType();
486 // FIXME: Need better qualifier checks: check mutated status for
487 // Decl(e.g. was it assigned, passed as nonconst argument, etc)
488 // FIXME: check if parameter will be a non l-value reference.
489 // FIXME: We don't want to always pass variables of types like int,
490 // pointers, etc by reference.
491 bool IsPassedByReference = true;
492 // We use the index of declaration as the ordering priority for parameters.
493 ExtractedFunc.Parameters.push_back(
494 {VD->getName(), TypeInfo, IsPassedByReference, DeclInfo.DeclIndex});
495 }
496 llvm::sort(ExtractedFunc.Parameters);
497 return true;
498}
499
500// Clangd uses open ranges while ExtractionSemicolonPolicy (in Clang Tooling)
501// uses closed ranges. Generates the semicolon policy for the extraction and
502// extends the ZoneRange if necessary.
503tooling::ExtractionSemicolonPolicy
504getSemicolonPolicy(ExtractionZone &ExtZone, const SourceManager &SM,
505 const LangOptions &LangOpts) {
506 // Get closed ZoneRange.
507 SourceRange FuncBodyRange = {ExtZone.ZoneRange.getBegin(),
508 ExtZone.ZoneRange.getEnd().getLocWithOffset(-1)};
509 auto SemicolonPolicy = tooling::ExtractionSemicolonPolicy::compute(
510 ExtZone.getLastRootStmt()->ASTNode.get<Stmt>(), FuncBodyRange, SM,
511 LangOpts);
512 // Update ZoneRange.
513 ExtZone.ZoneRange.setEnd(FuncBodyRange.getEnd().getLocWithOffset(1));
514 return SemicolonPolicy;
515}
516
517// Generate return type for ExtractedFunc. Return false if unable to do so.
518bool generateReturnProperties(NewFunction &ExtractedFunc,
519 const CapturedZoneInfo &CapturedInfo) {
520
521 // FIXME: Use Existing Return statements (if present)
522 // FIXME: Generate new return statement if needed.
523 if (CapturedInfo.HasReturnStmt)
524 return false;
525 ExtractedFunc.ReturnType = "void";
526 return true;
527}
528
529// FIXME: add support for adding other function return types besides void.
530// FIXME: assign the value returned by non void extracted function.
531llvm::Expected<NewFunction> getExtractedFunction(ExtractionZone &ExtZone,
532 const SourceManager &SM,
533 const LangOptions &LangOpts) {
534 CapturedZoneInfo CapturedInfo = captureZoneInfo(ExtZone);
535 // Bail out if any break of continue exists
536 // FIXME: check for broken control flow only
537 if (CapturedInfo.HasBreakOrContinue)
538 return llvm::createStringError(llvm::inconvertibleErrorCode(),
539 +"Cannot extract break or continue.");
540 NewFunction ExtractedFunc(getSemicolonPolicy(ExtZone, SM, LangOpts));
541 ExtractedFunc.BodyRange = ExtZone.ZoneRange;
542 ExtractedFunc.InsertionPoint = ExtZone.getInsertionPoint();
543 ExtractedFunc.EnclosingFuncContext =
544 ExtZone.EnclosingFunction->getDeclContext();
545 if (!createParameters(ExtractedFunc, CapturedInfo) ||
546 !generateReturnProperties(ExtractedFunc, CapturedInfo))
547 return llvm::createStringError(llvm::inconvertibleErrorCode(),
548 +"Too complex to extract.");
549 return ExtractedFunc;
550}
551
552class ExtractFunction : public Tweak {
553public:
554 const char *id() const override final;
555 bool prepare(const Selection &Inputs) override;
556 Expected<Effect> apply(const Selection &Inputs) override;
557 std::string title() const override { return "Extract to function"; }
558 Intent intent() const override { return Refactor; }
559
560private:
561 ExtractionZone ExtZone;
562};
563
564REGISTER_TWEAK(ExtractFunction)
565tooling::Replacement replaceWithFuncCall(const NewFunction &ExtractedFunc,
566 const SourceManager &SM,
567 const LangOptions &LangOpts) {
568 std::string FuncCall = ExtractedFunc.renderCall();
569 return tooling::Replacement(
570 SM, CharSourceRange(ExtractedFunc.BodyRange, false), FuncCall, LangOpts);
571}
572
573tooling::Replacement createFunctionDefinition(const NewFunction &ExtractedFunc,
574 const SourceManager &SM) {
575 std::string FunctionDef = ExtractedFunc.renderDefinition(SM);
576 return tooling::Replacement(SM, ExtractedFunc.InsertionPoint, 0, FunctionDef);
577}
578
579bool ExtractFunction::prepare(const Selection &Inputs) {
580 const Node *CommonAnc = Inputs.ASTSelection.commonAncestor();
581 const SourceManager &SM = Inputs.AST.getSourceManager();
582 const LangOptions &LangOpts = Inputs.AST.getASTContext().getLangOpts();
583 if (auto MaybeExtZone = findExtractionZone(CommonAnc, SM, LangOpts)) {
584 ExtZone = std::move(*MaybeExtZone);
585 return true;
586 }
587 return false;
588}
589
590Expected<Tweak::Effect> ExtractFunction::apply(const Selection &Inputs) {
591 const SourceManager &SM = Inputs.AST.getSourceManager();
592 const LangOptions &LangOpts = Inputs.AST.getASTContext().getLangOpts();
593 auto ExtractedFunc = getExtractedFunction(ExtZone, SM, LangOpts);
594 // FIXME: Add more types of errors.
595 if (!ExtractedFunc)
596 return ExtractedFunc.takeError();
597 tooling::Replacements Result;
598 if (auto Err = Result.add(createFunctionDefinition(*ExtractedFunc, SM)))
599 return std::move(Err);
600 if (auto Err = Result.add(replaceWithFuncCall(*ExtractedFunc, SM, LangOpts)))
601 return std::move(Err);
602 return Effect::applyEdit(Result);
603}
604
605} // namespace
606} // namespace clangd
607} // namespace clang