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
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 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> &
41 getInverseForScale(DurationScale Scale) {
42 static const llvm::IndexedMap<std::pair<llvm::StringRef, llvm::StringRef>,
43 DurationScale2IndexFunctor>
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>,
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 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
201 std::string rewriteExprFromNumberToDuration(
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
DurationRewriter.h
clang::ast_matchers
Definition: AbseilMatcher.h:15
clang::tidy::abseil::rewriteExprFromNumberToDuration
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 ...
Definition: DurationRewriter.cpp:201
clang::tidy::abseil::DurationScale2IndexFunctor
Definition: DurationRewriter.cpp:20
clang::tidy::abseil::rewriteInverseDurationCall
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...
Definition: DurationRewriter.cpp:72
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::DurationScale2IndexFunctor::operator()
unsigned operator()(DurationScale Scale) const
Definition: DurationRewriter.cpp:22
Name
static constexpr llvm::StringLiteral Name
Definition: UppercaseLiteralSuffixCheck.cpp:28
Result
llvm::Optional< llvm::Expected< tooling::AtomicChanges > > Result
Definition: ClangdServer.cpp:60
clang::tidy::abseil::simplifyDurationFactoryArg
std::string simplifyDurationFactoryArg(const MatchFinder::MatchResult &Result, const Expr &Node)
Definition: DurationRewriter.cpp:164
clang::tidy::abseil::getScaleForInverse
llvm::Optional< DurationScale > getScaleForInverse(llvm::StringRef Name)
Given the name of an inverse Duration function (e.g., ToDoubleSeconds), return its DurationScale...
Definition: DurationRewriter.cpp:179
clang
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//
Definition: ChangeNamespace.cpp:18
clang::tidy::abseil::truncateIfIntegral
static llvm::Optional< llvm::APSInt > truncateIfIntegral(const FloatingLiteral &FloatLiteral)
Returns an integer if the fractional part of a FloatingLiteral is 0.
Definition: DurationRewriter.cpp:29
clang::tidy::abseil::stripFloatCast
llvm::Optional< std::string > stripFloatCast(const ast_matchers::MatchFinder::MatchResult &Result, const Expr &Node)
Possibly strip a floating point cast expression.
Definition: DurationRewriter.cpp:134
clang::tidy::abseil::stripFloatLiteralFraction
llvm::Optional< std::string > stripFloatLiteralFraction(const MatchFinder::MatchResult &Result, const Expr &Node)
Definition: DurationRewriter.cpp:154
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::getInverseForScale
const std::pair< llvm::StringRef, llvm::StringRef > & getInverseForScale(DurationScale Scale)
Given a Scale return the fully qualified inverse functions for it.
Definition: DurationRewriter.cpp:41
clang::tidy::abseil::DurationScale
DurationScale
Duration factory and conversion scales.
Definition: DurationRewriter.h:22