Coverage Report

Created: 2020-02-18 08:44

/Users/buildslave/jenkins/workspace/coverage/llvm-project/clang/lib/Tooling/Transformer/RewriteRule.cpp
Line
Count
Source (jump to first uncovered line)
1
//===--- Transformer.cpp - Transformer library implementation ---*- C++ -*-===//
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
#include "clang/Tooling/Transformer/RewriteRule.h"
10
#include "clang/ASTMatchers/ASTMatchFinder.h"
11
#include "clang/ASTMatchers/ASTMatchers.h"
12
#include "clang/Basic/SourceLocation.h"
13
#include "clang/Tooling/Transformer/SourceCode.h"
14
#include "llvm/ADT/Optional.h"
15
#include "llvm/ADT/StringRef.h"
16
#include "llvm/Support/Errc.h"
17
#include "llvm/Support/Error.h"
18
#include <map>
19
#include <string>
20
#include <utility>
21
#include <vector>
22
23
using namespace clang;
24
using namespace transformer;
25
26
using ast_matchers::MatchFinder;
27
using ast_matchers::internal::DynTypedMatcher;
28
29
using MatchResult = MatchFinder::MatchResult;
30
31
Expected<SmallVector<transformer::detail::Transformation, 1>>
32
transformer::detail::translateEdits(const MatchResult &Result,
33
41
                                llvm::ArrayRef<ASTEdit> Edits) {
34
41
  SmallVector<transformer::detail::Transformation, 1> Transformations;
35
44
  for (const auto &Edit : Edits) {
36
44
    Expected<CharSourceRange> Range = Edit.TargetRange(Result);
37
44
    if (!Range)
38
2
      return Range.takeError();
39
42
    llvm::Optional<CharSourceRange> EditRange =
40
42
        tooling::getRangeForEdit(*Range, *Result.Context);
41
42
    // FIXME: let user specify whether to treat this case as an error or ignore
42
42
    // it as is currently done.
43
42
    if (!EditRange)
44
2
      return SmallVector<Transformation, 0>();
45
40
    auto Replacement = Edit.Replacement->eval(Result);
46
40
    if (!Replacement)
47
1
      return Replacement.takeError();
48
39
    transformer::detail::Transformation T;
49
39
    T.Range = *EditRange;
50
39
    T.Replacement = std::move(*Replacement);
51
39
    Transformations.push_back(std::move(T));
52
39
  }
53
41
  
return Transformations36
;
54
41
}
55
56
43
ASTEdit transformer::changeTo(RangeSelector S, TextGenerator Replacement) {
57
43
  ASTEdit E;
58
43
  E.TargetRange = std::move(S);
59
43
  E.Replacement = std::move(Replacement);
60
43
  return E;
61
43
}
62
63
namespace {
64
/// A \c TextGenerator that always returns a fixed string.
65
class SimpleTextGenerator : public MatchComputation<std::string> {
66
  std::string S;
67
68
public:
69
1
  SimpleTextGenerator(std::string S) : S(std::move(S)) {}
70
  llvm::Error eval(const ast_matchers::MatchFinder::MatchResult &,
71
1
                   std::string *Result) const override {
72
1
    Result->append(S);
73
1
    return llvm::Error::success();
74
1
  }
75
0
  std::string toString() const override {
76
0
    return (llvm::Twine("text(\"") + S + "\")").str();
77
0
  }
78
};
79
} // namespace
80
81
1
ASTEdit transformer::remove(RangeSelector S) {
82
1
  return change(std::move(S), std::make_shared<SimpleTextGenerator>(""));
83
1
}
84
85
RewriteRule transformer::makeRule(DynTypedMatcher M, SmallVector<ASTEdit, 1> Edits,
86
40
                              TextGenerator Explanation) {
87
40
  return RewriteRule{{RewriteRule::Case{
88
40
      std::move(M), std::move(Edits), std::move(Explanation), {}}}};
89
40
}
90
91
void transformer::addInclude(RewriteRule &Rule, StringRef Header,
92
2
                         IncludeFormat Format) {
93
2
  for (auto &Case : Rule.Cases)
94
2
    Case.AddedIncludes.emplace_back(Header.str(), Format);
95
2
}
96
97
#ifndef NDEBUG
98
// Filters for supported matcher kinds. FIXME: Explicitly list the allowed kinds
99
// (all node matcher types except for `QualType` and `Type`), rather than just
100
// banning `QualType` and `Type`.
101
41
static bool hasValidKind(const DynTypedMatcher &M) {
102
41
  return !M.canConvertTo<QualType>();
103
41
}
104
#endif
105
106
// Binds each rule's matcher to a unique (and deterministic) tag based on
107
// `TagBase` and the id paired with the case.
108
static std::vector<DynTypedMatcher> taggedMatchers(
109
    StringRef TagBase,
110
36
    const SmallVectorImpl<std::pair<size_t, RewriteRule::Case>> &Cases) {
111
36
  std::vector<DynTypedMatcher> Matchers;
112
36
  Matchers.reserve(Cases.size());
113
41
  for (const auto &Case : Cases) {
114
41
    std::string Tag = (TagBase + Twine(Case.first)).str();
115
41
    // HACK: Many matchers are not bindable, so ensure that tryBind will work.
116
41
    DynTypedMatcher BoundMatcher(Case.second.Matcher);
117
41
    BoundMatcher.setAllowBind(true);
118
41
    auto M = BoundMatcher.tryBind(Tag);
119
41
    Matchers.push_back(*std::move(M));
120
41
  }
121
36
  return Matchers;
122
36
}
123
124
// Simply gathers the contents of the various rules into a single rule. The
125
// actual work to combine these into an ordered choice is deferred to matcher
126
// registration.
127
4
RewriteRule transformer::applyFirst(ArrayRef<RewriteRule> Rules) {
128
4
  RewriteRule R;
129
4
  for (auto &Rule : Rules)
130
9
    R.Cases.append(Rule.Cases.begin(), Rule.Cases.end());
131
4
  return R;
132
4
}
133
134
std::vector<DynTypedMatcher>
135
34
transformer::detail::buildMatchers(const RewriteRule &Rule) {
136
34
  // Map the cases into buckets of matchers -- one for each "root" AST kind,
137
34
  // which guarantees that they can be combined in a single anyOf matcher. Each
138
34
  // case is paired with an identifying number that is converted to a string id
139
34
  // in `taggedMatchers`.
140
34
  std::map<ASTNodeKind, SmallVector<std::pair<size_t, RewriteRule::Case>, 1>>
141
34
      Buckets;
142
34
  const SmallVectorImpl<RewriteRule::Case> &Cases = Rule.Cases;
143
75
  for (int I = 0, N = Cases.size(); I < N; 
++I41
) {
144
41
    assert(hasValidKind(Cases[I].Matcher) &&
145
41
           "Matcher must be non-(Qual)Type node matcher");
146
41
    Buckets[Cases[I].Matcher.getSupportedKind()].emplace_back(I, Cases[I]);
147
41
  }
148
34
149
34
  std::vector<DynTypedMatcher> Matchers;
150
36
  for (const auto &Bucket : Buckets) {
151
36
    DynTypedMatcher M = DynTypedMatcher::constructVariadic(
152
36
        DynTypedMatcher::VO_AnyOf, Bucket.first,
153
36
        taggedMatchers("Tag", Bucket.second));
154
36
    M.setAllowBind(true);
155
36
    // `tryBind` is guaranteed to succeed, because `AllowBind` was set to true.
156
36
    Matchers.push_back(*M.tryBind(RewriteRule::RootID));
157
36
  }
158
34
  return Matchers;
159
34
}
160
161
0
DynTypedMatcher transformer::detail::buildMatcher(const RewriteRule &Rule) {
162
0
  std::vector<DynTypedMatcher> Ms = buildMatchers(Rule);
163
0
  assert(Ms.size() == 1 && "Cases must have compatible matchers.");
164
0
  return Ms[0];
165
0
}
166
167
2
SourceLocation transformer::detail::getRuleMatchLoc(const MatchResult &Result) {
168
2
  auto &NodesMap = Result.Nodes.getMap();
169
2
  auto Root = NodesMap.find(RewriteRule::RootID);
170
2
  assert(Root != NodesMap.end() && "Transformation failed: missing root node.");
171
2
  llvm::Optional<CharSourceRange> RootRange = tooling::getRangeForEdit(
172
2
      CharSourceRange::getTokenRange(Root->second.getSourceRange()),
173
2
      *Result.Context);
174
2
  if (RootRange)
175
0
    return RootRange->getBegin();
176
2
  // The match doesn't have a coherent range, so fall back to the expansion
177
2
  // location as the "beginning" of the match.
178
2
  return Result.SourceManager->getExpansionLoc(
179
2
      Root->second.getSourceRange().getBegin());
180
2
}
181
182
// Finds the case that was "selected" -- that is, whose matcher triggered the
183
// `MatchResult`.
184
const RewriteRule::Case &
185
transformer::detail::findSelectedCase(const MatchResult &Result,
186
41
                                  const RewriteRule &Rule) {
187
41
  if (Rule.Cases.size() == 1)
188
32
    return Rule.Cases[0];
189
9
190
9
  auto &NodesMap = Result.Nodes.getMap();
191
14
  for (size_t i = 0, N = Rule.Cases.size(); i < N; 
++i5
) {
192
14
    std::string Tag = ("Tag" + Twine(i)).str();
193
14
    if (NodesMap.find(Tag) != NodesMap.end())
194
9
      return Rule.Cases[i];
195
14
  }
196
9
  
llvm_unreachable0
("No tag found for this rule.");
197
9
}
198
199
constexpr llvm::StringLiteral RewriteRule::RootID;
200
201
0
TextGenerator tooling::text(std::string M) {
202
0
  return std::make_shared<SimpleTextGenerator>(std::move(M));
203
0
}