11 #include "clang/AST/ASTContext.h"
12 #include "clang/ASTMatchers/ASTMatchFinder.h"
13 #include "clang/Tooling/FixIt.h"
24 static llvm::Optional<DurationScale>
26 return llvm::StringSwitch<llvm::Optional<DurationScale>>(FactoryName)
27 .Case(
"Nanoseconds", DurationScale::Nanoseconds)
28 .Case(
"Microseconds", DurationScale::Microseconds)
29 .Case(
"Milliseconds", DurationScale::Milliseconds)
30 .Case(
"Seconds", DurationScale::Seconds)
31 .Case(
"Minutes", DurationScale::Minutes)
32 .Case(
"Hours", DurationScale::Hours)
38 static double GetValue(
const IntegerLiteral *IntLit,
39 const FloatingLiteral *FloatLit) {
41 return IntLit->getValue().getLimitedValue();
43 assert(FloatLit !=
nullptr &&
"Neither IntLit nor FloatLit set");
44 return FloatLit->getValueAsApproximateDouble();
50 static llvm::Optional<std::tuple<DurationScale, double>>
53 case DurationScale::Hours:
54 if (Multiplier <= 1.0 / 60.0)
55 return std::make_tuple(DurationScale::Minutes, Multiplier * 60.0);
58 case DurationScale::Minutes:
59 if (Multiplier >= 60.0)
60 return std::make_tuple(DurationScale::Hours, Multiplier / 60.0);
61 if (Multiplier <= 1.0 / 60.0)
62 return std::make_tuple(DurationScale::Seconds, Multiplier * 60.0);
65 case DurationScale::Seconds:
66 if (Multiplier >= 60.0)
67 return std::make_tuple(DurationScale::Minutes, Multiplier / 60.0);
68 if (Multiplier <= 1e-3)
69 return std::make_tuple(DurationScale::Milliseconds, Multiplier * 1e3);
72 case DurationScale::Milliseconds:
73 if (Multiplier >= 1e3)
74 return std::make_tuple(DurationScale::Seconds, Multiplier / 1e3);
75 if (Multiplier <= 1e-3)
76 return std::make_tuple(DurationScale::Microseconds, Multiplier * 1e3);
79 case DurationScale::Microseconds:
80 if (Multiplier >= 1e3)
81 return std::make_tuple(DurationScale::Milliseconds, Multiplier / 1e3);
82 if (Multiplier <= 1e-3)
83 return std::make_tuple(DurationScale::Nanoseconds, Multiplier * 1e-3);
86 case DurationScale::Nanoseconds:
87 if (Multiplier >= 1e3)
88 return std::make_tuple(DurationScale::Microseconds, Multiplier / 1e3);
99 while (Multiplier != 1.0) {
100 llvm::Optional<std::tuple<DurationScale, double>> result =
104 if (std::get<1>(*result) == 1.0)
105 return std::get<0>(*result);
106 Multiplier = std::get<1>(*result);
107 OldScale = std::get<0>(*result);
113 void DurationFactoryScaleCheck::registerMatchers(MatchFinder *Finder) {
116 callee(functionDecl(DurationFactoryFunction()).bind(
"call_decl")),
119 ignoringImpCasts(anyOf(
120 cxxFunctionalCastExpr(
122 anyOf(isInteger(), realFloatingPointType())),
123 hasSourceExpression(initListExpr())),
124 integerLiteral(equals(0)), floatLiteral(equals(0.0)),
125 binaryOperator(hasOperatorName(
"*"),
126 hasEitherOperand(ignoringImpCasts(
127 anyOf(integerLiteral(), floatLiteral()))))
129 binaryOperator(hasOperatorName(
"/"), hasRHS(floatLiteral()))
130 .bind(
"div_binop")))))
135 void DurationFactoryScaleCheck::check(
const MatchFinder::MatchResult &Result) {
136 const auto *Call = Result.Nodes.getNodeAs<CallExpr>(
"call");
139 if (Call->getExprLoc().isMacroID())
142 const Expr *Arg = Call->getArg(0)->IgnoreParenImpCasts();
144 if (Arg->getBeginLoc().isMacroID())
149 diag(Call->getBeginLoc(),
150 "use ZeroDuration() for zero-length time intervals")
151 << FixItHint::CreateReplacement(Call->getSourceRange(),
152 "absl::ZeroDuration()");
156 const auto *CallDecl = Result.Nodes.getNodeAs<FunctionDecl>(
"call_decl");
157 llvm::Optional<DurationScale> MaybeScale =
163 const Expr *Remainder;
164 llvm::Optional<DurationScale> NewScale;
167 if (
const auto *MultBinOp =
168 Result.Nodes.getNodeAs<BinaryOperator>(
"mult_binop")) {
173 const auto *IntLit = llvm::dyn_cast<IntegerLiteral>(MultBinOp->getLHS());
174 const auto *FloatLit = llvm::dyn_cast<FloatingLiteral>(MultBinOp->getLHS());
175 if (IntLit || FloatLit) {
178 Remainder = MultBinOp->getRHS();
183 IntLit = llvm::dyn_cast<IntegerLiteral>(MultBinOp->getRHS());
184 FloatLit = llvm::dyn_cast<FloatingLiteral>(MultBinOp->getRHS());
185 if (IntLit || FloatLit) {
188 Remainder = MultBinOp->getLHS();
191 }
else if (
const auto *DivBinOp =
192 Result.Nodes.getNodeAs<BinaryOperator>(
"div_binop")) {
195 const auto *FloatLit = llvm::dyn_cast<FloatingLiteral>(DivBinOp->getRHS());
197 llvm::Optional<DurationScale> NewScale =
198 GetNewScale(Scale, 1.0 / FloatLit->getValueAsApproximateDouble());
200 const Expr *Remainder = DivBinOp->getLHS();
204 diag(Call->getBeginLoc(),
"internal duration scaling can be removed")
205 << FixItHint::CreateReplacement(
206 Call->getSourceRange(),
208 tooling::fixit::getText(*Remainder, *Result.Context) +
")")
214 assert(Remainder &&
"No remainder found");
217 diag(Call->getBeginLoc(),
"internal duration scaling can be removed")
218 << FixItHint::CreateReplacement(
219 Call->getSourceRange(),
221 tooling::fixit::getText(*Remainder, *Result.Context) +
")")