forked from OSchip/llvm-project
				
			
		
			
				
	
	
		
			199 lines
		
	
	
		
			6.5 KiB
		
	
	
	
		
			C++
		
	
	
	
			
		
		
	
	
			199 lines
		
	
	
		
			6.5 KiB
		
	
	
	
		
			C++
		
	
	
	
| //===--- Extract.cpp - Clang refactoring library --------------------------===//
 | |
| //
 | |
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
 | |
| // See https://llvm.org/LICENSE.txt for license information.
 | |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
 | |
| //
 | |
| //===----------------------------------------------------------------------===//
 | |
| ///
 | |
| /// \file
 | |
| /// Implements the "extract" refactoring that can pull code into
 | |
| /// new functions, methods or declare new variables.
 | |
| ///
 | |
| //===----------------------------------------------------------------------===//
 | |
| 
 | |
| #include "clang/Tooling/Refactoring/Extract/Extract.h"
 | |
| #include "clang/AST/ASTContext.h"
 | |
| #include "clang/AST/DeclCXX.h"
 | |
| #include "clang/AST/Expr.h"
 | |
| #include "clang/AST/ExprObjC.h"
 | |
| #include "clang/Rewrite/Core/Rewriter.h"
 | |
| #include "clang/Tooling/Refactoring/Extract/SourceExtraction.h"
 | |
| 
 | |
| namespace clang {
 | |
| namespace tooling {
 | |
| 
 | |
| namespace {
 | |
| 
 | |
| /// Returns true if \c E is a simple literal or a reference expression that
 | |
| /// should not be extracted.
 | |
| bool isSimpleExpression(const Expr *E) {
 | |
|   if (!E)
 | |
|     return false;
 | |
|   switch (E->IgnoreParenCasts()->getStmtClass()) {
 | |
|   case Stmt::DeclRefExprClass:
 | |
|   case Stmt::PredefinedExprClass:
 | |
|   case Stmt::IntegerLiteralClass:
 | |
|   case Stmt::FloatingLiteralClass:
 | |
|   case Stmt::ImaginaryLiteralClass:
 | |
|   case Stmt::CharacterLiteralClass:
 | |
|   case Stmt::StringLiteralClass:
 | |
|     return true;
 | |
|   default:
 | |
|     return false;
 | |
|   }
 | |
| }
 | |
| 
 | |
| SourceLocation computeFunctionExtractionLocation(const Decl *D) {
 | |
|   if (isa<CXXMethodDecl>(D)) {
 | |
|     // Code from method that is defined in class body should be extracted to a
 | |
|     // function defined just before the class.
 | |
|     while (const auto *RD = dyn_cast<CXXRecordDecl>(D->getLexicalDeclContext()))
 | |
|       D = RD;
 | |
|   }
 | |
|   return D->getBeginLoc();
 | |
| }
 | |
| 
 | |
| } // end anonymous namespace
 | |
| 
 | |
| const RefactoringDescriptor &ExtractFunction::describe() {
 | |
|   static const RefactoringDescriptor Descriptor = {
 | |
|       "extract-function",
 | |
|       "Extract Function",
 | |
|       "(WIP action; use with caution!) Extracts code into a new function",
 | |
|   };
 | |
|   return Descriptor;
 | |
| }
 | |
| 
 | |
| Expected<ExtractFunction>
 | |
| ExtractFunction::initiate(RefactoringRuleContext &Context,
 | |
|                           CodeRangeASTSelection Code,
 | |
|                           Optional<std::string> DeclName) {
 | |
|   // We would like to extract code out of functions/methods/blocks.
 | |
|   // Prohibit extraction from things like global variable / field
 | |
|   // initializers and other top-level expressions.
 | |
|   if (!Code.isInFunctionLikeBodyOfCode())
 | |
|     return Context.createDiagnosticError(
 | |
|         diag::err_refactor_code_outside_of_function);
 | |
| 
 | |
|   if (Code.size() == 1) {
 | |
|     // Avoid extraction of simple literals and references.
 | |
|     if (isSimpleExpression(dyn_cast<Expr>(Code[0])))
 | |
|       return Context.createDiagnosticError(
 | |
|           diag::err_refactor_extract_simple_expression);
 | |
| 
 | |
|     // Property setters can't be extracted.
 | |
|     if (const auto *PRE = dyn_cast<ObjCPropertyRefExpr>(Code[0])) {
 | |
|       if (!PRE->isMessagingGetter())
 | |
|         return Context.createDiagnosticError(
 | |
|             diag::err_refactor_extract_prohibited_expression);
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   return ExtractFunction(std::move(Code), DeclName);
 | |
| }
 | |
| 
 | |
| // FIXME: Support C++ method extraction.
 | |
| // FIXME: Support Objective-C method extraction.
 | |
| Expected<AtomicChanges>
 | |
| ExtractFunction::createSourceReplacements(RefactoringRuleContext &Context) {
 | |
|   const Decl *ParentDecl = Code.getFunctionLikeNearestParent();
 | |
|   assert(ParentDecl && "missing parent");
 | |
| 
 | |
|   // Compute the source range of the code that should be extracted.
 | |
|   SourceRange ExtractedRange(Code[0]->getBeginLoc(),
 | |
|                              Code[Code.size() - 1]->getEndLoc());
 | |
|   // FIXME (Alex L): Add code that accounts for macro locations.
 | |
| 
 | |
|   ASTContext &AST = Context.getASTContext();
 | |
|   SourceManager &SM = AST.getSourceManager();
 | |
|   const LangOptions &LangOpts = AST.getLangOpts();
 | |
|   Rewriter ExtractedCodeRewriter(SM, LangOpts);
 | |
| 
 | |
|   // FIXME: Capture used variables.
 | |
| 
 | |
|   // Compute the return type.
 | |
|   QualType ReturnType = AST.VoidTy;
 | |
|   // FIXME (Alex L): Account for the return statement in extracted code.
 | |
|   // FIXME (Alex L): Check for lexical expression instead.
 | |
|   bool IsExpr = Code.size() == 1 && isa<Expr>(Code[0]);
 | |
|   if (IsExpr) {
 | |
|     // FIXME (Alex L): Get a more user-friendly type if needed.
 | |
|     ReturnType = cast<Expr>(Code[0])->getType();
 | |
|   }
 | |
| 
 | |
|   // FIXME: Rewrite the extracted code performing any required adjustments.
 | |
| 
 | |
|   // FIXME: Capture any field if necessary (method -> function extraction).
 | |
| 
 | |
|   // FIXME: Sort captured variables by name.
 | |
| 
 | |
|   // FIXME: Capture 'this' / 'self' if necessary.
 | |
| 
 | |
|   // FIXME: Compute the actual parameter types.
 | |
| 
 | |
|   // Compute the location of the extracted declaration.
 | |
|   SourceLocation ExtractedDeclLocation =
 | |
|       computeFunctionExtractionLocation(ParentDecl);
 | |
|   // FIXME: Adjust the location to account for any preceding comments.
 | |
| 
 | |
|   // FIXME: Adjust with PP awareness like in Sema to get correct 'bool'
 | |
|   // treatment.
 | |
|   PrintingPolicy PP = AST.getPrintingPolicy();
 | |
|   // FIXME: PP.UseStdFunctionForLambda = true;
 | |
|   PP.SuppressStrongLifetime = true;
 | |
|   PP.SuppressLifetimeQualifiers = true;
 | |
|   PP.SuppressUnwrittenScope = true;
 | |
| 
 | |
|   ExtractionSemicolonPolicy Semicolons = ExtractionSemicolonPolicy::compute(
 | |
|       Code[Code.size() - 1], ExtractedRange, SM, LangOpts);
 | |
|   AtomicChange Change(SM, ExtractedDeclLocation);
 | |
|   // Create the replacement for the extracted declaration.
 | |
|   {
 | |
|     std::string ExtractedCode;
 | |
|     llvm::raw_string_ostream OS(ExtractedCode);
 | |
|     // FIXME: Use 'inline' in header.
 | |
|     OS << "static ";
 | |
|     ReturnType.print(OS, PP, DeclName);
 | |
|     OS << '(';
 | |
|     // FIXME: Arguments.
 | |
|     OS << ')';
 | |
| 
 | |
|     // Function body.
 | |
|     OS << " {\n";
 | |
|     if (IsExpr && !ReturnType->isVoidType())
 | |
|       OS << "return ";
 | |
|     OS << ExtractedCodeRewriter.getRewrittenText(ExtractedRange);
 | |
|     if (Semicolons.isNeededInExtractedFunction())
 | |
|       OS << ';';
 | |
|     OS << "\n}\n\n";
 | |
|     auto Err = Change.insert(SM, ExtractedDeclLocation, OS.str());
 | |
|     if (Err)
 | |
|       return std::move(Err);
 | |
|   }
 | |
| 
 | |
|   // Create the replacement for the call to the extracted declaration.
 | |
|   {
 | |
|     std::string ReplacedCode;
 | |
|     llvm::raw_string_ostream OS(ReplacedCode);
 | |
| 
 | |
|     OS << DeclName << '(';
 | |
|     // FIXME: Forward arguments.
 | |
|     OS << ')';
 | |
|     if (Semicolons.isNeededInOriginalFunction())
 | |
|       OS << ';';
 | |
| 
 | |
|     auto Err = Change.replace(
 | |
|         SM, CharSourceRange::getTokenRange(ExtractedRange), OS.str());
 | |
|     if (Err)
 | |
|       return std::move(Err);
 | |
|   }
 | |
| 
 | |
|   // FIXME: Add support for assocciated symbol location to AtomicChange to mark
 | |
|   // the ranges of the name of the extracted declaration.
 | |
|   return AtomicChanges{std::move(Change)};
 | |
| }
 | |
| 
 | |
| } // end namespace tooling
 | |
| } // end namespace clang
 |