brintos

brintos / llvm-project-archived public Read only

0
0
Text · 10.7 KiB · a78d07d Raw
276 lines · cpp
1//===----------------------------------------------------------------------===//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===//8 9#include <array>10#include <cmath>11#include <optional>12 13#include "DurationRewriter.h"14#include "clang/Tooling/FixIt.h"15 16using namespace clang::ast_matchers;17 18namespace clang::tidy::abseil {19 20/// Returns an integer if the fractional part of a `FloatingLiteral` is `0`.21static std::optional<llvm::APSInt>22truncateIfIntegral(const FloatingLiteral &FloatLiteral) {23  const double Value = FloatLiteral.getValueAsApproximateDouble();24  if (std::fmod(Value, 1) == 0) {25    if (Value >= static_cast<double>(1U << 31))26      return std::nullopt;27 28    return llvm::APSInt::get(static_cast<int64_t>(Value));29  }30  return std::nullopt;31}32 33const std::pair<llvm::StringRef, llvm::StringRef> &34getDurationInverseForScale(DurationScale Scale) {35  static constexpr std::array<std::pair<llvm::StringRef, llvm::StringRef>, 6>36      InverseMap = {{37          {"::absl::ToDoubleHours", "::absl::ToInt64Hours"},38          {"::absl::ToDoubleMinutes", "::absl::ToInt64Minutes"},39          {"::absl::ToDoubleSeconds", "::absl::ToInt64Seconds"},40          {"::absl::ToDoubleMilliseconds", "::absl::ToInt64Milliseconds"},41          {"::absl::ToDoubleMicroseconds", "::absl::ToInt64Microseconds"},42          {"::absl::ToDoubleNanoseconds", "::absl::ToInt64Nanoseconds"},43      }};44 45  return InverseMap[llvm::to_underlying(Scale)];46}47 48/// If `Node` is a call to the inverse of `Scale`, return that inverse's49/// argument, otherwise std::nullopt.50static std::optional<std::string>51rewriteInverseDurationCall(const MatchFinder::MatchResult &Result,52                           DurationScale Scale, const Expr &Node) {53  const std::pair<llvm::StringRef, llvm::StringRef> &InverseFunctions =54      getDurationInverseForScale(Scale);55  if (const auto *MaybeCallArg = selectFirst<const Expr>(56          "e",57          match(callExpr(callee(functionDecl(hasAnyName(58                             InverseFunctions.first, InverseFunctions.second))),59                         hasArgument(0, expr().bind("e"))),60                Node, *Result.Context))) {61    return tooling::fixit::getText(*MaybeCallArg, *Result.Context).str();62  }63 64  return std::nullopt;65}66 67/// If `Node` is a call to the inverse of `Scale`, return that inverse's68/// argument, otherwise std::nullopt.69static std::optional<std::string>70rewriteInverseTimeCall(const MatchFinder::MatchResult &Result,71                       DurationScale Scale, const Expr &Node) {72  const llvm::StringRef InverseFunction = getTimeInverseForScale(Scale);73  if (const auto *MaybeCallArg = selectFirst<const Expr>(74          "e", match(callExpr(callee(functionDecl(hasName(InverseFunction))),75                              hasArgument(0, expr().bind("e"))),76                     Node, *Result.Context))) {77    return tooling::fixit::getText(*MaybeCallArg, *Result.Context).str();78  }79 80  return std::nullopt;81}82 83/// Returns the factory function name for a given `Scale`.84llvm::StringRef getDurationFactoryForScale(DurationScale Scale) {85  static constexpr std::array<llvm::StringRef, 6> FactoryMap = {86      "absl::Hours",        "absl::Minutes",      "absl::Seconds",87      "absl::Milliseconds", "absl::Microseconds", "absl::Nanoseconds",88  };89 90  return FactoryMap[llvm::to_underlying(Scale)];91}92 93llvm::StringRef getTimeFactoryForScale(DurationScale Scale) {94  static constexpr std::array<llvm::StringRef, 6> FactoryMap = {95      "absl::FromUnixHours",  "absl::FromUnixMinutes", "absl::FromUnixSeconds",96      "absl::FromUnixMillis", "absl::FromUnixMicros",  "absl::FromUnixNanos",97  };98 99  return FactoryMap[llvm::to_underlying(Scale)];100}101 102/// Returns the Time factory function name for a given `Scale`.103llvm::StringRef getTimeInverseForScale(DurationScale Scale) {104  static constexpr std::array<llvm::StringRef, 6> InverseMap = {105      "absl::ToUnixHours",  "absl::ToUnixMinutes", "absl::ToUnixSeconds",106      "absl::ToUnixMillis", "absl::ToUnixMicros",  "absl::ToUnixNanos",107  };108 109  return InverseMap[llvm::to_underlying(Scale)];110}111 112/// Returns `true` if `Node` is a value which evaluates to a literal `0`.113bool isLiteralZero(const MatchFinder::MatchResult &Result, const Expr &Node) {114  auto ZeroMatcher =115      anyOf(integerLiteral(equals(0)), floatLiteral(equals(0.0)));116 117  // Check to see if we're using a zero directly.118  if (selectFirst<const clang::Expr>(119          "val", match(expr(ignoringImpCasts(ZeroMatcher)).bind("val"), Node,120                       *Result.Context)) != nullptr)121    return true;122 123  // Now check to see if we're using a functional cast with a scalar124  // initializer expression, e.g. `int{0}`.125  if (selectFirst<const clang::Expr>(126          "val", match(cxxFunctionalCastExpr(127                           hasDestinationType(128                               anyOf(isInteger(), realFloatingPointType())),129                           hasSourceExpression(initListExpr(130                               hasInit(0, ignoringParenImpCasts(ZeroMatcher)))))131                           .bind("val"),132                       Node, *Result.Context)) != nullptr)133    return true;134 135  return false;136}137 138std::optional<std::string>139stripFloatCast(const ast_matchers::MatchFinder::MatchResult &Result,140               const Expr &Node) {141  if (const Expr *MaybeCastArg = selectFirst<const Expr>(142          "cast_arg",143          match(expr(anyOf(cxxStaticCastExpr(144                               hasDestinationType(realFloatingPointType()),145                               hasSourceExpression(expr().bind("cast_arg"))),146                           cStyleCastExpr(147                               hasDestinationType(realFloatingPointType()),148                               hasSourceExpression(expr().bind("cast_arg"))),149                           cxxFunctionalCastExpr(150                               hasDestinationType(realFloatingPointType()),151                               hasSourceExpression(expr().bind("cast_arg"))))),152                Node, *Result.Context)))153    return tooling::fixit::getText(*MaybeCastArg, *Result.Context).str();154 155  return std::nullopt;156}157 158std::optional<std::string>159stripFloatLiteralFraction(const MatchFinder::MatchResult &Result,160                          const Expr &Node) {161  if (const auto *LitFloat = llvm::dyn_cast<FloatingLiteral>(&Node))162    // Attempt to simplify a `Duration` factory call with a literal argument.163    if (std::optional<llvm::APSInt> IntValue = truncateIfIntegral(*LitFloat))164      return toString(*IntValue, /*radix=*/10);165 166  return std::nullopt;167}168 169std::string simplifyDurationFactoryArg(const MatchFinder::MatchResult &Result,170                                       const Expr &Node) {171  // Check for an explicit cast to `float` or `double`.172  if (std::optional<std::string> MaybeArg = stripFloatCast(Result, Node))173    return *MaybeArg;174 175  // Check for floats without fractional components.176  if (std::optional<std::string> MaybeArg =177          stripFloatLiteralFraction(Result, Node))178    return *MaybeArg;179 180  // We couldn't simplify any further, so return the argument text.181  return tooling::fixit::getText(Node, *Result.Context).str();182}183 184std::optional<DurationScale> getScaleForDurationInverse(llvm::StringRef Name) {185  static const llvm::StringMap<DurationScale> ScaleMap(186      {{"ToDoubleHours", DurationScale::Hours},187       {"ToInt64Hours", DurationScale::Hours},188       {"ToDoubleMinutes", DurationScale::Minutes},189       {"ToInt64Minutes", DurationScale::Minutes},190       {"ToDoubleSeconds", DurationScale::Seconds},191       {"ToInt64Seconds", DurationScale::Seconds},192       {"ToDoubleMilliseconds", DurationScale::Milliseconds},193       {"ToInt64Milliseconds", DurationScale::Milliseconds},194       {"ToDoubleMicroseconds", DurationScale::Microseconds},195       {"ToInt64Microseconds", DurationScale::Microseconds},196       {"ToDoubleNanoseconds", DurationScale::Nanoseconds},197       {"ToInt64Nanoseconds", DurationScale::Nanoseconds}});198 199  auto ScaleIter = ScaleMap.find(Name);200  if (ScaleIter == ScaleMap.end())201    return std::nullopt;202 203  return ScaleIter->second;204}205 206std::optional<DurationScale> getScaleForTimeInverse(llvm::StringRef Name) {207  static const llvm::StringMap<DurationScale> ScaleMap(208      {{"ToUnixHours", DurationScale::Hours},209       {"ToUnixMinutes", DurationScale::Minutes},210       {"ToUnixSeconds", DurationScale::Seconds},211       {"ToUnixMillis", DurationScale::Milliseconds},212       {"ToUnixMicros", DurationScale::Microseconds},213       {"ToUnixNanos", DurationScale::Nanoseconds}});214 215  auto ScaleIter = ScaleMap.find(Name);216  if (ScaleIter == ScaleMap.end())217    return std::nullopt;218 219  return ScaleIter->second;220}221 222std::string rewriteExprFromNumberToDuration(223    const ast_matchers::MatchFinder::MatchResult &Result, DurationScale Scale,224    const Expr *Node) {225  const Expr &RootNode = *Node->IgnoreParenImpCasts();226 227  // First check to see if we can undo a complementary function call.228  if (std::optional<std::string> MaybeRewrite =229          rewriteInverseDurationCall(Result, Scale, RootNode))230    return *MaybeRewrite;231 232  if (isLiteralZero(Result, RootNode))233    return {"absl::ZeroDuration()"};234 235  return (llvm::Twine(getDurationFactoryForScale(Scale)) + "(" +236          simplifyDurationFactoryArg(Result, RootNode) + ")")237      .str();238}239 240std::string rewriteExprFromNumberToTime(241    const ast_matchers::MatchFinder::MatchResult &Result, DurationScale Scale,242    const Expr *Node) {243  const Expr &RootNode = *Node->IgnoreParenImpCasts();244 245  // First check to see if we can undo a complementary function call.246  if (std::optional<std::string> MaybeRewrite =247          rewriteInverseTimeCall(Result, Scale, RootNode))248    return *MaybeRewrite;249 250  if (isLiteralZero(Result, RootNode))251    return {"absl::UnixEpoch()"};252 253  return (llvm::Twine(getTimeFactoryForScale(Scale)) + "(" +254          tooling::fixit::getText(RootNode, *Result.Context) + ")")255      .str();256}257 258bool isInMacro(const MatchFinder::MatchResult &Result, const Expr *E) {259  if (!E->getBeginLoc().isMacroID())260    return false;261 262  SourceLocation Loc = E->getBeginLoc();263  // We want to get closer towards the initial macro typed into the source only264  // if the location is being expanded as a macro argument.265  while (Result.SourceManager->isMacroArgExpansion(Loc)) {266    // We are calling getImmediateMacroCallerLoc, but note it is essentially267    // equivalent to calling getImmediateSpellingLoc in this context according268    // to Clang implementation. We are not calling getImmediateSpellingLoc269    // because Clang comment says it "should not generally be used by clients."270    Loc = Result.SourceManager->getImmediateMacroCallerLoc(Loc);271  }272  return Loc.isMacroID();273}274 275} // namespace clang::tidy::abseil276