brintos

brintos / llvm-project-archived public Read only

0
0
Text · 8.5 KiB · 9e403fb Raw
226 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 "DurationFactoryScaleCheck.h"10#include "DurationRewriter.h"11#include "clang/AST/ASTContext.h"12#include "clang/ASTMatchers/ASTMatchFinder.h"13#include "clang/Tooling/FixIt.h"14#include <optional>15 16using namespace clang::ast_matchers;17 18namespace clang::tidy::abseil {19 20// Given the name of a duration factory function, return the appropriate21// `DurationScale` for that factory.  If no factory can be found for22// `FactoryName`, return `std::nullopt`.23static std::optional<DurationScale>24getScaleForFactory(llvm::StringRef FactoryName) {25  return llvm::StringSwitch<std::optional<DurationScale>>(FactoryName)26      .Case("Nanoseconds", DurationScale::Nanoseconds)27      .Case("Microseconds", DurationScale::Microseconds)28      .Case("Milliseconds", DurationScale::Milliseconds)29      .Case("Seconds", DurationScale::Seconds)30      .Case("Minutes", DurationScale::Minutes)31      .Case("Hours", DurationScale::Hours)32      .Default(std::nullopt);33}34 35// Given either an integer or float literal, return its value.36// One and only one of `IntLit` and `FloatLit` should be provided.37static double getValue(const IntegerLiteral *IntLit,38                       const FloatingLiteral *FloatLit) {39  if (IntLit)40    return IntLit->getValue().getLimitedValue();41 42  assert(FloatLit != nullptr && "Neither IntLit nor FloatLit set");43  return FloatLit->getValueAsApproximateDouble();44}45 46// Given the scale of a duration and a `Multiplier`, determine if `Multiplier`47// would produce a new scale.  If so, return a tuple containing the new scale48// and a suitable Multiplier for that scale, otherwise `std::nullopt`.49static std::optional<std::tuple<DurationScale, double>>50getNewScaleSingleStep(DurationScale OldScale, double Multiplier) {51  switch (OldScale) {52  case DurationScale::Hours:53    if (Multiplier <= 1.0 / 60.0)54      return std::make_tuple(DurationScale::Minutes, Multiplier * 60.0);55    break;56 57  case DurationScale::Minutes:58    if (Multiplier >= 60.0)59      return std::make_tuple(DurationScale::Hours, Multiplier / 60.0);60    if (Multiplier <= 1.0 / 60.0)61      return std::make_tuple(DurationScale::Seconds, Multiplier * 60.0);62    break;63 64  case DurationScale::Seconds:65    if (Multiplier >= 60.0)66      return std::make_tuple(DurationScale::Minutes, Multiplier / 60.0);67    if (Multiplier <= 1e-3)68      return std::make_tuple(DurationScale::Milliseconds, Multiplier * 1e3);69    break;70 71  case DurationScale::Milliseconds:72    if (Multiplier >= 1e3)73      return std::make_tuple(DurationScale::Seconds, Multiplier / 1e3);74    if (Multiplier <= 1e-3)75      return std::make_tuple(DurationScale::Microseconds, Multiplier * 1e3);76    break;77 78  case DurationScale::Microseconds:79    if (Multiplier >= 1e3)80      return std::make_tuple(DurationScale::Milliseconds, Multiplier / 1e3);81    if (Multiplier <= 1e-3)82      return std::make_tuple(DurationScale::Nanoseconds, Multiplier * 1e-3);83    break;84 85  case DurationScale::Nanoseconds:86    if (Multiplier >= 1e3)87      return std::make_tuple(DurationScale::Microseconds, Multiplier / 1e3);88    break;89  }90 91  return std::nullopt;92}93 94// Given the scale of a duration and a `Multiplier`, determine if `Multiplier`95// would produce a new scale.  If so, return it, otherwise `std::nullopt`.96static std::optional<DurationScale> getNewScale(DurationScale OldScale,97                                                double Multiplier) {98  while (Multiplier != 1.0) {99    std::optional<std::tuple<DurationScale, double>> Result =100        getNewScaleSingleStep(OldScale, Multiplier);101    if (!Result)102      break;103    if (std::get<1>(*Result) == 1.0)104      return std::get<0>(*Result);105    Multiplier = std::get<1>(*Result);106    OldScale = std::get<0>(*Result);107  }108 109  return std::nullopt;110}111 112void DurationFactoryScaleCheck::registerMatchers(MatchFinder *Finder) {113  Finder->addMatcher(114      callExpr(115          callee(functionDecl(durationFactoryFunction()).bind("call_decl")),116          hasArgument(117              0,118              ignoringImpCasts(anyOf(119                  cxxFunctionalCastExpr(120                      hasDestinationType(121                          anyOf(isInteger(), realFloatingPointType())),122                      hasSourceExpression(initListExpr())),123                  integerLiteral(equals(0)), floatLiteral(equals(0.0)),124                  binaryOperator(hasOperatorName("*"),125                                 hasEitherOperand(ignoringImpCasts(126                                     anyOf(integerLiteral(), floatLiteral()))))127                      .bind("mult_binop"),128                  binaryOperator(hasOperatorName("/"), hasRHS(floatLiteral()))129                      .bind("div_binop")))))130          .bind("call"),131      this);132}133 134void DurationFactoryScaleCheck::check(const MatchFinder::MatchResult &Result) {135  const auto *Call = Result.Nodes.getNodeAs<CallExpr>("call");136 137  // Don't try to replace things inside of macro definitions.138  if (Call->getExprLoc().isMacroID())139    return;140 141  const Expr *Arg = Call->getArg(0)->IgnoreParenImpCasts();142  // Arguments which are macros are ignored.143  if (Arg->getBeginLoc().isMacroID())144    return;145 146  // We first handle the cases of literal zero (both float and integer).147  if (isLiteralZero(Result, *Arg)) {148    diag(Call->getBeginLoc(),149         "use ZeroDuration() for zero-length time intervals")150        << FixItHint::CreateReplacement(Call->getSourceRange(),151                                        "absl::ZeroDuration()");152    return;153  }154 155  const auto *CallDecl = Result.Nodes.getNodeAs<FunctionDecl>("call_decl");156  std::optional<DurationScale> MaybeScale =157      getScaleForFactory(CallDecl->getName());158  if (!MaybeScale)159    return;160 161  const DurationScale Scale = *MaybeScale;162  const Expr *Remainder = nullptr;163  std::optional<DurationScale> NewScale;164 165  // We next handle the cases of multiplication and division.166  if (const auto *MultBinOp =167          Result.Nodes.getNodeAs<BinaryOperator>("mult_binop")) {168    // For multiplication, we need to look at both operands, and consider the169    // cases where a user is multiplying by something such as 1e-3.170 171    // First check the LHS172    const auto *IntLit = llvm::dyn_cast<IntegerLiteral>(MultBinOp->getLHS());173    const auto *FloatLit = llvm::dyn_cast<FloatingLiteral>(MultBinOp->getLHS());174    if (IntLit || FloatLit) {175      NewScale = getNewScale(Scale, getValue(IntLit, FloatLit));176      if (NewScale)177        Remainder = MultBinOp->getRHS();178    }179 180    // If we weren't able to scale based on the LHS, check the RHS181    if (!NewScale) {182      IntLit = llvm::dyn_cast<IntegerLiteral>(MultBinOp->getRHS());183      FloatLit = llvm::dyn_cast<FloatingLiteral>(MultBinOp->getRHS());184      if (IntLit || FloatLit) {185        NewScale = getNewScale(Scale, getValue(IntLit, FloatLit));186        if (NewScale)187          Remainder = MultBinOp->getLHS();188      }189    }190  } else if (const auto *DivBinOp =191                 Result.Nodes.getNodeAs<BinaryOperator>("div_binop")) {192    // We next handle division.193    // For division, we only check the RHS.194    const auto *FloatLit = llvm::cast<FloatingLiteral>(DivBinOp->getRHS());195 196    std::optional<DurationScale> NewScale =197        getNewScale(Scale, 1.0 / FloatLit->getValueAsApproximateDouble());198    if (NewScale) {199      const Expr *Remainder = DivBinOp->getLHS();200 201      // We've found an appropriate scaling factor and the new scale, so output202      // the relevant fix.203      diag(Call->getBeginLoc(), "internal duration scaling can be removed")204          << FixItHint::CreateReplacement(205                 Call->getSourceRange(),206                 (llvm::Twine(getDurationFactoryForScale(*NewScale)) + "(" +207                  tooling::fixit::getText(*Remainder, *Result.Context) + ")")208                     .str());209    }210  }211 212  if (NewScale) {213    assert(Remainder && "No remainder found");214    // We've found an appropriate scaling factor and the new scale, so output215    // the relevant fix.216    diag(Call->getBeginLoc(), "internal duration scaling can be removed")217        << FixItHint::CreateReplacement(218               Call->getSourceRange(),219               (llvm::Twine(getDurationFactoryForScale(*NewScale)) + "(" +220                tooling::fixit::getText(*Remainder, *Result.Context) + ")")221                   .str());222  }223}224 225} // namespace clang::tidy::abseil226