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