clang  6.0.0svn
Extract.cpp
Go to the documentation of this file.
1 //===--- Extract.cpp - Clang refactoring library --------------------------===//
2 //
3 // The LLVM Compiler Infrastructure
4 //
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
7 //
8 //===----------------------------------------------------------------------===//
9 ///
10 /// \file
11 /// \brief Implements the "extract" refactoring that can pull code into
12 /// new functions, methods or declare new variables.
13 ///
14 //===----------------------------------------------------------------------===//
15 
17 #include "SourceExtraction.h"
18 #include "clang/AST/ASTContext.h"
19 #include "clang/AST/DeclCXX.h"
20 #include "clang/AST/Expr.h"
21 #include "clang/AST/ExprObjC.h"
23 
24 namespace clang {
25 namespace tooling {
26 
27 namespace {
28 
29 /// Returns true if \c E is a simple literal or a reference expression that
30 /// should not be extracted.
31 bool isSimpleExpression(const Expr *E) {
32  if (!E)
33  return false;
34  switch (E->IgnoreParenCasts()->getStmtClass()) {
35  case Stmt::DeclRefExprClass:
36  case Stmt::PredefinedExprClass:
37  case Stmt::IntegerLiteralClass:
38  case Stmt::FloatingLiteralClass:
39  case Stmt::ImaginaryLiteralClass:
40  case Stmt::CharacterLiteralClass:
41  case Stmt::StringLiteralClass:
42  return true;
43  default:
44  return false;
45  }
46 }
47 
48 SourceLocation computeFunctionExtractionLocation(const Decl *D) {
49  if (isa<CXXMethodDecl>(D)) {
50  // Code from method that is defined in class body should be extracted to a
51  // function defined just before the class.
52  while (const auto *RD = dyn_cast<CXXRecordDecl>(D->getLexicalDeclContext()))
53  D = RD;
54  }
55  return D->getLocStart();
56 }
57 
58 } // end anonymous namespace
59 
61  static const RefactoringDescriptor Descriptor = {
62  "extract-function",
63  "Extract Function",
64  "(WIP action; use with caution!) Extracts code into a new function",
65  };
66  return Descriptor;
67 }
68 
72  Optional<std::string> DeclName) {
73  // We would like to extract code out of functions/methods/blocks.
74  // Prohibit extraction from things like global variable / field
75  // initializers and other top-level expressions.
76  if (!Code.isInFunctionLikeBodyOfCode())
77  return Context.createDiagnosticError(
78  diag::err_refactor_code_outside_of_function);
79 
80  if (Code.size() == 1) {
81  // Avoid extraction of simple literals and references.
82  if (isSimpleExpression(dyn_cast<Expr>(Code[0])))
83  return Context.createDiagnosticError(
84  diag::err_refactor_extract_simple_expression);
85 
86  // Property setters can't be extracted.
87  if (const auto *PRE = dyn_cast<ObjCPropertyRefExpr>(Code[0])) {
88  if (!PRE->isMessagingGetter())
89  return Context.createDiagnosticError(
90  diag::err_refactor_extract_prohibited_expression);
91  }
92  }
93 
94  return ExtractFunction(std::move(Code), DeclName);
95 }
96 
97 // FIXME: Support C++ method extraction.
98 // FIXME: Support Objective-C method extraction.
100 ExtractFunction::createSourceReplacements(RefactoringRuleContext &Context) {
101  const Decl *ParentDecl = Code.getFunctionLikeNearestParent();
102  assert(ParentDecl && "missing parent");
103 
104  // Compute the source range of the code that should be extracted.
105  SourceRange ExtractedRange(Code[0]->getLocStart(),
106  Code[Code.size() - 1]->getLocEnd());
107  // FIXME (Alex L): Add code that accounts for macro locations.
108 
109  ASTContext &AST = Context.getASTContext();
110  SourceManager &SM = AST.getSourceManager();
111  const LangOptions &LangOpts = AST.getLangOpts();
112  Rewriter ExtractedCodeRewriter(SM, LangOpts);
113 
114  // FIXME: Capture used variables.
115 
116  // Compute the return type.
117  QualType ReturnType = AST.VoidTy;
118  // FIXME (Alex L): Account for the return statement in extracted code.
119  // FIXME (Alex L): Check for lexical expression instead.
120  bool IsExpr = Code.size() == 1 && isa<Expr>(Code[0]);
121  if (IsExpr) {
122  // FIXME (Alex L): Get a more user-friendly type if needed.
123  ReturnType = cast<Expr>(Code[0])->getType();
124  }
125 
126  // FIXME: Rewrite the extracted code performing any required adjustments.
127 
128  // FIXME: Capture any field if necessary (method -> function extraction).
129 
130  // FIXME: Sort captured variables by name.
131 
132  // FIXME: Capture 'this' / 'self' if necessary.
133 
134  // FIXME: Compute the actual parameter types.
135 
136  // Compute the location of the extracted declaration.
137  SourceLocation ExtractedDeclLocation =
138  computeFunctionExtractionLocation(ParentDecl);
139  // FIXME: Adjust the location to account for any preceding comments.
140 
141  // FIXME: Adjust with PP awareness like in Sema to get correct 'bool'
142  // treatment.
143  PrintingPolicy PP = AST.getPrintingPolicy();
144  // FIXME: PP.UseStdFunctionForLambda = true;
145  PP.SuppressStrongLifetime = true;
146  PP.SuppressLifetimeQualifiers = true;
147  PP.SuppressUnwrittenScope = true;
148 
150  Code[Code.size() - 1], ExtractedRange, SM, LangOpts);
151  AtomicChange Change(SM, ExtractedDeclLocation);
152  // Create the replacement for the extracted declaration.
153  {
154  std::string ExtractedCode;
155  llvm::raw_string_ostream OS(ExtractedCode);
156  // FIXME: Use 'inline' in header.
157  OS << "static ";
158  ReturnType.print(OS, PP, DeclName);
159  OS << '(';
160  // FIXME: Arguments.
161  OS << ')';
162 
163  // Function body.
164  OS << " {\n";
165  if (IsExpr && !ReturnType->isVoidType())
166  OS << "return ";
167  OS << ExtractedCodeRewriter.getRewrittenText(ExtractedRange);
168  if (Semicolons.isNeededInExtractedFunction())
169  OS << ';';
170  OS << "\n}\n\n";
171  auto Err = Change.insert(SM, ExtractedDeclLocation, OS.str());
172  if (Err)
173  return std::move(Err);
174  }
175 
176  // Create the replacement for the call to the extracted declaration.
177  {
178  std::string ReplacedCode;
179  llvm::raw_string_ostream OS(ReplacedCode);
180 
181  OS << DeclName << '(';
182  // FIXME: Forward arguments.
183  OS << ')';
184  if (Semicolons.isNeededInOriginalFunction())
185  OS << ';';
186 
187  auto Err = Change.replace(
188  SM, CharSourceRange::getTokenRange(ExtractedRange), OS.str());
189  if (Err)
190  return std::move(Err);
191  }
192 
193  // FIXME: Add support for assocciated symbol location to AtomicChange to mark
194  // the ranges of the name of the extracted declaration.
195  return AtomicChanges{std::move(Change)};
196 }
197 
198 } // end namespace tooling
199 } // end namespace clang
Defines the clang::ASTContext interface.
A (possibly-)qualified type.
Definition: Type.h:653
void print(raw_ostream &OS, const PrintingPolicy &Policy, const Twine &PlaceHolder=Twine(), unsigned Indentation=0) const
Definition: Type.h:991
std::vector< AtomicChange > AtomicChanges
Definition: AtomicChange.h:141
static CharSourceRange getTokenRange(SourceRange R)
Decl - This represents one declaration (or definition), e.g.
Definition: DeclBase.h:86
Describes how types, statements, expressions, and declarations should be printed. ...
Definition: PrettyPrinter.h:38
std::string getRewrittenText(SourceRange Range) const
getRewrittenText - Return the rewritten form of the text in the specified range.
Definition: Rewriter.cpp:168
unsigned SuppressLifetimeQualifiers
When true, suppress printing of lifetime qualifier in ARC.
Holds long-lived AST nodes (such as types and decls) that can be referred to throughout the semantic ...
Definition: ASTContext.h:149
Determines which semicolons should be inserted during extraction.
llvm::Error createDiagnosticError(SourceLocation Loc, unsigned DiagID)
Creates an llvm::Error value that contains a diagnostic.
Keeps track of the various options that can be enabled, which controls the dialect of C or C++ that i...
Definition: LangOptions.h:48
unsigned SuppressStrongLifetime
When true, suppress printing of the __strong lifetime qualifier in ARC.
The refactoring rule context stores all of the inputs that might be needed by a refactoring action ru...
const Decl * getFunctionLikeNearestParent() const
Returns the nearest function-like parent declaration or null if such declaration doesn&#39;t exist...
An "Extract Function" refactoring moves code into a new function that&#39;s then called from the place wh...
Definition: Extract.h:21
const SourceManager & SM
Definition: Format.cpp:1337
Encodes a location in the source.
An AST selection value that corresponds to a selection of a set of statements that belong to one body...
Definition: ASTSelection.h:95
static ExtractionSemicolonPolicy compute(const Stmt *S, SourceRange &ExtractedRange, const SourceManager &SM, const LangOptions &LangOpts)
Returns the semicolon insertion policy that is needed for extraction of the given statement from the ...
Dataflow Directional Tag Classes.
size_t size() const
Returns the number of selected statements.
Definition: ASTSelection.h:104
bool isInFunctionLikeBodyOfCode() const
Returns true when a selected code range is in a function-like body of code, like a function...
bool SuppressUnwrittenScope
Suppress printing parts of scope specifiers that don&#39;t need to be written, e.g., for inline or anonym...
static const RefactoringDescriptor & describe()
Definition: Extract.cpp:60
Rewriter - This is the main interface to the rewrite buffers.
Definition: Rewriter.h:31
Defines the C++ Decl subclasses, other than those for templates (found in DeclTemplate.h) and friends (in DeclFriend.h).
bool isVoidType() const
Definition: Type.h:6171
An atomic change is used to create and group a set of source edits, e.g.
Definition: AtomicChange.h:37
A trivial tuple used to represent a source range.
static Expected< ExtractFunction > initiate(RefactoringRuleContext &Context, CodeRangeASTSelection Code, Optional< std::string > DeclName)
Initiates the extract function refactoring operation.
Definition: Extract.cpp:70
This class handles loading and caching of source files into memory.