Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/contrib/llvm-project/clang/lib/Tooling/Refactoring/Extract/Extract.cpp
35295 views
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
15
#include "clang/Tooling/Refactoring/Extract/Extract.h"
16
#include "clang/AST/ASTContext.h"
17
#include "clang/AST/DeclCXX.h"
18
#include "clang/AST/Expr.h"
19
#include "clang/AST/ExprObjC.h"
20
#include "clang/Rewrite/Core/Rewriter.h"
21
#include "clang/Tooling/Refactoring/Extract/SourceExtraction.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
60
const RefactoringDescriptor &ExtractFunction::describe() {
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
69
Expected<ExtractFunction>
70
ExtractFunction::initiate(RefactoringRuleContext &Context,
71
CodeRangeASTSelection Code,
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.
99
Expected<AtomicChanges>
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();
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
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
200
201