Coverage Report

Created: 2020-02-25 14:32

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