[Utils] Identity map module-level debug info on first use in CloneFunction* (#118627)
[llvm-project.git] / clang / lib / Tooling / Refactoring / Extract / Extract.cpp
blobd437f4c21f4776314683ff80b25ff9583cb8aa08
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 //===----------------------------------------------------------------------===//
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>
24 namespace clang {
25 namespace tooling {
27 namespace {
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;
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;
55 return D->getBeginLoc();
58 } // end anonymous namespace
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",
66 return Descriptor;
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]);
121 if (IsExpr) {
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'
142 // treatment.
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.
157 OS << "static ";
158 ReturnType.print(OS, PP, DeclName);
159 OS << '(';
160 // FIXME: Arguments.
161 OS << ')';
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);
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.
183 OS << ')';
184 if (Semicolons.isNeededInOriginalFunction())
185 OS << ';';
187 auto Err = Change.replace(
188 SM, CharSourceRange::getTokenRange(ExtractedRange), OS.str());
189 if (Err)
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