clang  19.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 #include <optional>
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->getBeginLoc();
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  std::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]->getBeginLoc(),
106  Code[Code.size() - 1]->getEndLoc());
107  // FIXME (Alex L): Add code that accounts for macro locations.
108 
109  ASTContext &AST = Context.getASTContext();
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.
144  // FIXME: PP.UseStdFunctionForLambda = true;
145  PP.SuppressStrongLifetime = true;
146  PP.SuppressLifetimeQualifiers = true;
147  PP.SuppressUnwrittenScope = true;
148 
149  ExtractionSemicolonPolicy Semicolons = ExtractionSemicolonPolicy::compute(
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.
#define SM(sm)
Definition: Cuda.cpp:83
Defines the C++ Decl subclasses, other than those for templates (found in DeclTemplate....
Holds long-lived AST nodes (such as types and decls) that can be referred to throughout the semantic ...
Definition: ASTContext.h:185
const clang::PrintingPolicy & getPrintingPolicy() const
Definition: ASTContext.h:700
SourceManager & getSourceManager()
Definition: ASTContext.h:708
const LangOptions & getLangOpts() const
Definition: ASTContext.h:778
CanQualType VoidTy
Definition: ASTContext.h:1094
static CharSourceRange getTokenRange(SourceRange R)
Decl - This represents one declaration (or definition), e.g.
Definition: DeclBase.h:86
Keeps track of the various options that can be enabled, which controls the dialect of C or C++ that i...
Definition: LangOptions.h:482
A (possibly-)qualified type.
Definition: Type.h:940
void print(raw_ostream &OS, const PrintingPolicy &Policy, const Twine &PlaceHolder=Twine(), unsigned Indentation=0) const
Rewriter - This is the main interface to the rewrite buffers.
Definition: Rewriter.h:32
Encodes a location in the source.
This class handles loading and caching of source files into memory.
A trivial tuple used to represent a source range.
bool isVoidType() const
Definition: Type.h:7939
An AST selection value that corresponds to a selection of a set of statements that belong to one body...
Definition: ASTSelection.h:96
const Decl * getFunctionLikeNearestParent() const
Returns the nearest function-like parent declaration or null if such declaration doesn't exist.
bool isInFunctionLikeBodyOfCode() const
Returns true when a selected code range is in a function-like body of code, like a function,...
size_t size() const
Returns the number of selected statements.
Definition: ASTSelection.h:105
An "Extract Function" refactoring moves code into a new function that's then called from the place wh...
Definition: Extract.h:21
static Expected< ExtractFunction > initiate(RefactoringRuleContext &Context, CodeRangeASTSelection Code, std::optional< std::string > DeclName)
Initiates the extract function refactoring operation.
Definition: Extract.cpp:70
static const RefactoringDescriptor & describe()
Definition: Extract.cpp:60
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 ...
The refactoring rule context stores all of the inputs that might be needed by a refactoring action ru...
std::vector< AtomicChange > AtomicChanges
Definition: AtomicChange.h:154
The JSON file list parser is used to communicate input to InstallAPI.
Describes how types, statements, expressions, and declarations should be printed.
Definition: PrettyPrinter.h:57
unsigned SuppressUnwrittenScope
Suppress printing parts of scope specifiers that are never written, e.g., for anonymous namespaces.
unsigned SuppressStrongLifetime
When true, suppress printing of the __strong lifetime qualifier in ARC.
unsigned SuppressLifetimeQualifiers
When true, suppress printing of lifetime qualifier in ARC.