Coverage Report

Created: 2020-02-15 09:57

/Users/buildslave/jenkins/workspace/coverage/llvm-project/clang/lib/Tooling/RefactoringCallbacks.cpp
Line
Count
Source (jump to first uncovered line)
1
//===--- RefactoringCallbacks.cpp - Structural query framework ------------===//
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
//
10
//===----------------------------------------------------------------------===//
11
#include "clang/Tooling/RefactoringCallbacks.h"
12
#include "clang/ASTMatchers/ASTMatchFinder.h"
13
#include "clang/Basic/SourceLocation.h"
14
#include "clang/Lex/Lexer.h"
15
16
using llvm::StringError;
17
using llvm::make_error;
18
19
namespace clang {
20
namespace tooling {
21
22
10
RefactoringCallback::RefactoringCallback() {}
23
20
tooling::Replacements &RefactoringCallback::getReplacements() {
24
20
  return Replace;
25
20
}
26
27
ASTMatchRefactorer::ASTMatchRefactorer(
28
    std::map<std::string, Replacements> &FileToReplaces)
29
10
    : FileToReplaces(FileToReplaces) {}
30
31
void ASTMatchRefactorer::addDynamicMatcher(
32
    const ast_matchers::internal::DynTypedMatcher &Matcher,
33
0
    RefactoringCallback *Callback) {
34
0
  MatchFinder.addDynamicMatcher(Matcher, Callback);
35
0
  Callbacks.push_back(Callback);
36
0
}
37
38
class RefactoringASTConsumer : public ASTConsumer {
39
public:
40
  explicit RefactoringASTConsumer(ASTMatchRefactorer &Refactoring)
41
10
      : Refactoring(Refactoring) {}
42
43
10
  void HandleTranslationUnit(ASTContext &Context) override {
44
10
    // The ASTMatchRefactorer is re-used between translation units.
45
10
    // Clear the matchers so that each Replacement is only emitted once.
46
10
    for (const auto &Callback : Refactoring.Callbacks) {
47
10
      Callback->getReplacements().clear();
48
10
    }
49
10
    Refactoring.MatchFinder.matchAST(Context);
50
10
    for (const auto &Callback : Refactoring.Callbacks) {
51
10
      for (const auto &Replacement : Callback->getReplacements()) {
52
9
        llvm::Error Err =
53
9
            Refactoring.FileToReplaces[std::string(Replacement.getFilePath())]
54
9
                .add(Replacement);
55
9
        if (Err) {
56
0
          llvm::errs() << "Skipping replacement " << Replacement.toString()
57
0
                       << " due to this error:\n"
58
0
                       << toString(std::move(Err)) << "\n";
59
0
        }
60
9
      }
61
10
    }
62
10
  }
63
64
private:
65
  ASTMatchRefactorer &Refactoring;
66
};
67
68
10
std::unique_ptr<ASTConsumer> ASTMatchRefactorer::newASTConsumer() {
69
10
  return std::make_unique<RefactoringASTConsumer>(*this);
70
10
}
71
72
static Replacement replaceStmtWithText(SourceManager &Sources, const Stmt &From,
73
3
                                       StringRef Text) {
74
3
  return tooling::Replacement(
75
3
      Sources, CharSourceRange::getTokenRange(From.getSourceRange()), Text);
76
3
}
77
static Replacement replaceStmtWithStmt(SourceManager &Sources, const Stmt &From,
78
2
                                       const Stmt &To) {
79
2
  return replaceStmtWithText(
80
2
      Sources, From,
81
2
      Lexer::getSourceText(CharSourceRange::getTokenRange(To.getSourceRange()),
82
2
                           Sources, LangOptions()));
83
2
}
84
85
ReplaceStmtWithText::ReplaceStmtWithText(StringRef FromId, StringRef ToText)
86
4
    : FromId(std::string(FromId)), ToText(std::string(ToText)) {}
87
88
void ReplaceStmtWithText::run(
89
3
    const ast_matchers::MatchFinder::MatchResult &Result) {
90
3
  if (const Stmt *FromMatch = Result.Nodes.getNodeAs<Stmt>(FromId)) {
91
3
    auto Err = Replace.add(tooling::Replacement(
92
3
        *Result.SourceManager,
93
3
        CharSourceRange::getTokenRange(FromMatch->getSourceRange()), ToText));
94
3
    // FIXME: better error handling. For now, just print error message in the
95
3
    // release version.
96
3
    if (Err) {
97
0
      llvm::errs() << llvm::toString(std::move(Err)) << "\n";
98
0
      assert(false);
99
0
    }
100
3
  }
101
3
}
102
103
ReplaceStmtWithStmt::ReplaceStmtWithStmt(StringRef FromId, StringRef ToId)
104
1
    : FromId(std::string(FromId)), ToId(std::string(ToId)) {}
105
106
void ReplaceStmtWithStmt::run(
107
1
    const ast_matchers::MatchFinder::MatchResult &Result) {
108
1
  const Stmt *FromMatch = Result.Nodes.getNodeAs<Stmt>(FromId);
109
1
  const Stmt *ToMatch = Result.Nodes.getNodeAs<Stmt>(ToId);
110
1
  if (FromMatch && ToMatch) {
111
1
    auto Err = Replace.add(
112
1
        replaceStmtWithStmt(*Result.SourceManager, *FromMatch, *ToMatch));
113
1
    // FIXME: better error handling. For now, just print error message in the
114
1
    // release version.
115
1
    if (Err) {
116
0
      llvm::errs() << llvm::toString(std::move(Err)) << "\n";
117
0
      assert(false);
118
0
    }
119
1
  }
120
1
}
121
122
ReplaceIfStmtWithItsBody::ReplaceIfStmtWithItsBody(StringRef Id,
123
                                                   bool PickTrueBranch)
124
2
    : Id(std::string(Id)), PickTrueBranch(PickTrueBranch) {}
125
126
void ReplaceIfStmtWithItsBody::run(
127
2
    const ast_matchers::MatchFinder::MatchResult &Result) {
128
2
  if (const IfStmt *Node = Result.Nodes.getNodeAs<IfStmt>(Id)) {
129
2
    const Stmt *Body = PickTrueBranch ? 
Node->getThen()1
:
Node->getElse()1
;
130
2
    if (Body) {
131
1
      auto Err =
132
1
          Replace.add(replaceStmtWithStmt(*Result.SourceManager, *Node, *Body));
133
1
      // FIXME: better error handling. For now, just print error message in the
134
1
      // release version.
135
1
      if (Err) {
136
0
        llvm::errs() << llvm::toString(std::move(Err)) << "\n";
137
0
        assert(false);
138
0
      }
139
1
    } else if (!PickTrueBranch) {
140
1
      // If we want to use the 'else'-branch, but it doesn't exist, delete
141
1
      // the whole 'if'.
142
1
      auto Err =
143
1
          Replace.add(replaceStmtWithText(*Result.SourceManager, *Node, ""));
144
1
      // FIXME: better error handling. For now, just print error message in the
145
1
      // release version.
146
1
      if (Err) {
147
0
        llvm::errs() << llvm::toString(std::move(Err)) << "\n";
148
0
        assert(false);
149
0
      }
150
1
    }
151
2
  }
152
2
}
153
154
ReplaceNodeWithTemplate::ReplaceNodeWithTemplate(
155
    llvm::StringRef FromId, std::vector<TemplateElement> Template)
156
3
    : FromId(std::string(FromId)), Template(std::move(Template)) {}
157
158
llvm::Expected<std::unique_ptr<ReplaceNodeWithTemplate>>
159
5
ReplaceNodeWithTemplate::create(StringRef FromId, StringRef ToTemplate) {
160
5
  std::vector<TemplateElement> ParsedTemplate;
161
17
  for (size_t Index = 0; Index < ToTemplate.size();) {
162
14
    if (ToTemplate[Index] == '$') {
163
6
      if (ToTemplate.substr(Index, 2) == "$$") {
164
2
        Index += 2;
165
2
        ParsedTemplate.push_back(
166
2
            TemplateElement{TemplateElement::Literal, "$"});
167
4
      } else if (ToTemplate.substr(Index, 2) == "${") {
168
3
        size_t EndOfIdentifier = ToTemplate.find("}", Index);
169
3
        if (EndOfIdentifier == std::string::npos) {
170
1
          return make_error<StringError>(
171
1
              "Unterminated ${...} in replacement template near " +
172
1
                  ToTemplate.substr(Index),
173
1
              llvm::inconvertibleErrorCode());
174
1
        }
175
2
        std::string SourceNodeName = std::string(
176
2
            ToTemplate.substr(Index + 2, EndOfIdentifier - Index - 2));
177
2
        ParsedTemplate.push_back(
178
2
            TemplateElement{TemplateElement::Identifier, SourceNodeName});
179
2
        Index = EndOfIdentifier + 1;
180
2
      } else {
181
1
        return make_error<StringError>(
182
1
            "Invalid $ in replacement template near " +
183
1
                ToTemplate.substr(Index),
184
1
            llvm::inconvertibleErrorCode());
185
1
      }
186
8
    } else {
187
8
      size_t NextIndex = ToTemplate.find('$', Index + 1);
188
8
      ParsedTemplate.push_back(TemplateElement{
189
8
          TemplateElement::Literal,
190
8
          std::string(ToTemplate.substr(Index, NextIndex - Index))});
191
8
      Index = NextIndex;
192
8
    }
193
14
  }
194
5
  return std::unique_ptr<ReplaceNodeWithTemplate>(
195
3
      new ReplaceNodeWithTemplate(FromId, std::move(ParsedTemplate)));
196
5
}
197
198
void ReplaceNodeWithTemplate::run(
199
3
    const ast_matchers::MatchFinder::MatchResult &Result) {
200
3
  const auto &NodeMap = Result.Nodes.getMap();
201
3
202
3
  std::string ToText;
203
8
  for (const auto &Element : Template) {
204
8
    switch (Element.Type) {
205
6
    case TemplateElement::Literal:
206
6
      ToText += Element.Value;
207
6
      break;
208
2
    case TemplateElement::Identifier: {
209
2
      auto NodeIter = NodeMap.find(Element.Value);
210
2
      if (NodeIter == NodeMap.end()) {
211
0
        llvm::errs() << "Node " << Element.Value
212
0
                     << " used in replacement template not bound in Matcher \n";
213
0
        llvm::report_fatal_error("Unbound node in replacement template.");
214
0
      }
215
2
      CharSourceRange Source =
216
2
          CharSourceRange::getTokenRange(NodeIter->second.getSourceRange());
217
2
      ToText += Lexer::getSourceText(Source, *Result.SourceManager,
218
2
                                     Result.Context->getLangOpts());
219
2
      break;
220
2
    }
221
8
    }
222
8
  }
223
3
  if (NodeMap.count(FromId) == 0) {
224
0
    llvm::errs() << "Node to be replaced " << FromId
225
0
                 << " not bound in query.\n";
226
0
    llvm::report_fatal_error("FromId node not bound in MatchResult");
227
0
  }
228
3
  auto Replacement =
229
3
      tooling::Replacement(*Result.SourceManager, &NodeMap.at(FromId), ToText,
230
3
                           Result.Context->getLangOpts());
231
3
  llvm::Error Err = Replace.add(Replacement);
232
3
  if (Err) {
233
0
    llvm::errs() << "Query and replace failed in " << Replacement.getFilePath()
234
0
                 << "! " << llvm::toString(std::move(Err)) << "\n";
235
0
    llvm::report_fatal_error("Replacement failed");
236
0
  }
237
3
}
238
239
} // end namespace tooling
240
} // end namespace clang