llvm-project

Форк
0
323 строки · 12.2 Кб
1
//===--- DurationRewriter.cpp - clang-tidy --------------------------------===//
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 <cmath>
10
#include <optional>
11

12
#include "DurationRewriter.h"
13
#include "clang/Tooling/FixIt.h"
14
#include "llvm/ADT/IndexedMap.h"
15

16
using namespace clang::ast_matchers;
17

18
namespace clang::tidy::abseil {
19

20
struct DurationScale2IndexFunctor {
21
  using argument_type = DurationScale;
22
  unsigned operator()(DurationScale Scale) const {
23
    return static_cast<unsigned>(Scale);
24
  }
25
};
26

27
/// Returns an integer if the fractional part of a `FloatingLiteral` is `0`.
28
static std::optional<llvm::APSInt>
29
truncateIfIntegral(const FloatingLiteral &FloatLiteral) {
30
  double Value = FloatLiteral.getValueAsApproximateDouble();
31
  if (std::fmod(Value, 1) == 0) {
32
    if (Value >= static_cast<double>(1U << 31))
33
      return std::nullopt;
34

35
    return llvm::APSInt::get(static_cast<int64_t>(Value));
36
  }
37
  return std::nullopt;
38
}
39

40
const std::pair<llvm::StringRef, llvm::StringRef> &
41
getDurationInverseForScale(DurationScale Scale) {
42
  static const llvm::IndexedMap<std::pair<llvm::StringRef, llvm::StringRef>,
43
                                DurationScale2IndexFunctor>
44
      InverseMap = []() {
45
        // TODO: Revisit the immediately invoked lambda technique when
46
        // IndexedMap gets an initializer list constructor.
47
        llvm::IndexedMap<std::pair<llvm::StringRef, llvm::StringRef>,
48
                         DurationScale2IndexFunctor>
49
            InverseMap;
50
        InverseMap.resize(6);
51
        InverseMap[DurationScale::Hours] =
52
            std::make_pair("::absl::ToDoubleHours", "::absl::ToInt64Hours");
53
        InverseMap[DurationScale::Minutes] =
54
            std::make_pair("::absl::ToDoubleMinutes", "::absl::ToInt64Minutes");
55
        InverseMap[DurationScale::Seconds] =
56
            std::make_pair("::absl::ToDoubleSeconds", "::absl::ToInt64Seconds");
57
        InverseMap[DurationScale::Milliseconds] = std::make_pair(
58
            "::absl::ToDoubleMilliseconds", "::absl::ToInt64Milliseconds");
59
        InverseMap[DurationScale::Microseconds] = std::make_pair(
60
            "::absl::ToDoubleMicroseconds", "::absl::ToInt64Microseconds");
61
        InverseMap[DurationScale::Nanoseconds] = std::make_pair(
62
            "::absl::ToDoubleNanoseconds", "::absl::ToInt64Nanoseconds");
63
        return InverseMap;
64
      }();
65

66
  return InverseMap[Scale];
67
}
68

69
/// If `Node` is a call to the inverse of `Scale`, return that inverse's
70
/// argument, otherwise std::nullopt.
71
static std::optional<std::string>
72
rewriteInverseDurationCall(const MatchFinder::MatchResult &Result,
73
                           DurationScale Scale, const Expr &Node) {
74
  const std::pair<llvm::StringRef, llvm::StringRef> &InverseFunctions =
75
      getDurationInverseForScale(Scale);
76
  if (const auto *MaybeCallArg = selectFirst<const Expr>(
77
          "e",
78
          match(callExpr(callee(functionDecl(hasAnyName(
79
                             InverseFunctions.first, InverseFunctions.second))),
80
                         hasArgument(0, expr().bind("e"))),
81
                Node, *Result.Context))) {
82
    return tooling::fixit::getText(*MaybeCallArg, *Result.Context).str();
83
  }
84

85
  return std::nullopt;
86
}
87

88
/// If `Node` is a call to the inverse of `Scale`, return that inverse's
89
/// argument, otherwise std::nullopt.
90
static std::optional<std::string>
91
rewriteInverseTimeCall(const MatchFinder::MatchResult &Result,
92
                       DurationScale Scale, const Expr &Node) {
93
  llvm::StringRef InverseFunction = getTimeInverseForScale(Scale);
94
  if (const auto *MaybeCallArg = selectFirst<const Expr>(
95
          "e", match(callExpr(callee(functionDecl(hasName(InverseFunction))),
96
                              hasArgument(0, expr().bind("e"))),
97
                     Node, *Result.Context))) {
98
    return tooling::fixit::getText(*MaybeCallArg, *Result.Context).str();
99
  }
100

101
  return std::nullopt;
102
}
103

104
/// Returns the factory function name for a given `Scale`.
105
llvm::StringRef getDurationFactoryForScale(DurationScale Scale) {
106
  switch (Scale) {
107
  case DurationScale::Hours:
108
    return "absl::Hours";
109
  case DurationScale::Minutes:
110
    return "absl::Minutes";
111
  case DurationScale::Seconds:
112
    return "absl::Seconds";
113
  case DurationScale::Milliseconds:
114
    return "absl::Milliseconds";
115
  case DurationScale::Microseconds:
116
    return "absl::Microseconds";
117
  case DurationScale::Nanoseconds:
118
    return "absl::Nanoseconds";
119
  }
120
  llvm_unreachable("unknown scaling factor");
121
}
122

123
llvm::StringRef getTimeFactoryForScale(DurationScale Scale) {
124
  switch (Scale) {
125
  case DurationScale::Hours:
126
    return "absl::FromUnixHours";
127
  case DurationScale::Minutes:
128
    return "absl::FromUnixMinutes";
129
  case DurationScale::Seconds:
130
    return "absl::FromUnixSeconds";
131
  case DurationScale::Milliseconds:
132
    return "absl::FromUnixMillis";
133
  case DurationScale::Microseconds:
134
    return "absl::FromUnixMicros";
135
  case DurationScale::Nanoseconds:
136
    return "absl::FromUnixNanos";
137
  }
138
  llvm_unreachable("unknown scaling factor");
139
}
140

141
/// Returns the Time factory function name for a given `Scale`.
142
llvm::StringRef getTimeInverseForScale(DurationScale Scale) {
143
  switch (Scale) {
144
  case DurationScale::Hours:
145
    return "absl::ToUnixHours";
146
  case DurationScale::Minutes:
147
    return "absl::ToUnixMinutes";
148
  case DurationScale::Seconds:
149
    return "absl::ToUnixSeconds";
150
  case DurationScale::Milliseconds:
151
    return "absl::ToUnixMillis";
152
  case DurationScale::Microseconds:
153
    return "absl::ToUnixMicros";
154
  case DurationScale::Nanoseconds:
155
    return "absl::ToUnixNanos";
156
  }
157
  llvm_unreachable("unknown scaling factor");
158
}
159

160
/// Returns `true` if `Node` is a value which evaluates to a literal `0`.
161
bool isLiteralZero(const MatchFinder::MatchResult &Result, const Expr &Node) {
162
  auto ZeroMatcher =
163
      anyOf(integerLiteral(equals(0)), floatLiteral(equals(0.0)));
164

165
  // Check to see if we're using a zero directly.
166
  if (selectFirst<const clang::Expr>(
167
          "val", match(expr(ignoringImpCasts(ZeroMatcher)).bind("val"), Node,
168
                       *Result.Context)) != nullptr)
169
    return true;
170

171
  // Now check to see if we're using a functional cast with a scalar
172
  // initializer expression, e.g. `int{0}`.
173
  if (selectFirst<const clang::Expr>(
174
          "val", match(cxxFunctionalCastExpr(
175
                           hasDestinationType(
176
                               anyOf(isInteger(), realFloatingPointType())),
177
                           hasSourceExpression(initListExpr(
178
                               hasInit(0, ignoringParenImpCasts(ZeroMatcher)))))
179
                           .bind("val"),
180
                       Node, *Result.Context)) != nullptr)
181
    return true;
182

183
  return false;
184
}
185

186
std::optional<std::string>
187
stripFloatCast(const ast_matchers::MatchFinder::MatchResult &Result,
188
               const Expr &Node) {
189
  if (const Expr *MaybeCastArg = selectFirst<const Expr>(
190
          "cast_arg",
191
          match(expr(anyOf(cxxStaticCastExpr(
192
                               hasDestinationType(realFloatingPointType()),
193
                               hasSourceExpression(expr().bind("cast_arg"))),
194
                           cStyleCastExpr(
195
                               hasDestinationType(realFloatingPointType()),
196
                               hasSourceExpression(expr().bind("cast_arg"))),
197
                           cxxFunctionalCastExpr(
198
                               hasDestinationType(realFloatingPointType()),
199
                               hasSourceExpression(expr().bind("cast_arg"))))),
200
                Node, *Result.Context)))
201
    return tooling::fixit::getText(*MaybeCastArg, *Result.Context).str();
202

203
  return std::nullopt;
204
}
205

206
std::optional<std::string>
207
stripFloatLiteralFraction(const MatchFinder::MatchResult &Result,
208
                          const Expr &Node) {
209
  if (const auto *LitFloat = llvm::dyn_cast<FloatingLiteral>(&Node))
210
    // Attempt to simplify a `Duration` factory call with a literal argument.
211
    if (std::optional<llvm::APSInt> IntValue = truncateIfIntegral(*LitFloat))
212
      return toString(*IntValue, /*radix=*/10);
213

214
  return std::nullopt;
215
}
216

217
std::string simplifyDurationFactoryArg(const MatchFinder::MatchResult &Result,
218
                                       const Expr &Node) {
219
  // Check for an explicit cast to `float` or `double`.
220
  if (std::optional<std::string> MaybeArg = stripFloatCast(Result, Node))
221
    return *MaybeArg;
222

223
  // Check for floats without fractional components.
224
  if (std::optional<std::string> MaybeArg =
225
          stripFloatLiteralFraction(Result, Node))
226
    return *MaybeArg;
227

228
  // We couldn't simplify any further, so return the argument text.
229
  return tooling::fixit::getText(Node, *Result.Context).str();
230
}
231

232
std::optional<DurationScale> getScaleForDurationInverse(llvm::StringRef Name) {
233
  static const llvm::StringMap<DurationScale> ScaleMap(
234
      {{"ToDoubleHours", DurationScale::Hours},
235
       {"ToInt64Hours", DurationScale::Hours},
236
       {"ToDoubleMinutes", DurationScale::Minutes},
237
       {"ToInt64Minutes", DurationScale::Minutes},
238
       {"ToDoubleSeconds", DurationScale::Seconds},
239
       {"ToInt64Seconds", DurationScale::Seconds},
240
       {"ToDoubleMilliseconds", DurationScale::Milliseconds},
241
       {"ToInt64Milliseconds", DurationScale::Milliseconds},
242
       {"ToDoubleMicroseconds", DurationScale::Microseconds},
243
       {"ToInt64Microseconds", DurationScale::Microseconds},
244
       {"ToDoubleNanoseconds", DurationScale::Nanoseconds},
245
       {"ToInt64Nanoseconds", DurationScale::Nanoseconds}});
246

247
  auto ScaleIter = ScaleMap.find(std::string(Name));
248
  if (ScaleIter == ScaleMap.end())
249
    return std::nullopt;
250

251
  return ScaleIter->second;
252
}
253

254
std::optional<DurationScale> getScaleForTimeInverse(llvm::StringRef Name) {
255
  static const llvm::StringMap<DurationScale> ScaleMap(
256
      {{"ToUnixHours", DurationScale::Hours},
257
       {"ToUnixMinutes", DurationScale::Minutes},
258
       {"ToUnixSeconds", DurationScale::Seconds},
259
       {"ToUnixMillis", DurationScale::Milliseconds},
260
       {"ToUnixMicros", DurationScale::Microseconds},
261
       {"ToUnixNanos", DurationScale::Nanoseconds}});
262

263
  auto ScaleIter = ScaleMap.find(std::string(Name));
264
  if (ScaleIter == ScaleMap.end())
265
    return std::nullopt;
266

267
  return ScaleIter->second;
268
}
269

270
std::string rewriteExprFromNumberToDuration(
271
    const ast_matchers::MatchFinder::MatchResult &Result, DurationScale Scale,
272
    const Expr *Node) {
273
  const Expr &RootNode = *Node->IgnoreParenImpCasts();
274

275
  // First check to see if we can undo a complementary function call.
276
  if (std::optional<std::string> MaybeRewrite =
277
          rewriteInverseDurationCall(Result, Scale, RootNode))
278
    return *MaybeRewrite;
279

280
  if (isLiteralZero(Result, RootNode))
281
    return {"absl::ZeroDuration()"};
282

283
  return (llvm::Twine(getDurationFactoryForScale(Scale)) + "(" +
284
          simplifyDurationFactoryArg(Result, RootNode) + ")")
285
      .str();
286
}
287

288
std::string rewriteExprFromNumberToTime(
289
    const ast_matchers::MatchFinder::MatchResult &Result, DurationScale Scale,
290
    const Expr *Node) {
291
  const Expr &RootNode = *Node->IgnoreParenImpCasts();
292

293
  // First check to see if we can undo a complementary function call.
294
  if (std::optional<std::string> MaybeRewrite =
295
          rewriteInverseTimeCall(Result, Scale, RootNode))
296
    return *MaybeRewrite;
297

298
  if (isLiteralZero(Result, RootNode))
299
    return {"absl::UnixEpoch()"};
300

301
  return (llvm::Twine(getTimeFactoryForScale(Scale)) + "(" +
302
          tooling::fixit::getText(RootNode, *Result.Context) + ")")
303
      .str();
304
}
305

306
bool isInMacro(const MatchFinder::MatchResult &Result, const Expr *E) {
307
  if (!E->getBeginLoc().isMacroID())
308
    return false;
309

310
  SourceLocation Loc = E->getBeginLoc();
311
  // We want to get closer towards the initial macro typed into the source only
312
  // if the location is being expanded as a macro argument.
313
  while (Result.SourceManager->isMacroArgExpansion(Loc)) {
314
    // We are calling getImmediateMacroCallerLoc, but note it is essentially
315
    // equivalent to calling getImmediateSpellingLoc in this context according
316
    // to Clang implementation. We are not calling getImmediateSpellingLoc
317
    // because Clang comment says it "should not generally be used by clients."
318
    Loc = Result.SourceManager->getImmediateMacroCallerLoc(Loc);
319
  }
320
  return Loc.isMacroID();
321
}
322

323
} // namespace clang::tidy::abseil
324

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.