xref: /freebsd-src/contrib/llvm-project/clang/lib/Tooling/Refactoring/Extract/Extract.cpp (revision bdd1243df58e60e85101c09001d9812a789b6bc4)
10b57cec5SDimitry Andric //===--- Extract.cpp - Clang refactoring library --------------------------===//
20b57cec5SDimitry Andric //
30b57cec5SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
40b57cec5SDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
50b57cec5SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
60b57cec5SDimitry Andric //
70b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
80b57cec5SDimitry Andric ///
90b57cec5SDimitry Andric /// \file
100b57cec5SDimitry Andric /// Implements the "extract" refactoring that can pull code into
110b57cec5SDimitry Andric /// new functions, methods or declare new variables.
120b57cec5SDimitry Andric ///
130b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
140b57cec5SDimitry Andric 
150b57cec5SDimitry Andric #include "clang/Tooling/Refactoring/Extract/Extract.h"
160b57cec5SDimitry Andric #include "clang/AST/ASTContext.h"
170b57cec5SDimitry Andric #include "clang/AST/DeclCXX.h"
180b57cec5SDimitry Andric #include "clang/AST/Expr.h"
190b57cec5SDimitry Andric #include "clang/AST/ExprObjC.h"
200b57cec5SDimitry Andric #include "clang/Rewrite/Core/Rewriter.h"
21a7dea167SDimitry Andric #include "clang/Tooling/Refactoring/Extract/SourceExtraction.h"
22*bdd1243dSDimitry Andric #include <optional>
230b57cec5SDimitry Andric 
240b57cec5SDimitry Andric namespace clang {
250b57cec5SDimitry Andric namespace tooling {
260b57cec5SDimitry Andric 
270b57cec5SDimitry Andric namespace {
280b57cec5SDimitry Andric 
290b57cec5SDimitry Andric /// Returns true if \c E is a simple literal or a reference expression that
300b57cec5SDimitry Andric /// should not be extracted.
isSimpleExpression(const Expr * E)310b57cec5SDimitry Andric bool isSimpleExpression(const Expr *E) {
320b57cec5SDimitry Andric   if (!E)
330b57cec5SDimitry Andric     return false;
340b57cec5SDimitry Andric   switch (E->IgnoreParenCasts()->getStmtClass()) {
350b57cec5SDimitry Andric   case Stmt::DeclRefExprClass:
360b57cec5SDimitry Andric   case Stmt::PredefinedExprClass:
370b57cec5SDimitry Andric   case Stmt::IntegerLiteralClass:
380b57cec5SDimitry Andric   case Stmt::FloatingLiteralClass:
390b57cec5SDimitry Andric   case Stmt::ImaginaryLiteralClass:
400b57cec5SDimitry Andric   case Stmt::CharacterLiteralClass:
410b57cec5SDimitry Andric   case Stmt::StringLiteralClass:
420b57cec5SDimitry Andric     return true;
430b57cec5SDimitry Andric   default:
440b57cec5SDimitry Andric     return false;
450b57cec5SDimitry Andric   }
460b57cec5SDimitry Andric }
470b57cec5SDimitry Andric 
computeFunctionExtractionLocation(const Decl * D)480b57cec5SDimitry Andric SourceLocation computeFunctionExtractionLocation(const Decl *D) {
490b57cec5SDimitry Andric   if (isa<CXXMethodDecl>(D)) {
500b57cec5SDimitry Andric     // Code from method that is defined in class body should be extracted to a
510b57cec5SDimitry Andric     // function defined just before the class.
520b57cec5SDimitry Andric     while (const auto *RD = dyn_cast<CXXRecordDecl>(D->getLexicalDeclContext()))
530b57cec5SDimitry Andric       D = RD;
540b57cec5SDimitry Andric   }
550b57cec5SDimitry Andric   return D->getBeginLoc();
560b57cec5SDimitry Andric }
570b57cec5SDimitry Andric 
580b57cec5SDimitry Andric } // end anonymous namespace
590b57cec5SDimitry Andric 
describe()600b57cec5SDimitry Andric const RefactoringDescriptor &ExtractFunction::describe() {
610b57cec5SDimitry Andric   static const RefactoringDescriptor Descriptor = {
620b57cec5SDimitry Andric       "extract-function",
630b57cec5SDimitry Andric       "Extract Function",
640b57cec5SDimitry Andric       "(WIP action; use with caution!) Extracts code into a new function",
650b57cec5SDimitry Andric   };
660b57cec5SDimitry Andric   return Descriptor;
670b57cec5SDimitry Andric }
680b57cec5SDimitry Andric 
690b57cec5SDimitry Andric Expected<ExtractFunction>
initiate(RefactoringRuleContext & Context,CodeRangeASTSelection Code,std::optional<std::string> DeclName)700b57cec5SDimitry Andric ExtractFunction::initiate(RefactoringRuleContext &Context,
710b57cec5SDimitry Andric                           CodeRangeASTSelection Code,
72*bdd1243dSDimitry Andric                           std::optional<std::string> DeclName) {
730b57cec5SDimitry Andric   // We would like to extract code out of functions/methods/blocks.
740b57cec5SDimitry Andric   // Prohibit extraction from things like global variable / field
750b57cec5SDimitry Andric   // initializers and other top-level expressions.
760b57cec5SDimitry Andric   if (!Code.isInFunctionLikeBodyOfCode())
770b57cec5SDimitry Andric     return Context.createDiagnosticError(
780b57cec5SDimitry Andric         diag::err_refactor_code_outside_of_function);
790b57cec5SDimitry Andric 
800b57cec5SDimitry Andric   if (Code.size() == 1) {
810b57cec5SDimitry Andric     // Avoid extraction of simple literals and references.
820b57cec5SDimitry Andric     if (isSimpleExpression(dyn_cast<Expr>(Code[0])))
830b57cec5SDimitry Andric       return Context.createDiagnosticError(
840b57cec5SDimitry Andric           diag::err_refactor_extract_simple_expression);
850b57cec5SDimitry Andric 
860b57cec5SDimitry Andric     // Property setters can't be extracted.
870b57cec5SDimitry Andric     if (const auto *PRE = dyn_cast<ObjCPropertyRefExpr>(Code[0])) {
880b57cec5SDimitry Andric       if (!PRE->isMessagingGetter())
890b57cec5SDimitry Andric         return Context.createDiagnosticError(
900b57cec5SDimitry Andric             diag::err_refactor_extract_prohibited_expression);
910b57cec5SDimitry Andric     }
920b57cec5SDimitry Andric   }
930b57cec5SDimitry Andric 
940b57cec5SDimitry Andric   return ExtractFunction(std::move(Code), DeclName);
950b57cec5SDimitry Andric }
960b57cec5SDimitry Andric 
970b57cec5SDimitry Andric // FIXME: Support C++ method extraction.
980b57cec5SDimitry Andric // FIXME: Support Objective-C method extraction.
990b57cec5SDimitry Andric Expected<AtomicChanges>
createSourceReplacements(RefactoringRuleContext & Context)1000b57cec5SDimitry Andric ExtractFunction::createSourceReplacements(RefactoringRuleContext &Context) {
1010b57cec5SDimitry Andric   const Decl *ParentDecl = Code.getFunctionLikeNearestParent();
1020b57cec5SDimitry Andric   assert(ParentDecl && "missing parent");
1030b57cec5SDimitry Andric 
1040b57cec5SDimitry Andric   // Compute the source range of the code that should be extracted.
1050b57cec5SDimitry Andric   SourceRange ExtractedRange(Code[0]->getBeginLoc(),
1060b57cec5SDimitry Andric                              Code[Code.size() - 1]->getEndLoc());
1070b57cec5SDimitry Andric   // FIXME (Alex L): Add code that accounts for macro locations.
1080b57cec5SDimitry Andric 
1090b57cec5SDimitry Andric   ASTContext &AST = Context.getASTContext();
1100b57cec5SDimitry Andric   SourceManager &SM = AST.getSourceManager();
1110b57cec5SDimitry Andric   const LangOptions &LangOpts = AST.getLangOpts();
1120b57cec5SDimitry Andric   Rewriter ExtractedCodeRewriter(SM, LangOpts);
1130b57cec5SDimitry Andric 
1140b57cec5SDimitry Andric   // FIXME: Capture used variables.
1150b57cec5SDimitry Andric 
1160b57cec5SDimitry Andric   // Compute the return type.
1170b57cec5SDimitry Andric   QualType ReturnType = AST.VoidTy;
1180b57cec5SDimitry Andric   // FIXME (Alex L): Account for the return statement in extracted code.
1190b57cec5SDimitry Andric   // FIXME (Alex L): Check for lexical expression instead.
1200b57cec5SDimitry Andric   bool IsExpr = Code.size() == 1 && isa<Expr>(Code[0]);
1210b57cec5SDimitry Andric   if (IsExpr) {
1220b57cec5SDimitry Andric     // FIXME (Alex L): Get a more user-friendly type if needed.
1230b57cec5SDimitry Andric     ReturnType = cast<Expr>(Code[0])->getType();
1240b57cec5SDimitry Andric   }
1250b57cec5SDimitry Andric 
1260b57cec5SDimitry Andric   // FIXME: Rewrite the extracted code performing any required adjustments.
1270b57cec5SDimitry Andric 
1280b57cec5SDimitry Andric   // FIXME: Capture any field if necessary (method -> function extraction).
1290b57cec5SDimitry Andric 
1300b57cec5SDimitry Andric   // FIXME: Sort captured variables by name.
1310b57cec5SDimitry Andric 
1320b57cec5SDimitry Andric   // FIXME: Capture 'this' / 'self' if necessary.
1330b57cec5SDimitry Andric 
1340b57cec5SDimitry Andric   // FIXME: Compute the actual parameter types.
1350b57cec5SDimitry Andric 
1360b57cec5SDimitry Andric   // Compute the location of the extracted declaration.
1370b57cec5SDimitry Andric   SourceLocation ExtractedDeclLocation =
1380b57cec5SDimitry Andric       computeFunctionExtractionLocation(ParentDecl);
1390b57cec5SDimitry Andric   // FIXME: Adjust the location to account for any preceding comments.
1400b57cec5SDimitry Andric 
1410b57cec5SDimitry Andric   // FIXME: Adjust with PP awareness like in Sema to get correct 'bool'
1420b57cec5SDimitry Andric   // treatment.
1430b57cec5SDimitry Andric   PrintingPolicy PP = AST.getPrintingPolicy();
1440b57cec5SDimitry Andric   // FIXME: PP.UseStdFunctionForLambda = true;
1450b57cec5SDimitry Andric   PP.SuppressStrongLifetime = true;
1460b57cec5SDimitry Andric   PP.SuppressLifetimeQualifiers = true;
1470b57cec5SDimitry Andric   PP.SuppressUnwrittenScope = true;
1480b57cec5SDimitry Andric 
1490b57cec5SDimitry Andric   ExtractionSemicolonPolicy Semicolons = ExtractionSemicolonPolicy::compute(
1500b57cec5SDimitry Andric       Code[Code.size() - 1], ExtractedRange, SM, LangOpts);
1510b57cec5SDimitry Andric   AtomicChange Change(SM, ExtractedDeclLocation);
1520b57cec5SDimitry Andric   // Create the replacement for the extracted declaration.
1530b57cec5SDimitry Andric   {
1540b57cec5SDimitry Andric     std::string ExtractedCode;
1550b57cec5SDimitry Andric     llvm::raw_string_ostream OS(ExtractedCode);
1560b57cec5SDimitry Andric     // FIXME: Use 'inline' in header.
1570b57cec5SDimitry Andric     OS << "static ";
1580b57cec5SDimitry Andric     ReturnType.print(OS, PP, DeclName);
1590b57cec5SDimitry Andric     OS << '(';
1600b57cec5SDimitry Andric     // FIXME: Arguments.
1610b57cec5SDimitry Andric     OS << ')';
1620b57cec5SDimitry Andric 
1630b57cec5SDimitry Andric     // Function body.
1640b57cec5SDimitry Andric     OS << " {\n";
1650b57cec5SDimitry Andric     if (IsExpr && !ReturnType->isVoidType())
1660b57cec5SDimitry Andric       OS << "return ";
1670b57cec5SDimitry Andric     OS << ExtractedCodeRewriter.getRewrittenText(ExtractedRange);
1680b57cec5SDimitry Andric     if (Semicolons.isNeededInExtractedFunction())
1690b57cec5SDimitry Andric       OS << ';';
1700b57cec5SDimitry Andric     OS << "\n}\n\n";
1710b57cec5SDimitry Andric     auto Err = Change.insert(SM, ExtractedDeclLocation, OS.str());
1720b57cec5SDimitry Andric     if (Err)
1730b57cec5SDimitry Andric       return std::move(Err);
1740b57cec5SDimitry Andric   }
1750b57cec5SDimitry Andric 
1760b57cec5SDimitry Andric   // Create the replacement for the call to the extracted declaration.
1770b57cec5SDimitry Andric   {
1780b57cec5SDimitry Andric     std::string ReplacedCode;
1790b57cec5SDimitry Andric     llvm::raw_string_ostream OS(ReplacedCode);
1800b57cec5SDimitry Andric 
1810b57cec5SDimitry Andric     OS << DeclName << '(';
1820b57cec5SDimitry Andric     // FIXME: Forward arguments.
1830b57cec5SDimitry Andric     OS << ')';
1840b57cec5SDimitry Andric     if (Semicolons.isNeededInOriginalFunction())
1850b57cec5SDimitry Andric       OS << ';';
1860b57cec5SDimitry Andric 
1870b57cec5SDimitry Andric     auto Err = Change.replace(
1880b57cec5SDimitry Andric         SM, CharSourceRange::getTokenRange(ExtractedRange), OS.str());
1890b57cec5SDimitry Andric     if (Err)
1900b57cec5SDimitry Andric       return std::move(Err);
1910b57cec5SDimitry Andric   }
1920b57cec5SDimitry Andric 
1930b57cec5SDimitry Andric   // FIXME: Add support for assocciated symbol location to AtomicChange to mark
1940b57cec5SDimitry Andric   // the ranges of the name of the extracted declaration.
1950b57cec5SDimitry Andric   return AtomicChanges{std::move(Change)};
1960b57cec5SDimitry Andric }
1970b57cec5SDimitry Andric 
1980b57cec5SDimitry Andric } // end namespace tooling
1990b57cec5SDimitry Andric } // end namespace clang
200