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