/Users/buildslave/jenkins/workspace/coverage/llvm-project/clang/lib/Tooling/Refactoring/Extract/Extract.cpp
Line | Count | Source (jump to first uncovered line) |
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 | | |
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 | 32 | bool isSimpleExpression(const Expr *E) { |
31 | 32 | if (!E) |
32 | 18 | return false; |
33 | 14 | switch (E->IgnoreParenCasts()->getStmtClass()) { |
34 | 1 | case Stmt::DeclRefExprClass: |
35 | 1 | case Stmt::PredefinedExprClass: |
36 | 3 | case Stmt::IntegerLiteralClass: |
37 | 3 | case Stmt::FloatingLiteralClass: |
38 | 3 | case Stmt::ImaginaryLiteralClass: |
39 | 4 | case Stmt::CharacterLiteralClass: |
40 | 5 | case Stmt::StringLiteralClass: |
41 | 5 | return true; |
42 | 9 | default: |
43 | 9 | return false; |
44 | 14 | } |
45 | 14 | } |
46 | | |
47 | 26 | SourceLocation computeFunctionExtractionLocation(const Decl *D) { |
48 | 26 | 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 | 7 | while (const auto *RD = dyn_cast<CXXRecordDecl>(D->getLexicalDeclContext())) |
52 | 3 | D = RD; |
53 | 4 | } |
54 | 26 | return D->getBeginLoc(); |
55 | 26 | } |
56 | | |
57 | | } // end anonymous namespace |
58 | | |
59 | 1 | const RefactoringDescriptor &ExtractFunction::describe() { |
60 | 1 | static const RefactoringDescriptor Descriptor = { |
61 | 1 | "extract-function", |
62 | 1 | "Extract Function", |
63 | 1 | "(WIP action; use with caution!) Extracts code into a new function", |
64 | 1 | }; |
65 | 1 | return Descriptor; |
66 | 1 | } |
67 | | |
68 | | Expected<ExtractFunction> |
69 | | ExtractFunction::initiate(RefactoringRuleContext &Context, |
70 | | CodeRangeASTSelection Code, |
71 | 41 | 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 | 41 | if (!Code.isInFunctionLikeBodyOfCode()) |
76 | 8 | return Context.createDiagnosticError( |
77 | 8 | diag::err_refactor_code_outside_of_function); |
78 | | |
79 | 33 | if (Code.size() == 1) { |
80 | | // Avoid extraction of simple literals and references. |
81 | 32 | if (isSimpleExpression(dyn_cast<Expr>(Code[0]))) |
82 | 5 | return Context.createDiagnosticError( |
83 | 5 | diag::err_refactor_extract_simple_expression); |
84 | | |
85 | | // Property setters can't be extracted. |
86 | 27 | if (const auto *PRE = dyn_cast<ObjCPropertyRefExpr>(Code[0])) { |
87 | 4 | if (!PRE->isMessagingGetter()) |
88 | 2 | return Context.createDiagnosticError( |
89 | 2 | diag::err_refactor_extract_prohibited_expression); |
90 | 26 | } |
91 | 27 | } |
92 | | |
93 | 26 | return ExtractFunction(std::move(Code), DeclName); |
94 | 26 | } |
95 | | |
96 | | // FIXME: Support C++ method extraction. |
97 | | // FIXME: Support Objective-C method extraction. |
98 | | Expected<AtomicChanges> |
99 | 26 | ExtractFunction::createSourceReplacements(RefactoringRuleContext &Context) { |
100 | 26 | const Decl *ParentDecl = Code.getFunctionLikeNearestParent(); |
101 | 26 | assert(ParentDecl && "missing parent"); |
102 | | |
103 | | // Compute the source range of the code that should be extracted. |
104 | 26 | SourceRange ExtractedRange(Code[0]->getBeginLoc(), |
105 | 26 | Code[Code.size() - 1]->getEndLoc()); |
106 | | // FIXME (Alex L): Add code that accounts for macro locations. |
107 | | |
108 | 26 | ASTContext &AST = Context.getASTContext(); |
109 | 26 | SourceManager &SM = AST.getSourceManager(); |
110 | 26 | const LangOptions &LangOpts = AST.getLangOpts(); |
111 | 26 | Rewriter ExtractedCodeRewriter(SM, LangOpts); |
112 | | |
113 | | // FIXME: Capture used variables. |
114 | | |
115 | | // Compute the return type. |
116 | 26 | 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 | 26 | bool IsExpr = Code.size() == 1 && isa<Expr>(Code[0])25 ; |
120 | 26 | if (IsExpr) { |
121 | | // FIXME (Alex L): Get a more user-friendly type if needed. |
122 | 7 | ReturnType = cast<Expr>(Code[0])->getType(); |
123 | 7 | } |
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 | 26 | SourceLocation ExtractedDeclLocation = |
137 | 26 | 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. |
142 | 26 | PrintingPolicy PP = AST.getPrintingPolicy(); |
143 | | // FIXME: PP.UseStdFunctionForLambda = true; |
144 | 26 | PP.SuppressStrongLifetime = true; |
145 | 26 | PP.SuppressLifetimeQualifiers = true; |
146 | 26 | PP.SuppressUnwrittenScope = true; |
147 | | |
148 | 26 | ExtractionSemicolonPolicy Semicolons = ExtractionSemicolonPolicy::compute( |
149 | 26 | Code[Code.size() - 1], ExtractedRange, SM, LangOpts); |
150 | 26 | AtomicChange Change(SM, ExtractedDeclLocation); |
151 | | // Create the replacement for the extracted declaration. |
152 | 26 | { |
153 | 26 | std::string ExtractedCode; |
154 | 26 | llvm::raw_string_ostream OS(ExtractedCode); |
155 | | // FIXME: Use 'inline' in header. |
156 | 26 | OS << "static "; |
157 | 26 | ReturnType.print(OS, PP, DeclName); |
158 | 26 | OS << '('; |
159 | | // FIXME: Arguments. |
160 | 26 | OS << ')'; |
161 | | |
162 | | // Function body. |
163 | 26 | OS << " {\n"; |
164 | 26 | if (IsExpr && !ReturnType->isVoidType()7 ) |
165 | 7 | OS << "return "; |
166 | 26 | OS << ExtractedCodeRewriter.getRewrittenText(ExtractedRange); |
167 | 26 | if (Semicolons.isNeededInExtractedFunction()) |
168 | 8 | OS << ';'; |
169 | 26 | OS << "\n}\n\n"; |
170 | 26 | auto Err = Change.insert(SM, ExtractedDeclLocation, OS.str()); |
171 | 26 | if (Err) |
172 | 0 | return std::move(Err); |
173 | 26 | } |
174 | | |
175 | | // Create the replacement for the call to the extracted declaration. |
176 | 26 | { |
177 | 26 | std::string ReplacedCode; |
178 | 26 | llvm::raw_string_ostream OS(ReplacedCode); |
179 | | |
180 | 26 | OS << DeclName << '('; |
181 | | // FIXME: Forward arguments. |
182 | 26 | OS << ')'; |
183 | 26 | if (Semicolons.isNeededInOriginalFunction()) |
184 | 19 | OS << ';'; |
185 | | |
186 | 26 | auto Err = Change.replace( |
187 | 26 | SM, CharSourceRange::getTokenRange(ExtractedRange), OS.str()); |
188 | 26 | if (Err) |
189 | 0 | return std::move(Err); |
190 | 26 | } |
191 | | |
192 | | // FIXME: Add support for assocciated symbol location to AtomicChange to mark |
193 | | // the ranges of the name of the extracted declaration. |
194 | 26 | return AtomicChanges{std::move(Change)}; |
195 | 26 | } |
196 | | |
197 | | } // end namespace tooling |
198 | | } // end namespace clang |