clang-tools  8.0.0
DurationRewriter.cpp
Go to the documentation of this file.
1 //===--- DurationRewriter.cpp - clang-tidy --------------------------------===//
2 //
3 // The LLVM Compiler Infrastructure
4 //
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
7 //
8 //===----------------------------------------------------------------------===//
9 
10 #include "DurationRewriter.h"
11 #include "clang/Tooling/FixIt.h"
12 #include "llvm/ADT/IndexedMap.h"
13 
14 using namespace clang::ast_matchers;
15 
16 namespace clang {
17 namespace tidy {
18 namespace abseil {
19 
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 llvm::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 llvm::None;
34 
35  return llvm::APSInt::get(static_cast<int64_t>(Value));
36  }
37  return llvm::None;
38 }
39 
40 const std::pair<llvm::StringRef, llvm::StringRef> &
42  static const llvm::IndexedMap<std::pair<llvm::StringRef, llvm::StringRef>,
44  InverseMap = []() {
45  // TODO: Revisit the immediately invoked lamba technique when
46  // IndexedMap gets an initializer list constructor.
47  llvm::IndexedMap<std::pair<llvm::StringRef, llvm::StringRef>,
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 None.
71 static llvm::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  getInverseForScale(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 llvm::None;
86 }
87 
88 /// Returns the factory function name for a given `Scale`.
89 llvm::StringRef getFactoryForScale(DurationScale Scale) {
90  switch (Scale) {
91  case DurationScale::Hours:
92  return "absl::Hours";
93  case DurationScale::Minutes:
94  return "absl::Minutes";
95  case DurationScale::Seconds:
96  return "absl::Seconds";
97  case DurationScale::Milliseconds:
98  return "absl::Milliseconds";
99  case DurationScale::Microseconds:
100  return "absl::Microseconds";
101  case DurationScale::Nanoseconds:
102  return "absl::Nanoseconds";
103  }
104  llvm_unreachable("unknown scaling factor");
105 }
106 
107 /// Returns `true` if `Node` is a value which evaluates to a literal `0`.
108 bool IsLiteralZero(const MatchFinder::MatchResult &Result, const Expr &Node) {
109  auto ZeroMatcher =
110  anyOf(integerLiteral(equals(0)), floatLiteral(equals(0.0)));
111 
112  // Check to see if we're using a zero directly.
113  if (selectFirst<const clang::Expr>(
114  "val", match(expr(ignoringImpCasts(ZeroMatcher)).bind("val"), Node,
115  *Result.Context)) != nullptr)
116  return true;
117 
118  // Now check to see if we're using a functional cast with a scalar
119  // initializer expression, e.g. `int{0}`.
120  if (selectFirst<const clang::Expr>(
121  "val", match(cxxFunctionalCastExpr(
122  hasDestinationType(
123  anyOf(isInteger(), realFloatingPointType())),
124  hasSourceExpression(initListExpr(
125  hasInit(0, ignoringParenImpCasts(ZeroMatcher)))))
126  .bind("val"),
127  Node, *Result.Context)) != nullptr)
128  return true;
129 
130  return false;
131 }
132 
133 llvm::Optional<std::string>
134 stripFloatCast(const ast_matchers::MatchFinder::MatchResult &Result,
135  const Expr &Node) {
136  if (const Expr *MaybeCastArg = selectFirst<const Expr>(
137  "cast_arg",
138  match(expr(anyOf(cxxStaticCastExpr(
139  hasDestinationType(realFloatingPointType()),
140  hasSourceExpression(expr().bind("cast_arg"))),
141  cStyleCastExpr(
142  hasDestinationType(realFloatingPointType()),
143  hasSourceExpression(expr().bind("cast_arg"))),
144  cxxFunctionalCastExpr(
145  hasDestinationType(realFloatingPointType()),
146  hasSourceExpression(expr().bind("cast_arg"))))),
147  Node, *Result.Context)))
148  return tooling::fixit::getText(*MaybeCastArg, *Result.Context).str();
149 
150  return llvm::None;
151 }
152 
153 llvm::Optional<std::string>
154 stripFloatLiteralFraction(const MatchFinder::MatchResult &Result,
155  const Expr &Node) {
156  if (const auto *LitFloat = llvm::dyn_cast<FloatingLiteral>(&Node))
157  // Attempt to simplify a `Duration` factory call with a literal argument.
158  if (llvm::Optional<llvm::APSInt> IntValue = truncateIfIntegral(*LitFloat))
159  return IntValue->toString(/*radix=*/10);
160 
161  return llvm::None;
162 }
163 
164 std::string simplifyDurationFactoryArg(const MatchFinder::MatchResult &Result,
165  const Expr &Node) {
166  // Check for an explicit cast to `float` or `double`.
167  if (llvm::Optional<std::string> MaybeArg = stripFloatCast(Result, Node))
168  return *MaybeArg;
169 
170  // Check for floats without fractional components.
171  if (llvm::Optional<std::string> MaybeArg =
172  stripFloatLiteralFraction(Result, Node))
173  return *MaybeArg;
174 
175  // We couldn't simplify any further, so return the argument text.
176  return tooling::fixit::getText(Node, *Result.Context).str();
177 }
178 
179 llvm::Optional<DurationScale> getScaleForInverse(llvm::StringRef Name) {
180  static const llvm::StringMap<DurationScale> ScaleMap(
181  {{"ToDoubleHours", DurationScale::Hours},
182  {"ToInt64Hours", DurationScale::Hours},
183  {"ToDoubleMinutes", DurationScale::Minutes},
184  {"ToInt64Minutes", DurationScale::Minutes},
185  {"ToDoubleSeconds", DurationScale::Seconds},
186  {"ToInt64Seconds", DurationScale::Seconds},
187  {"ToDoubleMilliseconds", DurationScale::Milliseconds},
188  {"ToInt64Milliseconds", DurationScale::Milliseconds},
189  {"ToDoubleMicroseconds", DurationScale::Microseconds},
190  {"ToInt64Microseconds", DurationScale::Microseconds},
191  {"ToDoubleNanoseconds", DurationScale::Nanoseconds},
192  {"ToInt64Nanoseconds", DurationScale::Nanoseconds}});
193 
194  auto ScaleIter = ScaleMap.find(std::string(Name));
195  if (ScaleIter == ScaleMap.end())
196  return llvm::None;
197 
198  return ScaleIter->second;
199 }
200 
202  const ast_matchers::MatchFinder::MatchResult &Result, DurationScale Scale,
203  const Expr *Node) {
204  const Expr &RootNode = *Node->IgnoreParenImpCasts();
205 
206  // First check to see if we can undo a complimentary function call.
207  if (llvm::Optional<std::string> MaybeRewrite =
208  rewriteInverseDurationCall(Result, Scale, RootNode))
209  return *MaybeRewrite;
210 
211  if (IsLiteralZero(Result, RootNode))
212  return std::string("absl::ZeroDuration()");
213 
214  return (llvm::Twine(getFactoryForScale(Scale)) + "(" +
215  simplifyDurationFactoryArg(Result, RootNode) + ")")
216  .str();
217 }
218 
219 } // namespace abseil
220 } // namespace tidy
221 } // namespace clang
std::string rewriteExprFromNumberToDuration(const ast_matchers::MatchFinder::MatchResult &Result, DurationScale Scale, const Expr *Node)
Assuming Node has type double or int representing a time interval of Scale, return the expression to ...
static llvm::Optional< std::string > rewriteInverseDurationCall(const MatchFinder::MatchResult &Result, DurationScale Scale, const Expr &Node)
If Node is a call to the inverse of Scale, return that inverse&#39;s argument, otherwise None...
llvm::StringRef getFactoryForScale(DurationScale Scale)
Returns the factory function name for a given Scale.
unsigned operator()(DurationScale Scale) const
static constexpr llvm::StringLiteral Name
llvm::Optional< llvm::Expected< tooling::AtomicChanges > > Result
std::string simplifyDurationFactoryArg(const MatchFinder::MatchResult &Result, const Expr &Node)
llvm::Optional< DurationScale > getScaleForInverse(llvm::StringRef Name)
Given the name of an inverse Duration function (e.g., ToDoubleSeconds), return its DurationScale...
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//
static llvm::Optional< llvm::APSInt > truncateIfIntegral(const FloatingLiteral &FloatLiteral)
Returns an integer if the fractional part of a FloatingLiteral is 0.
llvm::Optional< std::string > stripFloatCast(const ast_matchers::MatchFinder::MatchResult &Result, const Expr &Node)
Possibly strip a floating point cast expression.
llvm::Optional< std::string > stripFloatLiteralFraction(const MatchFinder::MatchResult &Result, const Expr &Node)
bool IsLiteralZero(const MatchFinder::MatchResult &Result, const Expr &Node)
Returns true if Node is a value which evaluates to a literal 0.
const std::pair< llvm::StringRef, llvm::StringRef > & getInverseForScale(DurationScale Scale)
Given a Scale return the fully qualified inverse functions for it.
DurationScale
Duration factory and conversion scales.