xref: /llvm-project/clang/lib/Tooling/Refactoring/Extract/Extract.cpp (revision 6ad0788c332bb2043142954d300c49ac3e537f34)
13aae3283SAlex Lorenz //===--- Extract.cpp - Clang refactoring library --------------------------===//
23aae3283SAlex Lorenz //
32946cd70SChandler Carruth // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
42946cd70SChandler Carruth // See https://llvm.org/LICENSE.txt for license information.
52946cd70SChandler Carruth // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
63aae3283SAlex Lorenz //
73aae3283SAlex Lorenz //===----------------------------------------------------------------------===//
83aae3283SAlex Lorenz ///
93aae3283SAlex Lorenz /// \file
109fc8faf9SAdrian Prantl /// Implements the "extract" refactoring that can pull code into
113aae3283SAlex Lorenz /// new functions, methods or declare new variables.
123aae3283SAlex Lorenz ///
133aae3283SAlex Lorenz //===----------------------------------------------------------------------===//
143aae3283SAlex Lorenz 
153aae3283SAlex Lorenz #include "clang/Tooling/Refactoring/Extract/Extract.h"
163aae3283SAlex Lorenz #include "clang/AST/ASTContext.h"
173aae3283SAlex Lorenz #include "clang/AST/DeclCXX.h"
183aae3283SAlex Lorenz #include "clang/AST/Expr.h"
193aae3283SAlex Lorenz #include "clang/AST/ExprObjC.h"
203aae3283SAlex Lorenz #include "clang/Rewrite/Core/Rewriter.h"
2152d0cfc9SShaurya Gupta #include "clang/Tooling/Refactoring/Extract/SourceExtraction.h"
22a1580d7bSKazu Hirata #include <optional>
233aae3283SAlex Lorenz 
243aae3283SAlex Lorenz namespace clang {
253aae3283SAlex Lorenz namespace tooling {
263aae3283SAlex Lorenz 
273aae3283SAlex Lorenz namespace {
283aae3283SAlex Lorenz 
293aae3283SAlex Lorenz /// Returns true if \c E is a simple literal or a reference expression that
303aae3283SAlex Lorenz /// should not be extracted.
isSimpleExpression(const Expr * E)313aae3283SAlex Lorenz bool isSimpleExpression(const Expr *E) {
323aae3283SAlex Lorenz   if (!E)
333aae3283SAlex Lorenz     return false;
343aae3283SAlex Lorenz   switch (E->IgnoreParenCasts()->getStmtClass()) {
353aae3283SAlex Lorenz   case Stmt::DeclRefExprClass:
363aae3283SAlex Lorenz   case Stmt::PredefinedExprClass:
373aae3283SAlex Lorenz   case Stmt::IntegerLiteralClass:
383aae3283SAlex Lorenz   case Stmt::FloatingLiteralClass:
393aae3283SAlex Lorenz   case Stmt::ImaginaryLiteralClass:
403aae3283SAlex Lorenz   case Stmt::CharacterLiteralClass:
413aae3283SAlex Lorenz   case Stmt::StringLiteralClass:
423aae3283SAlex Lorenz     return true;
433aae3283SAlex Lorenz   default:
443aae3283SAlex Lorenz     return false;
453aae3283SAlex Lorenz   }
463aae3283SAlex Lorenz }
473aae3283SAlex Lorenz 
computeFunctionExtractionLocation(const Decl * D)483aae3283SAlex Lorenz SourceLocation computeFunctionExtractionLocation(const Decl *D) {
493aae3283SAlex Lorenz   if (isa<CXXMethodDecl>(D)) {
503aae3283SAlex Lorenz     // Code from method that is defined in class body should be extracted to a
513aae3283SAlex Lorenz     // function defined just before the class.
523aae3283SAlex Lorenz     while (const auto *RD = dyn_cast<CXXRecordDecl>(D->getLexicalDeclContext()))
533aae3283SAlex Lorenz       D = RD;
543aae3283SAlex Lorenz   }
55f2ceec48SStephen Kelly   return D->getBeginLoc();
563aae3283SAlex Lorenz }
573aae3283SAlex Lorenz 
583aae3283SAlex Lorenz } // end anonymous namespace
593aae3283SAlex Lorenz 
describe()603aae3283SAlex Lorenz const RefactoringDescriptor &ExtractFunction::describe() {
613aae3283SAlex Lorenz   static const RefactoringDescriptor Descriptor = {
623aae3283SAlex Lorenz       "extract-function",
633aae3283SAlex Lorenz       "Extract Function",
643aae3283SAlex Lorenz       "(WIP action; use with caution!) Extracts code into a new function",
653aae3283SAlex Lorenz   };
663aae3283SAlex Lorenz   return Descriptor;
673aae3283SAlex Lorenz }
683aae3283SAlex Lorenz 
693aae3283SAlex Lorenz Expected<ExtractFunction>
initiate(RefactoringRuleContext & Context,CodeRangeASTSelection Code,std::optional<std::string> DeclName)703aae3283SAlex Lorenz ExtractFunction::initiate(RefactoringRuleContext &Context,
713aae3283SAlex Lorenz                           CodeRangeASTSelection Code,
72*6ad0788cSKazu Hirata                           std::optional<std::string> DeclName) {
733aae3283SAlex Lorenz   // We would like to extract code out of functions/methods/blocks.
743aae3283SAlex Lorenz   // Prohibit extraction from things like global variable / field
753aae3283SAlex Lorenz   // initializers and other top-level expressions.
763aae3283SAlex Lorenz   if (!Code.isInFunctionLikeBodyOfCode())
773aae3283SAlex Lorenz     return Context.createDiagnosticError(
783aae3283SAlex Lorenz         diag::err_refactor_code_outside_of_function);
793aae3283SAlex Lorenz 
803aae3283SAlex Lorenz   if (Code.size() == 1) {
813aae3283SAlex Lorenz     // Avoid extraction of simple literals and references.
823aae3283SAlex Lorenz     if (isSimpleExpression(dyn_cast<Expr>(Code[0])))
833aae3283SAlex Lorenz       return Context.createDiagnosticError(
843aae3283SAlex Lorenz           diag::err_refactor_extract_simple_expression);
853aae3283SAlex Lorenz 
863aae3283SAlex Lorenz     // Property setters can't be extracted.
873aae3283SAlex Lorenz     if (const auto *PRE = dyn_cast<ObjCPropertyRefExpr>(Code[0])) {
883aae3283SAlex Lorenz       if (!PRE->isMessagingGetter())
893aae3283SAlex Lorenz         return Context.createDiagnosticError(
903aae3283SAlex Lorenz             diag::err_refactor_extract_prohibited_expression);
913aae3283SAlex Lorenz     }
923aae3283SAlex Lorenz   }
933aae3283SAlex Lorenz 
943aae3283SAlex Lorenz   return ExtractFunction(std::move(Code), DeclName);
953aae3283SAlex Lorenz }
963aae3283SAlex Lorenz 
973aae3283SAlex Lorenz // FIXME: Support C++ method extraction.
983aae3283SAlex Lorenz // FIXME: Support Objective-C method extraction.
993aae3283SAlex Lorenz Expected<AtomicChanges>
createSourceReplacements(RefactoringRuleContext & Context)1003aae3283SAlex Lorenz ExtractFunction::createSourceReplacements(RefactoringRuleContext &Context) {
1013aae3283SAlex Lorenz   const Decl *ParentDecl = Code.getFunctionLikeNearestParent();
1023aae3283SAlex Lorenz   assert(ParentDecl && "missing parent");
1033aae3283SAlex Lorenz 
1043aae3283SAlex Lorenz   // Compute the source range of the code that should be extracted.
105f2ceec48SStephen Kelly   SourceRange ExtractedRange(Code[0]->getBeginLoc(),
1061c301dcbSStephen Kelly                              Code[Code.size() - 1]->getEndLoc());
1073aae3283SAlex Lorenz   // FIXME (Alex L): Add code that accounts for macro locations.
1083aae3283SAlex Lorenz 
1093aae3283SAlex Lorenz   ASTContext &AST = Context.getASTContext();
1103aae3283SAlex Lorenz   SourceManager &SM = AST.getSourceManager();
1113aae3283SAlex Lorenz   const LangOptions &LangOpts = AST.getLangOpts();
1123aae3283SAlex Lorenz   Rewriter ExtractedCodeRewriter(SM, LangOpts);
1133aae3283SAlex Lorenz 
1143aae3283SAlex Lorenz   // FIXME: Capture used variables.
1153aae3283SAlex Lorenz 
1163aae3283SAlex Lorenz   // Compute the return type.
1173aae3283SAlex Lorenz   QualType ReturnType = AST.VoidTy;
1183aae3283SAlex Lorenz   // FIXME (Alex L): Account for the return statement in extracted code.
1193aae3283SAlex Lorenz   // FIXME (Alex L): Check for lexical expression instead.
1203aae3283SAlex Lorenz   bool IsExpr = Code.size() == 1 && isa<Expr>(Code[0]);
1213aae3283SAlex Lorenz   if (IsExpr) {
1223aae3283SAlex Lorenz     // FIXME (Alex L): Get a more user-friendly type if needed.
1233aae3283SAlex Lorenz     ReturnType = cast<Expr>(Code[0])->getType();
1243aae3283SAlex Lorenz   }
1253aae3283SAlex Lorenz 
1263aae3283SAlex Lorenz   // FIXME: Rewrite the extracted code performing any required adjustments.
1273aae3283SAlex Lorenz 
1283aae3283SAlex Lorenz   // FIXME: Capture any field if necessary (method -> function extraction).
1293aae3283SAlex Lorenz 
1303aae3283SAlex Lorenz   // FIXME: Sort captured variables by name.
1313aae3283SAlex Lorenz 
1323aae3283SAlex Lorenz   // FIXME: Capture 'this' / 'self' if necessary.
1333aae3283SAlex Lorenz 
1343aae3283SAlex Lorenz   // FIXME: Compute the actual parameter types.
1353aae3283SAlex Lorenz 
1363aae3283SAlex Lorenz   // Compute the location of the extracted declaration.
1373aae3283SAlex Lorenz   SourceLocation ExtractedDeclLocation =
1383aae3283SAlex Lorenz       computeFunctionExtractionLocation(ParentDecl);
1393aae3283SAlex Lorenz   // FIXME: Adjust the location to account for any preceding comments.
1403aae3283SAlex Lorenz 
1413aae3283SAlex Lorenz   // FIXME: Adjust with PP awareness like in Sema to get correct 'bool'
1423aae3283SAlex Lorenz   // treatment.
1433aae3283SAlex Lorenz   PrintingPolicy PP = AST.getPrintingPolicy();
1443aae3283SAlex Lorenz   // FIXME: PP.UseStdFunctionForLambda = true;
1453aae3283SAlex Lorenz   PP.SuppressStrongLifetime = true;
1463aae3283SAlex Lorenz   PP.SuppressLifetimeQualifiers = true;
1473aae3283SAlex Lorenz   PP.SuppressUnwrittenScope = true;
1483aae3283SAlex Lorenz 
1493aae3283SAlex Lorenz   ExtractionSemicolonPolicy Semicolons = ExtractionSemicolonPolicy::compute(
1503aae3283SAlex Lorenz       Code[Code.size() - 1], ExtractedRange, SM, LangOpts);
1513aae3283SAlex Lorenz   AtomicChange Change(SM, ExtractedDeclLocation);
1523aae3283SAlex Lorenz   // Create the replacement for the extracted declaration.
1533aae3283SAlex Lorenz   {
1543aae3283SAlex Lorenz     std::string ExtractedCode;
1553aae3283SAlex Lorenz     llvm::raw_string_ostream OS(ExtractedCode);
1563aae3283SAlex Lorenz     // FIXME: Use 'inline' in header.
1573aae3283SAlex Lorenz     OS << "static ";
1583aae3283SAlex Lorenz     ReturnType.print(OS, PP, DeclName);
1593aae3283SAlex Lorenz     OS << '(';
1603aae3283SAlex Lorenz     // FIXME: Arguments.
1613aae3283SAlex Lorenz     OS << ')';
1623aae3283SAlex Lorenz 
1633aae3283SAlex Lorenz     // Function body.
1643aae3283SAlex Lorenz     OS << " {\n";
1653aae3283SAlex Lorenz     if (IsExpr && !ReturnType->isVoidType())
1663aae3283SAlex Lorenz       OS << "return ";
1673aae3283SAlex Lorenz     OS << ExtractedCodeRewriter.getRewrittenText(ExtractedRange);
1683aae3283SAlex Lorenz     if (Semicolons.isNeededInExtractedFunction())
1693aae3283SAlex Lorenz       OS << ';';
1703aae3283SAlex Lorenz     OS << "\n}\n\n";
1713aae3283SAlex Lorenz     auto Err = Change.insert(SM, ExtractedDeclLocation, OS.str());
1723aae3283SAlex Lorenz     if (Err)
1733aae3283SAlex Lorenz       return std::move(Err);
1743aae3283SAlex Lorenz   }
1753aae3283SAlex Lorenz 
1763aae3283SAlex Lorenz   // Create the replacement for the call to the extracted declaration.
1773aae3283SAlex Lorenz   {
1783aae3283SAlex Lorenz     std::string ReplacedCode;
1793aae3283SAlex Lorenz     llvm::raw_string_ostream OS(ReplacedCode);
1803aae3283SAlex Lorenz 
1813aae3283SAlex Lorenz     OS << DeclName << '(';
1823aae3283SAlex Lorenz     // FIXME: Forward arguments.
1833aae3283SAlex Lorenz     OS << ')';
1843aae3283SAlex Lorenz     if (Semicolons.isNeededInOriginalFunction())
1853aae3283SAlex Lorenz       OS << ';';
1863aae3283SAlex Lorenz 
1873aae3283SAlex Lorenz     auto Err = Change.replace(
1883aae3283SAlex Lorenz         SM, CharSourceRange::getTokenRange(ExtractedRange), OS.str());
1893aae3283SAlex Lorenz     if (Err)
1903aae3283SAlex Lorenz       return std::move(Err);
1913aae3283SAlex Lorenz   }
1923aae3283SAlex Lorenz 
1933aae3283SAlex Lorenz   // FIXME: Add support for assocciated symbol location to AtomicChange to mark
1943aae3283SAlex Lorenz   // the ranges of the name of the extracted declaration.
1953aae3283SAlex Lorenz   return AtomicChanges{std::move(Change)};
1963aae3283SAlex Lorenz }
1973aae3283SAlex Lorenz 
1983aae3283SAlex Lorenz } // end namespace tooling
1993aae3283SAlex Lorenz } // end namespace clang
200