1 //===--- Extract.cpp - Clang refactoring library --------------------------===//
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
7 //===----------------------------------------------------------------------===//
10 /// Implements the "extract" refactoring that can pull code into
11 /// new functions, methods or declare new variables.
13 //===----------------------------------------------------------------------===//
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"
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
) {
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
:
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()))
55 return D
->getBeginLoc();
58 } // end anonymous namespace
60 const RefactoringDescriptor
&ExtractFunction::describe() {
61 static const RefactoringDescriptor Descriptor
= {
64 "(WIP action; use with caution!) Extracts code into a new function",
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
);
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
);
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
);
94 return ExtractFunction(std::move(Code
), DeclName
);
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");
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.
109 ASTContext
&AST
= Context
.getASTContext();
110 SourceManager
&SM
= AST
.getSourceManager();
111 const LangOptions
&LangOpts
= AST
.getLangOpts();
112 Rewriter
ExtractedCodeRewriter(SM
, LangOpts
);
114 // FIXME: Capture used variables.
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]);
122 // FIXME (Alex L): Get a more user-friendly type if needed.
123 ReturnType
= cast
<Expr
>(Code
[0])->getType();
126 // FIXME: Rewrite the extracted code performing any required adjustments.
128 // FIXME: Capture any field if necessary (method -> function extraction).
130 // FIXME: Sort captured variables by name.
132 // FIXME: Capture 'this' / 'self' if necessary.
134 // FIXME: Compute the actual parameter types.
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.
141 // FIXME: Adjust with PP awareness like in Sema to get correct 'bool'
143 PrintingPolicy PP
= AST
.getPrintingPolicy();
144 // FIXME: PP.UseStdFunctionForLambda = true;
145 PP
.SuppressStrongLifetime
= true;
146 PP
.SuppressLifetimeQualifiers
= true;
147 PP
.SuppressUnwrittenScope
= true;
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.
154 std::string ExtractedCode
;
155 llvm::raw_string_ostream
OS(ExtractedCode
);
156 // FIXME: Use 'inline' in header.
158 ReturnType
.print(OS
, PP
, DeclName
);
165 if (IsExpr
&& !ReturnType
->isVoidType())
167 OS
<< ExtractedCodeRewriter
.getRewrittenText(ExtractedRange
);
168 if (Semicolons
.isNeededInExtractedFunction())
171 auto Err
= Change
.insert(SM
, ExtractedDeclLocation
, OS
.str());
173 return std::move(Err
);
176 // Create the replacement for the call to the extracted declaration.
178 std::string ReplacedCode
;
179 llvm::raw_string_ostream
OS(ReplacedCode
);
181 OS
<< DeclName
<< '(';
182 // FIXME: Forward arguments.
184 if (Semicolons
.isNeededInOriginalFunction())
187 auto Err
= Change
.replace(
188 SM
, CharSourceRange::getTokenRange(ExtractedRange
), OS
.str());
190 return std::move(Err
);
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
)};
198 } // end namespace tooling
199 } // end namespace clang