Extract.cpp 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198
  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-exception
  6. //
  7. //===----------------------------------------------------------------------===//
  8. ///
  9. /// \file
  10. /// Implements the "extract" refactoring that can pull code into
  11. /// new functions, methods or declare new variables.
  12. ///
  13. //===----------------------------------------------------------------------===//
  14. #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. namespace clang {
  22. namespace tooling {
  23. namespace {
  24. /// Returns true if \c E is a simple literal or a reference expression that
  25. /// should not be extracted.
  26. bool isSimpleExpression(const Expr *E) {
  27. if (!E)
  28. return false;
  29. switch (E->IgnoreParenCasts()->getStmtClass()) {
  30. case Stmt::DeclRefExprClass:
  31. case Stmt::PredefinedExprClass:
  32. case Stmt::IntegerLiteralClass:
  33. case Stmt::FloatingLiteralClass:
  34. case Stmt::ImaginaryLiteralClass:
  35. case Stmt::CharacterLiteralClass:
  36. case Stmt::StringLiteralClass:
  37. return true;
  38. default:
  39. return false;
  40. }
  41. }
  42. SourceLocation computeFunctionExtractionLocation(const Decl *D) {
  43. if (isa<CXXMethodDecl>(D)) {
  44. // Code from method that is defined in class body should be extracted to a
  45. // function defined just before the class.
  46. while (const auto *RD = dyn_cast<CXXRecordDecl>(D->getLexicalDeclContext()))
  47. D = RD;
  48. }
  49. return D->getBeginLoc();
  50. }
  51. } // end anonymous namespace
  52. const RefactoringDescriptor &ExtractFunction::describe() {
  53. static const RefactoringDescriptor Descriptor = {
  54. "extract-function",
  55. "Extract Function",
  56. "(WIP action; use with caution!) Extracts code into a new function",
  57. };
  58. return Descriptor;
  59. }
  60. Expected<ExtractFunction>
  61. ExtractFunction::initiate(RefactoringRuleContext &Context,
  62. CodeRangeASTSelection Code,
  63. Optional<std::string> DeclName) {
  64. // We would like to extract code out of functions/methods/blocks.
  65. // Prohibit extraction from things like global variable / field
  66. // initializers and other top-level expressions.
  67. if (!Code.isInFunctionLikeBodyOfCode())
  68. return Context.createDiagnosticError(
  69. diag::err_refactor_code_outside_of_function);
  70. if (Code.size() == 1) {
  71. // Avoid extraction of simple literals and references.
  72. if (isSimpleExpression(dyn_cast<Expr>(Code[0])))
  73. return Context.createDiagnosticError(
  74. diag::err_refactor_extract_simple_expression);
  75. // Property setters can't be extracted.
  76. if (const auto *PRE = dyn_cast<ObjCPropertyRefExpr>(Code[0])) {
  77. if (!PRE->isMessagingGetter())
  78. return Context.createDiagnosticError(
  79. diag::err_refactor_extract_prohibited_expression);
  80. }
  81. }
  82. return ExtractFunction(std::move(Code), DeclName);
  83. }
  84. // FIXME: Support C++ method extraction.
  85. // FIXME: Support Objective-C method extraction.
  86. Expected<AtomicChanges>
  87. ExtractFunction::createSourceReplacements(RefactoringRuleContext &Context) {
  88. const Decl *ParentDecl = Code.getFunctionLikeNearestParent();
  89. assert(ParentDecl && "missing parent");
  90. // Compute the source range of the code that should be extracted.
  91. SourceRange ExtractedRange(Code[0]->getBeginLoc(),
  92. Code[Code.size() - 1]->getEndLoc());
  93. // FIXME (Alex L): Add code that accounts for macro locations.
  94. ASTContext &AST = Context.getASTContext();
  95. SourceManager &SM = AST.getSourceManager();
  96. const LangOptions &LangOpts = AST.getLangOpts();
  97. Rewriter ExtractedCodeRewriter(SM, LangOpts);
  98. // FIXME: Capture used variables.
  99. // Compute the return type.
  100. QualType ReturnType = AST.VoidTy;
  101. // FIXME (Alex L): Account for the return statement in extracted code.
  102. // FIXME (Alex L): Check for lexical expression instead.
  103. bool IsExpr = Code.size() == 1 && isa<Expr>(Code[0]);
  104. if (IsExpr) {
  105. // FIXME (Alex L): Get a more user-friendly type if needed.
  106. ReturnType = cast<Expr>(Code[0])->getType();
  107. }
  108. // FIXME: Rewrite the extracted code performing any required adjustments.
  109. // FIXME: Capture any field if necessary (method -> function extraction).
  110. // FIXME: Sort captured variables by name.
  111. // FIXME: Capture 'this' / 'self' if necessary.
  112. // FIXME: Compute the actual parameter types.
  113. // Compute the location of the extracted declaration.
  114. SourceLocation ExtractedDeclLocation =
  115. computeFunctionExtractionLocation(ParentDecl);
  116. // FIXME: Adjust the location to account for any preceding comments.
  117. // FIXME: Adjust with PP awareness like in Sema to get correct 'bool'
  118. // treatment.
  119. PrintingPolicy PP = AST.getPrintingPolicy();
  120. // FIXME: PP.UseStdFunctionForLambda = true;
  121. PP.SuppressStrongLifetime = true;
  122. PP.SuppressLifetimeQualifiers = true;
  123. PP.SuppressUnwrittenScope = true;
  124. ExtractionSemicolonPolicy Semicolons = ExtractionSemicolonPolicy::compute(
  125. Code[Code.size() - 1], ExtractedRange, SM, LangOpts);
  126. AtomicChange Change(SM, ExtractedDeclLocation);
  127. // Create the replacement for the extracted declaration.
  128. {
  129. std::string ExtractedCode;
  130. llvm::raw_string_ostream OS(ExtractedCode);
  131. // FIXME: Use 'inline' in header.
  132. OS << "static ";
  133. ReturnType.print(OS, PP, DeclName);
  134. OS << '(';
  135. // FIXME: Arguments.
  136. OS << ')';
  137. // Function body.
  138. OS << " {\n";
  139. if (IsExpr && !ReturnType->isVoidType())
  140. OS << "return ";
  141. OS << ExtractedCodeRewriter.getRewrittenText(ExtractedRange);
  142. if (Semicolons.isNeededInExtractedFunction())
  143. OS << ';';
  144. OS << "\n}\n\n";
  145. auto Err = Change.insert(SM, ExtractedDeclLocation, OS.str());
  146. if (Err)
  147. return std::move(Err);
  148. }
  149. // Create the replacement for the call to the extracted declaration.
  150. {
  151. std::string ReplacedCode;
  152. llvm::raw_string_ostream OS(ReplacedCode);
  153. OS << DeclName << '(';
  154. // FIXME: Forward arguments.
  155. OS << ')';
  156. if (Semicolons.isNeededInOriginalFunction())
  157. OS << ';';
  158. auto Err = Change.replace(
  159. SM, CharSourceRange::getTokenRange(ExtractedRange), OS.str());
  160. if (Err)
  161. return std::move(Err);
  162. }
  163. // FIXME: Add support for assocciated symbol location to AtomicChange to mark
  164. // the ranges of the name of the extracted declaration.
  165. return AtomicChanges{std::move(Change)};
  166. }
  167. } // end namespace tooling
  168. } // end namespace clang