clang 20.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
17#include "clang/AST/DeclCXX.h"
18#include "clang/AST/Expr.h"
19#include "clang/AST/ExprObjC.h"
22#include <optional>
23
24namespace clang {
25namespace tooling {
26
27namespace {
28
29/// Returns true if \c E is a simple literal or a reference expression that
30/// should not be extracted.
31bool 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
48SourceLocation 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.
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.
100ExtractFunction::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;
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:84
const Decl * D
Expr * E
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:188
SourceManager & getSourceManager()
Definition: ASTContext.h:741
const LangOptions & getLangOpts() const
Definition: ASTContext.h:834
const clang::PrintingPolicy & getPrintingPolicy() const
Definition: ASTContext.h:733
CanQualType VoidTy
Definition: ASTContext.h:1160
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:499
A (possibly-)qualified type.
Definition: Type.h:929
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:8510
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.