123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323 |
- //===--- DurationRewriter.cpp - clang-tidy --------------------------------===//
- //
- // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
- // See https://llvm.org/LICENSE.txt for license information.
- // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
- //
- //===----------------------------------------------------------------------===//
- #include <cmath>
- #include <optional>
- #include "DurationRewriter.h"
- #include "clang/Tooling/FixIt.h"
- #include "llvm/ADT/IndexedMap.h"
- using namespace clang::ast_matchers;
- namespace clang::tidy::abseil {
- struct DurationScale2IndexFunctor {
- using argument_type = DurationScale;
- unsigned operator()(DurationScale Scale) const {
- return static_cast<unsigned>(Scale);
- }
- };
- /// Returns an integer if the fractional part of a `FloatingLiteral` is `0`.
- static std::optional<llvm::APSInt>
- truncateIfIntegral(const FloatingLiteral &FloatLiteral) {
- double Value = FloatLiteral.getValueAsApproximateDouble();
- if (std::fmod(Value, 1) == 0) {
- if (Value >= static_cast<double>(1u << 31))
- return std::nullopt;
- return llvm::APSInt::get(static_cast<int64_t>(Value));
- }
- return std::nullopt;
- }
- const std::pair<llvm::StringRef, llvm::StringRef> &
- getDurationInverseForScale(DurationScale Scale) {
- static const llvm::IndexedMap<std::pair<llvm::StringRef, llvm::StringRef>,
- DurationScale2IndexFunctor>
- InverseMap = []() {
- // TODO: Revisit the immediately invoked lambda technique when
- // IndexedMap gets an initializer list constructor.
- llvm::IndexedMap<std::pair<llvm::StringRef, llvm::StringRef>,
- DurationScale2IndexFunctor>
- InverseMap;
- InverseMap.resize(6);
- InverseMap[DurationScale::Hours] =
- std::make_pair("::absl::ToDoubleHours", "::absl::ToInt64Hours");
- InverseMap[DurationScale::Minutes] =
- std::make_pair("::absl::ToDoubleMinutes", "::absl::ToInt64Minutes");
- InverseMap[DurationScale::Seconds] =
- std::make_pair("::absl::ToDoubleSeconds", "::absl::ToInt64Seconds");
- InverseMap[DurationScale::Milliseconds] = std::make_pair(
- "::absl::ToDoubleMilliseconds", "::absl::ToInt64Milliseconds");
- InverseMap[DurationScale::Microseconds] = std::make_pair(
- "::absl::ToDoubleMicroseconds", "::absl::ToInt64Microseconds");
- InverseMap[DurationScale::Nanoseconds] = std::make_pair(
- "::absl::ToDoubleNanoseconds", "::absl::ToInt64Nanoseconds");
- return InverseMap;
- }();
- return InverseMap[Scale];
- }
- /// If `Node` is a call to the inverse of `Scale`, return that inverse's
- /// argument, otherwise std::nullopt.
- static std::optional<std::string>
- rewriteInverseDurationCall(const MatchFinder::MatchResult &Result,
- DurationScale Scale, const Expr &Node) {
- const std::pair<llvm::StringRef, llvm::StringRef> &InverseFunctions =
- getDurationInverseForScale(Scale);
- if (const auto *MaybeCallArg = selectFirst<const Expr>(
- "e",
- match(callExpr(callee(functionDecl(hasAnyName(
- InverseFunctions.first, InverseFunctions.second))),
- hasArgument(0, expr().bind("e"))),
- Node, *Result.Context))) {
- return tooling::fixit::getText(*MaybeCallArg, *Result.Context).str();
- }
- return std::nullopt;
- }
- /// If `Node` is a call to the inverse of `Scale`, return that inverse's
- /// argument, otherwise std::nullopt.
- static std::optional<std::string>
- rewriteInverseTimeCall(const MatchFinder::MatchResult &Result,
- DurationScale Scale, const Expr &Node) {
- llvm::StringRef InverseFunction = getTimeInverseForScale(Scale);
- if (const auto *MaybeCallArg = selectFirst<const Expr>(
- "e", match(callExpr(callee(functionDecl(hasName(InverseFunction))),
- hasArgument(0, expr().bind("e"))),
- Node, *Result.Context))) {
- return tooling::fixit::getText(*MaybeCallArg, *Result.Context).str();
- }
- return std::nullopt;
- }
- /// Returns the factory function name for a given `Scale`.
- llvm::StringRef getDurationFactoryForScale(DurationScale Scale) {
- switch (Scale) {
- case DurationScale::Hours:
- return "absl::Hours";
- case DurationScale::Minutes:
- return "absl::Minutes";
- case DurationScale::Seconds:
- return "absl::Seconds";
- case DurationScale::Milliseconds:
- return "absl::Milliseconds";
- case DurationScale::Microseconds:
- return "absl::Microseconds";
- case DurationScale::Nanoseconds:
- return "absl::Nanoseconds";
- }
- llvm_unreachable("unknown scaling factor");
- }
- llvm::StringRef getTimeFactoryForScale(DurationScale Scale) {
- switch (Scale) {
- case DurationScale::Hours:
- return "absl::FromUnixHours";
- case DurationScale::Minutes:
- return "absl::FromUnixMinutes";
- case DurationScale::Seconds:
- return "absl::FromUnixSeconds";
- case DurationScale::Milliseconds:
- return "absl::FromUnixMillis";
- case DurationScale::Microseconds:
- return "absl::FromUnixMicros";
- case DurationScale::Nanoseconds:
- return "absl::FromUnixNanos";
- }
- llvm_unreachable("unknown scaling factor");
- }
- /// Returns the Time factory function name for a given `Scale`.
- llvm::StringRef getTimeInverseForScale(DurationScale Scale) {
- switch (Scale) {
- case DurationScale::Hours:
- return "absl::ToUnixHours";
- case DurationScale::Minutes:
- return "absl::ToUnixMinutes";
- case DurationScale::Seconds:
- return "absl::ToUnixSeconds";
- case DurationScale::Milliseconds:
- return "absl::ToUnixMillis";
- case DurationScale::Microseconds:
- return "absl::ToUnixMicros";
- case DurationScale::Nanoseconds:
- return "absl::ToUnixNanos";
- }
- llvm_unreachable("unknown scaling factor");
- }
- /// Returns `true` if `Node` is a value which evaluates to a literal `0`.
- bool isLiteralZero(const MatchFinder::MatchResult &Result, const Expr &Node) {
- auto ZeroMatcher =
- anyOf(integerLiteral(equals(0)), floatLiteral(equals(0.0)));
- // Check to see if we're using a zero directly.
- if (selectFirst<const clang::Expr>(
- "val", match(expr(ignoringImpCasts(ZeroMatcher)).bind("val"), Node,
- *Result.Context)) != nullptr)
- return true;
- // Now check to see if we're using a functional cast with a scalar
- // initializer expression, e.g. `int{0}`.
- if (selectFirst<const clang::Expr>(
- "val", match(cxxFunctionalCastExpr(
- hasDestinationType(
- anyOf(isInteger(), realFloatingPointType())),
- hasSourceExpression(initListExpr(
- hasInit(0, ignoringParenImpCasts(ZeroMatcher)))))
- .bind("val"),
- Node, *Result.Context)) != nullptr)
- return true;
- return false;
- }
- std::optional<std::string>
- stripFloatCast(const ast_matchers::MatchFinder::MatchResult &Result,
- const Expr &Node) {
- if (const Expr *MaybeCastArg = selectFirst<const Expr>(
- "cast_arg",
- match(expr(anyOf(cxxStaticCastExpr(
- hasDestinationType(realFloatingPointType()),
- hasSourceExpression(expr().bind("cast_arg"))),
- cStyleCastExpr(
- hasDestinationType(realFloatingPointType()),
- hasSourceExpression(expr().bind("cast_arg"))),
- cxxFunctionalCastExpr(
- hasDestinationType(realFloatingPointType()),
- hasSourceExpression(expr().bind("cast_arg"))))),
- Node, *Result.Context)))
- return tooling::fixit::getText(*MaybeCastArg, *Result.Context).str();
- return std::nullopt;
- }
- std::optional<std::string>
- stripFloatLiteralFraction(const MatchFinder::MatchResult &Result,
- const Expr &Node) {
- if (const auto *LitFloat = llvm::dyn_cast<FloatingLiteral>(&Node))
- // Attempt to simplify a `Duration` factory call with a literal argument.
- if (std::optional<llvm::APSInt> IntValue = truncateIfIntegral(*LitFloat))
- return toString(*IntValue, /*radix=*/10);
- return std::nullopt;
- }
- std::string simplifyDurationFactoryArg(const MatchFinder::MatchResult &Result,
- const Expr &Node) {
- // Check for an explicit cast to `float` or `double`.
- if (std::optional<std::string> MaybeArg = stripFloatCast(Result, Node))
- return *MaybeArg;
- // Check for floats without fractional components.
- if (std::optional<std::string> MaybeArg =
- stripFloatLiteralFraction(Result, Node))
- return *MaybeArg;
- // We couldn't simplify any further, so return the argument text.
- return tooling::fixit::getText(Node, *Result.Context).str();
- }
- std::optional<DurationScale> getScaleForDurationInverse(llvm::StringRef Name) {
- static const llvm::StringMap<DurationScale> ScaleMap(
- {{"ToDoubleHours", DurationScale::Hours},
- {"ToInt64Hours", DurationScale::Hours},
- {"ToDoubleMinutes", DurationScale::Minutes},
- {"ToInt64Minutes", DurationScale::Minutes},
- {"ToDoubleSeconds", DurationScale::Seconds},
- {"ToInt64Seconds", DurationScale::Seconds},
- {"ToDoubleMilliseconds", DurationScale::Milliseconds},
- {"ToInt64Milliseconds", DurationScale::Milliseconds},
- {"ToDoubleMicroseconds", DurationScale::Microseconds},
- {"ToInt64Microseconds", DurationScale::Microseconds},
- {"ToDoubleNanoseconds", DurationScale::Nanoseconds},
- {"ToInt64Nanoseconds", DurationScale::Nanoseconds}});
- auto ScaleIter = ScaleMap.find(std::string(Name));
- if (ScaleIter == ScaleMap.end())
- return std::nullopt;
- return ScaleIter->second;
- }
- std::optional<DurationScale> getScaleForTimeInverse(llvm::StringRef Name) {
- static const llvm::StringMap<DurationScale> ScaleMap(
- {{"ToUnixHours", DurationScale::Hours},
- {"ToUnixMinutes", DurationScale::Minutes},
- {"ToUnixSeconds", DurationScale::Seconds},
- {"ToUnixMillis", DurationScale::Milliseconds},
- {"ToUnixMicros", DurationScale::Microseconds},
- {"ToUnixNanos", DurationScale::Nanoseconds}});
- auto ScaleIter = ScaleMap.find(std::string(Name));
- if (ScaleIter == ScaleMap.end())
- return std::nullopt;
- return ScaleIter->second;
- }
- std::string rewriteExprFromNumberToDuration(
- const ast_matchers::MatchFinder::MatchResult &Result, DurationScale Scale,
- const Expr *Node) {
- const Expr &RootNode = *Node->IgnoreParenImpCasts();
- // First check to see if we can undo a complementary function call.
- if (std::optional<std::string> MaybeRewrite =
- rewriteInverseDurationCall(Result, Scale, RootNode))
- return *MaybeRewrite;
- if (isLiteralZero(Result, RootNode))
- return std::string("absl::ZeroDuration()");
- return (llvm::Twine(getDurationFactoryForScale(Scale)) + "(" +
- simplifyDurationFactoryArg(Result, RootNode) + ")")
- .str();
- }
- std::string rewriteExprFromNumberToTime(
- const ast_matchers::MatchFinder::MatchResult &Result, DurationScale Scale,
- const Expr *Node) {
- const Expr &RootNode = *Node->IgnoreParenImpCasts();
- // First check to see if we can undo a complementary function call.
- if (std::optional<std::string> MaybeRewrite =
- rewriteInverseTimeCall(Result, Scale, RootNode))
- return *MaybeRewrite;
- if (isLiteralZero(Result, RootNode))
- return std::string("absl::UnixEpoch()");
- return (llvm::Twine(getTimeFactoryForScale(Scale)) + "(" +
- tooling::fixit::getText(RootNode, *Result.Context) + ")")
- .str();
- }
- bool isInMacro(const MatchFinder::MatchResult &Result, const Expr *E) {
- if (!E->getBeginLoc().isMacroID())
- return false;
- SourceLocation Loc = E->getBeginLoc();
- // We want to get closer towards the initial macro typed into the source only
- // if the location is being expanded as a macro argument.
- while (Result.SourceManager->isMacroArgExpansion(Loc)) {
- // We are calling getImmediateMacroCallerLoc, but note it is essentially
- // equivalent to calling getImmediateSpellingLoc in this context according
- // to Clang implementation. We are not calling getImmediateSpellingLoc
- // because Clang comment says it "should not generally be used by clients."
- Loc = Result.SourceManager->getImmediateMacroCallerLoc(Loc);
- }
- return Loc.isMacroID();
- }
- } // namespace clang::tidy::abseil
|