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
10 #include "DurationFactoryScaleCheck.h"
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
DurationRewriter.h
clang::ast_matchers
Definition: AbseilMatcher.h:15
clang::tidy::abseil::GetNewScaleSingleStep
static llvm::Optional< std::tuple< DurationScale, double > > GetNewScaleSingleStep(DurationScale OldScale, double Multiplier)
Definition: DurationFactoryScaleCheck.cpp:57
DurationFactoryScaleCheck.h
clang::tidy::abseil::getFactoryForScale
llvm::StringRef getFactoryForScale(DurationScale Scale)
Returns the factory function name for a given Scale.
Definition: DurationRewriter.cpp:89
clang::tidy::abseil::GetNewScale
static llvm::Optional< DurationScale > GetNewScale(DurationScale OldScale, double Multiplier)
Definition: DurationFactoryScaleCheck.cpp:103
Result
llvm::Optional< llvm::Expected< tooling::AtomicChanges > > Result
Definition: ClangdServer.cpp:60
clang
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//
Definition: ChangeNamespace.cpp:18
clang::tidy::abseil::GetValue
static double GetValue(const IntegerLiteral *IntLit, const FloatingLiteral *FloatLit)
Definition: DurationFactoryScaleCheck.cpp:44
clang::tidy::abseil::getScaleForFactory
static llvm::Optional< DurationScale > getScaleForFactory(llvm::StringRef FactoryName)
Definition: DurationFactoryScaleCheck.cpp:26
clang::tidy::abseil::IsLiteralZero
bool IsLiteralZero(const MatchFinder::MatchResult &Result, const Expr &Node)
Returns true if Node is a value which evaluates to a literal 0.
Definition: DurationRewriter.cpp:108
clang::tidy::abseil::DurationScale
DurationScale
Duration factory and conversion scales.
Definition: DurationRewriter.h:22