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