brintos

brintos / llvm-project-archived public Read only

0
0
Text · 6.6 KiB · d437f4c Raw
200 lines · cpp
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-exception6//7//===----------------------------------------------------------------------===//8///9/// \file10/// Implements the "extract" refactoring that can pull code into11/// 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#include <optional>23 24namespace clang {25namespace tooling {26 27namespace {28 29/// Returns true if \c E is a simple literal or a reference expression that30/// should not be extracted.31bool 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;45  }46}47 48SourceLocation computeFunctionExtractionLocation(const Decl *D) {49  if (isa<CXXMethodDecl>(D)) {50    // Code from method that is defined in class body should be extracted to a51    // function defined just before the class.52    while (const auto *RD = dyn_cast<CXXRecordDecl>(D->getLexicalDeclContext()))53      D = RD;54  }55  return D->getBeginLoc();56}57 58} // end anonymous namespace59 60const 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",65  };66  return Descriptor;67}68 69Expected<ExtractFunction>70ExtractFunction::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 / field75  // initializers and other top-level expressions.76  if (!Code.isInFunctionLikeBodyOfCode())77    return Context.createDiagnosticError(78        diag::err_refactor_code_outside_of_function);79 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);85 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);91    }92  }93 94  return ExtractFunction(std::move(Code), DeclName);95}96 97// FIXME: Support C++ method extraction.98// FIXME: Support Objective-C method extraction.99Expected<AtomicChanges>100ExtractFunction::createSourceReplacements(RefactoringRuleContext &Context) {101  const Decl *ParentDecl = Code.getFunctionLikeNearestParent();102  assert(ParentDecl && "missing parent");103 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.108 109  ASTContext &AST = Context.getASTContext();110  SourceManager &SM = AST.getSourceManager();111  const LangOptions &LangOpts = AST.getLangOpts();112  Rewriter ExtractedCodeRewriter(SM, LangOpts);113 114  // FIXME: Capture used variables.115 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();124  }125 126  // FIXME: Rewrite the extracted code performing any required adjustments.127 128  // FIXME: Capture any field if necessary (method -> function extraction).129 130  // FIXME: Sort captured variables by name.131 132  // FIXME: Capture 'this' / 'self' if necessary.133 134  // FIXME: Compute the actual parameter types.135 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.140 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;148 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.153  {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 << ')';162 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);174  }175 176  // Create the replacement for the call to the extracted declaration.177  {178    std::string ReplacedCode;179    llvm::raw_string_ostream OS(ReplacedCode);180 181    OS << DeclName << '(';182    // FIXME: Forward arguments.183    OS << ')';184    if (Semicolons.isNeededInOriginalFunction())185      OS << ';';186 187    auto Err = Change.replace(188        SM, CharSourceRange::getTokenRange(ExtractedRange), OS.str());189    if (Err)190      return std::move(Err);191  }192 193  // FIXME: Add support for assocciated symbol location to AtomicChange to mark194  // the ranges of the name of the extracted declaration.195  return AtomicChanges{std::move(Change)};196}197 198} // end namespace tooling199} // end namespace clang200