Path: blob/main/contrib/llvm-project/clang/lib/Tooling/Refactoring/Extract/Extract.cpp
35295 views
//===--- Extract.cpp - Clang refactoring library --------------------------===//1//2// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.3// See https://llvm.org/LICENSE.txt for license information.4// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception5//6//===----------------------------------------------------------------------===//7///8/// \file9/// Implements the "extract" refactoring that can pull code into10/// new functions, methods or declare new variables.11///12//===----------------------------------------------------------------------===//1314#include "clang/Tooling/Refactoring/Extract/Extract.h"15#include "clang/AST/ASTContext.h"16#include "clang/AST/DeclCXX.h"17#include "clang/AST/Expr.h"18#include "clang/AST/ExprObjC.h"19#include "clang/Rewrite/Core/Rewriter.h"20#include "clang/Tooling/Refactoring/Extract/SourceExtraction.h"21#include <optional>2223namespace clang {24namespace tooling {2526namespace {2728/// Returns true if \c E is a simple literal or a reference expression that29/// should not be extracted.30bool isSimpleExpression(const Expr *E) {31if (!E)32return false;33switch (E->IgnoreParenCasts()->getStmtClass()) {34case Stmt::DeclRefExprClass:35case Stmt::PredefinedExprClass:36case Stmt::IntegerLiteralClass:37case Stmt::FloatingLiteralClass:38case Stmt::ImaginaryLiteralClass:39case Stmt::CharacterLiteralClass:40case Stmt::StringLiteralClass:41return true;42default:43return false;44}45}4647SourceLocation computeFunctionExtractionLocation(const Decl *D) {48if (isa<CXXMethodDecl>(D)) {49// Code from method that is defined in class body should be extracted to a50// function defined just before the class.51while (const auto *RD = dyn_cast<CXXRecordDecl>(D->getLexicalDeclContext()))52D = RD;53}54return D->getBeginLoc();55}5657} // end anonymous namespace5859const RefactoringDescriptor &ExtractFunction::describe() {60static const RefactoringDescriptor Descriptor = {61"extract-function",62"Extract Function",63"(WIP action; use with caution!) Extracts code into a new function",64};65return Descriptor;66}6768Expected<ExtractFunction>69ExtractFunction::initiate(RefactoringRuleContext &Context,70CodeRangeASTSelection Code,71std::optional<std::string> DeclName) {72// We would like to extract code out of functions/methods/blocks.73// Prohibit extraction from things like global variable / field74// initializers and other top-level expressions.75if (!Code.isInFunctionLikeBodyOfCode())76return Context.createDiagnosticError(77diag::err_refactor_code_outside_of_function);7879if (Code.size() == 1) {80// Avoid extraction of simple literals and references.81if (isSimpleExpression(dyn_cast<Expr>(Code[0])))82return Context.createDiagnosticError(83diag::err_refactor_extract_simple_expression);8485// Property setters can't be extracted.86if (const auto *PRE = dyn_cast<ObjCPropertyRefExpr>(Code[0])) {87if (!PRE->isMessagingGetter())88return Context.createDiagnosticError(89diag::err_refactor_extract_prohibited_expression);90}91}9293return ExtractFunction(std::move(Code), DeclName);94}9596// FIXME: Support C++ method extraction.97// FIXME: Support Objective-C method extraction.98Expected<AtomicChanges>99ExtractFunction::createSourceReplacements(RefactoringRuleContext &Context) {100const Decl *ParentDecl = Code.getFunctionLikeNearestParent();101assert(ParentDecl && "missing parent");102103// Compute the source range of the code that should be extracted.104SourceRange ExtractedRange(Code[0]->getBeginLoc(),105Code[Code.size() - 1]->getEndLoc());106// FIXME (Alex L): Add code that accounts for macro locations.107108ASTContext &AST = Context.getASTContext();109SourceManager &SM = AST.getSourceManager();110const LangOptions &LangOpts = AST.getLangOpts();111Rewriter ExtractedCodeRewriter(SM, LangOpts);112113// FIXME: Capture used variables.114115// Compute the return type.116QualType ReturnType = AST.VoidTy;117// FIXME (Alex L): Account for the return statement in extracted code.118// FIXME (Alex L): Check for lexical expression instead.119bool IsExpr = Code.size() == 1 && isa<Expr>(Code[0]);120if (IsExpr) {121// FIXME (Alex L): Get a more user-friendly type if needed.122ReturnType = cast<Expr>(Code[0])->getType();123}124125// FIXME: Rewrite the extracted code performing any required adjustments.126127// FIXME: Capture any field if necessary (method -> function extraction).128129// FIXME: Sort captured variables by name.130131// FIXME: Capture 'this' / 'self' if necessary.132133// FIXME: Compute the actual parameter types.134135// Compute the location of the extracted declaration.136SourceLocation ExtractedDeclLocation =137computeFunctionExtractionLocation(ParentDecl);138// FIXME: Adjust the location to account for any preceding comments.139140// FIXME: Adjust with PP awareness like in Sema to get correct 'bool'141// treatment.142PrintingPolicy PP = AST.getPrintingPolicy();143// FIXME: PP.UseStdFunctionForLambda = true;144PP.SuppressStrongLifetime = true;145PP.SuppressLifetimeQualifiers = true;146PP.SuppressUnwrittenScope = true;147148ExtractionSemicolonPolicy Semicolons = ExtractionSemicolonPolicy::compute(149Code[Code.size() - 1], ExtractedRange, SM, LangOpts);150AtomicChange Change(SM, ExtractedDeclLocation);151// Create the replacement for the extracted declaration.152{153std::string ExtractedCode;154llvm::raw_string_ostream OS(ExtractedCode);155// FIXME: Use 'inline' in header.156OS << "static ";157ReturnType.print(OS, PP, DeclName);158OS << '(';159// FIXME: Arguments.160OS << ')';161162// Function body.163OS << " {\n";164if (IsExpr && !ReturnType->isVoidType())165OS << "return ";166OS << ExtractedCodeRewriter.getRewrittenText(ExtractedRange);167if (Semicolons.isNeededInExtractedFunction())168OS << ';';169OS << "\n}\n\n";170auto Err = Change.insert(SM, ExtractedDeclLocation, OS.str());171if (Err)172return std::move(Err);173}174175// Create the replacement for the call to the extracted declaration.176{177std::string ReplacedCode;178llvm::raw_string_ostream OS(ReplacedCode);179180OS << DeclName << '(';181// FIXME: Forward arguments.182OS << ')';183if (Semicolons.isNeededInOriginalFunction())184OS << ';';185186auto Err = Change.replace(187SM, CharSourceRange::getTokenRange(ExtractedRange), OS.str());188if (Err)189return std::move(Err);190}191192// FIXME: Add support for assocciated symbol location to AtomicChange to mark193// the ranges of the name of the extracted declaration.194return AtomicChanges{std::move(Change)};195}196197} // end namespace tooling198} // end namespace clang199200201