clang-tools  8.0.0
DurationFactoryScaleCheck.cpp
Go to the documentation of this file.
1 //===--- DurationFactoryScaleCheck.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 
11 #include "DurationRewriter.h"
12 #include "clang/AST/ASTContext.h"
13 #include "clang/ASTMatchers/ASTMatchFinder.h"
14 #include "clang/Tooling/FixIt.h"
15 
16 using namespace clang::ast_matchers;
17 
18 namespace clang {
19 namespace tidy {
20 namespace abseil {
21 
22 // Given the name of a duration factory function, return the appropriate
23 // `DurationScale` for that factory. If no factory can be found for
24 // `FactoryName`, return `None`.
25 static llvm::Optional<DurationScale>
26 getScaleForFactory(llvm::StringRef FactoryName) {
27  static const std::unordered_map<std::string, DurationScale> ScaleMap(
28  {{"Nanoseconds", DurationScale::Nanoseconds},
29  {"Microseconds", DurationScale::Microseconds},
30  {"Milliseconds", DurationScale::Milliseconds},
31  {"Seconds", DurationScale::Seconds},
32  {"Minutes", DurationScale::Minutes},
33  {"Hours", DurationScale::Hours}});
34 
35  auto ScaleIter = ScaleMap.find(FactoryName);
36  if (ScaleIter == ScaleMap.end())
37  return llvm::None;
38 
39  return ScaleIter->second;
40 }
41 
42 // Given either an integer or float literal, return its value.
43 // One and only one of `IntLit` and `FloatLit` should be provided.
44 static double GetValue(const IntegerLiteral *IntLit,
45  const FloatingLiteral *FloatLit) {
46  if (IntLit)
47  return IntLit->getValue().getLimitedValue();
48 
49  assert(FloatLit != nullptr && "Neither IntLit nor FloatLit set");
50  return FloatLit->getValueAsApproximateDouble();
51 }
52 
53 // Given the scale of a duration and a `Multiplier`, determine if `Multiplier`
54 // would produce a new scale. If so, return a tuple containing the new scale
55 // and a suitable Multipler for that scale, otherwise `None`.
56 static llvm::Optional<std::tuple<DurationScale, double>>
57 GetNewScaleSingleStep(DurationScale OldScale, double Multiplier) {
58  switch (OldScale) {
59  case DurationScale::Hours:
60  if (Multiplier <= 1.0 / 60.0)
61  return std::make_tuple(DurationScale::Minutes, Multiplier * 60.0);
62  break;
63 
64  case DurationScale::Minutes:
65  if (Multiplier >= 60.0)
66  return std::make_tuple(DurationScale::Hours, Multiplier / 60.0);
67  if (Multiplier <= 1.0 / 60.0)
68  return std::make_tuple(DurationScale::Seconds, Multiplier * 60.0);
69  break;
70 
71  case DurationScale::Seconds:
72  if (Multiplier >= 60.0)
73  return std::make_tuple(DurationScale::Minutes, Multiplier / 60.0);
74  if (Multiplier <= 1e-3)
75  return std::make_tuple(DurationScale::Milliseconds, Multiplier * 1e3);
76  break;
77 
78  case DurationScale::Milliseconds:
79  if (Multiplier >= 1e3)
80  return std::make_tuple(DurationScale::Seconds, Multiplier / 1e3);
81  if (Multiplier <= 1e-3)
82  return std::make_tuple(DurationScale::Microseconds, Multiplier * 1e3);
83  break;
84 
85  case DurationScale::Microseconds:
86  if (Multiplier >= 1e3)
87  return std::make_tuple(DurationScale::Milliseconds, Multiplier / 1e3);
88  if (Multiplier <= 1e-3)
89  return std::make_tuple(DurationScale::Nanoseconds, Multiplier * 1e-3);
90  break;
91 
92  case DurationScale::Nanoseconds:
93  if (Multiplier >= 1e3)
94  return std::make_tuple(DurationScale::Microseconds, Multiplier / 1e3);
95  break;
96  }
97 
98  return llvm::None;
99 }
100 
101 // Given the scale of a duration and a `Multiplier`, determine if `Multiplier`
102 // would produce a new scale. If so, return it, otherwise `None`.
103 static llvm::Optional<DurationScale> GetNewScale(DurationScale OldScale,
104  double Multiplier) {
105  while (Multiplier != 1.0) {
106  llvm::Optional<std::tuple<DurationScale, double>> result =
107  GetNewScaleSingleStep(OldScale, Multiplier);
108  if (!result)
109  break;
110  if (std::get<1>(*result) == 1.0)
111  return std::get<0>(*result);
112  Multiplier = std::get<1>(*result);
113  OldScale = std::get<0>(*result);
114  }
115 
116  return llvm::None;
117 }
118 
119 void DurationFactoryScaleCheck::registerMatchers(MatchFinder *Finder) {
120  Finder->addMatcher(
121  callExpr(
122  callee(functionDecl(DurationFactoryFunction()).bind("call_decl")),
123  hasArgument(
124  0,
125  ignoringImpCasts(anyOf(
126  cxxFunctionalCastExpr(
127  hasDestinationType(
128  anyOf(isInteger(), realFloatingPointType())),
129  hasSourceExpression(initListExpr())),
130  integerLiteral(equals(0)), floatLiteral(equals(0.0)),
131  binaryOperator(hasOperatorName("*"),
132  hasEitherOperand(ignoringImpCasts(
133  anyOf(integerLiteral(), floatLiteral()))))
134  .bind("mult_binop"),
135  binaryOperator(hasOperatorName("/"), hasRHS(floatLiteral()))
136  .bind("div_binop")))))
137  .bind("call"),
138  this);
139 }
140 
141 void DurationFactoryScaleCheck::check(const MatchFinder::MatchResult &Result) {
142  const auto *Call = Result.Nodes.getNodeAs<CallExpr>("call");
143 
144  // Don't try to replace things inside of macro definitions.
145  if (Call->getExprLoc().isMacroID())
146  return;
147 
148  const Expr *Arg = Call->getArg(0)->IgnoreParenImpCasts();
149  // Arguments which are macros are ignored.
150  if (Arg->getBeginLoc().isMacroID())
151  return;
152 
153  // We first handle the cases of literal zero (both float and integer).
154  if (IsLiteralZero(Result, *Arg)) {
155  diag(Call->getBeginLoc(),
156  "use ZeroDuration() for zero-length time intervals")
157  << FixItHint::CreateReplacement(Call->getSourceRange(),
158  "absl::ZeroDuration()");
159  return;
160  }
161 
162  const auto *CallDecl = Result.Nodes.getNodeAs<FunctionDecl>("call_decl");
163  llvm::Optional<DurationScale> MaybeScale =
164  getScaleForFactory(CallDecl->getName());
165  if (!MaybeScale)
166  return;
167 
168  DurationScale Scale = *MaybeScale;
169  const Expr *Remainder;
170  llvm::Optional<DurationScale> NewScale;
171 
172  // We next handle the cases of multiplication and division.
173  if (const auto *MultBinOp =
174  Result.Nodes.getNodeAs<BinaryOperator>("mult_binop")) {
175  // For multiplication, we need to look at both operands, and consider the
176  // cases where a user is multiplying by something such as 1e-3.
177 
178  // First check the LHS
179  const auto *IntLit = llvm::dyn_cast<IntegerLiteral>(MultBinOp->getLHS());
180  const auto *FloatLit = llvm::dyn_cast<FloatingLiteral>(MultBinOp->getLHS());
181  if (IntLit || FloatLit) {
182  NewScale = GetNewScale(Scale, GetValue(IntLit, FloatLit));
183  if (NewScale)
184  Remainder = MultBinOp->getRHS();
185  }
186 
187  // If we weren't able to scale based on the LHS, check the RHS
188  if (!NewScale) {
189  IntLit = llvm::dyn_cast<IntegerLiteral>(MultBinOp->getRHS());
190  FloatLit = llvm::dyn_cast<FloatingLiteral>(MultBinOp->getRHS());
191  if (IntLit || FloatLit) {
192  NewScale = GetNewScale(Scale, GetValue(IntLit, FloatLit));
193  if (NewScale)
194  Remainder = MultBinOp->getLHS();
195  }
196  }
197  } else if (const auto *DivBinOp =
198  Result.Nodes.getNodeAs<BinaryOperator>("div_binop")) {
199  // We next handle division.
200  // For division, we only check the RHS.
201  const auto *FloatLit = llvm::dyn_cast<FloatingLiteral>(DivBinOp->getRHS());
202 
203  llvm::Optional<DurationScale> NewScale =
204  GetNewScale(Scale, 1.0 / FloatLit->getValueAsApproximateDouble());
205  if (NewScale) {
206  const Expr *Remainder = DivBinOp->getLHS();
207 
208  // We've found an appropriate scaling factor and the new scale, so output
209  // the relevant fix.
210  diag(Call->getBeginLoc(), "internal duration scaling can be removed")
211  << FixItHint::CreateReplacement(
212  Call->getSourceRange(),
213  (llvm::Twine(getFactoryForScale(*NewScale)) + "(" +
214  tooling::fixit::getText(*Remainder, *Result.Context) + ")")
215  .str());
216  }
217  }
218 
219  if (NewScale) {
220  assert(Remainder && "No remainder found");
221  // We've found an appropriate scaling factor and the new scale, so output
222  // the relevant fix.
223  diag(Call->getBeginLoc(), "internal duration scaling can be removed")
224  << FixItHint::CreateReplacement(
225  Call->getSourceRange(),
226  (llvm::Twine(getFactoryForScale(*NewScale)) + "(" +
227  tooling::fixit::getText(*Remainder, *Result.Context) + ")")
228  .str());
229  }
230  return;
231 }
232 
233 } // namespace abseil
234 } // namespace tidy
235 } // namespace clang
static llvm::Optional< std::tuple< DurationScale, double > > GetNewScaleSingleStep(DurationScale OldScale, double Multiplier)
llvm::StringRef getFactoryForScale(DurationScale Scale)
Returns the factory function name for a given Scale.
static llvm::Optional< DurationScale > GetNewScale(DurationScale OldScale, double Multiplier)
llvm::Optional< llvm::Expected< tooling::AtomicChanges > > Result
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//
static double GetValue(const IntegerLiteral *IntLit, const FloatingLiteral *FloatLit)
static llvm::Optional< DurationScale > getScaleForFactory(llvm::StringRef FactoryName)
bool IsLiteralZero(const MatchFinder::MatchResult &Result, const Expr &Node)
Returns true if Node is a value which evaluates to a literal 0.
DurationScale
Duration factory and conversion scales.