brintos

brintos / llvm-project-archived public Read only

0
0
Text · 4.6 KiB · 5867fb6 Raw
114 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 "DurationUnnecessaryConversionCheck.h"10#include "DurationRewriter.h"11#include "clang/AST/ASTContext.h"12#include "clang/ASTMatchers/ASTMatchFinder.h"13#include "clang/Tooling/FixIt.h"14 15using namespace clang::ast_matchers;16 17namespace clang::tidy::abseil {18 19void DurationUnnecessaryConversionCheck::registerMatchers(MatchFinder *Finder) {20  for (const auto &Scale : {"Hours", "Minutes", "Seconds", "Milliseconds",21                            "Microseconds", "Nanoseconds"}) {22    const std::string DurationFactory = (llvm::Twine("::absl::") + Scale).str();23    const std::string FloatConversion =24        (llvm::Twine("::absl::ToDouble") + Scale).str();25    const std::string IntegerConversion =26        (llvm::Twine("::absl::ToInt64") + Scale).str();27 28    // Matcher which matches the current scale's factory with a `1` argument,29    // e.g. `absl::Seconds(1)`.30    auto FactoryMatcher = ignoringElidableConstructorCall(31        callExpr(callee(functionDecl(hasName(DurationFactory))),32                 hasArgument(0, ignoringImpCasts(integerLiteral(equals(1))))));33 34    // Matcher which matches either inverse function and binds its argument,35    // e.g. `absl::ToDoubleSeconds(dur)`.36    auto InverseFunctionMatcher = callExpr(37        callee(functionDecl(hasAnyName(FloatConversion, IntegerConversion))),38        hasArgument(0, expr().bind("arg")));39 40    // Matcher which matches a duration divided by the factory_matcher above,41    // e.g. `dur / absl::Seconds(1)`.42    auto DivisionOperatorMatcher = cxxOperatorCallExpr(43        hasOverloadedOperatorName("/"), hasArgument(0, expr().bind("arg")),44        hasArgument(1, FactoryMatcher));45 46    // Matcher which matches a duration argument to `FDivDuration`,47    // e.g. `absl::FDivDuration(dur, absl::Seconds(1))`48    auto FdivMatcher = callExpr(49        callee(functionDecl(hasName("::absl::FDivDuration"))),50        hasArgument(0, expr().bind("arg")), hasArgument(1, FactoryMatcher));51 52    // Matcher which matches a duration argument being scaled,53    // e.g. `absl::ToDoubleSeconds(dur) * 2`54    auto ScalarMatcher = ignoringImpCasts(55        binaryOperator(hasOperatorName("*"),56                       hasEitherOperand(expr(ignoringParenImpCasts(57                           callExpr(callee(functionDecl(hasAnyName(58                                        FloatConversion, IntegerConversion))),59                                    hasArgument(0, expr().bind("arg")))60                               .bind("inner_call")))))61            .bind("binop"));62 63    Finder->addMatcher(64        callExpr(callee(functionDecl(hasName(DurationFactory))),65                 hasArgument(0, anyOf(InverseFunctionMatcher,66                                      DivisionOperatorMatcher, FdivMatcher,67                                      ScalarMatcher)))68            .bind("call"),69        this);70  }71}72 73void DurationUnnecessaryConversionCheck::check(74    const MatchFinder::MatchResult &Result) {75  const auto *OuterCall = Result.Nodes.getNodeAs<Expr>("call");76 77  if (isInMacro(Result, OuterCall))78    return;79 80  FixItHint Hint;81  if (const auto *Binop = Result.Nodes.getNodeAs<BinaryOperator>("binop")) {82    const auto *Arg = Result.Nodes.getNodeAs<Expr>("arg");83    const auto *InnerCall = Result.Nodes.getNodeAs<Expr>("inner_call");84    const Expr *LHS = Binop->getLHS();85    const Expr *RHS = Binop->getRHS();86 87    if (LHS->IgnoreParenImpCasts() == InnerCall) {88      Hint = FixItHint::CreateReplacement(89          OuterCall->getSourceRange(),90          (llvm::Twine(tooling::fixit::getText(*Arg, *Result.Context)) + " * " +91           tooling::fixit::getText(*RHS, *Result.Context))92              .str());93    } else {94      assert(RHS->IgnoreParenImpCasts() == InnerCall &&95             "Inner call should be find on the RHS");96 97      Hint = FixItHint::CreateReplacement(98          OuterCall->getSourceRange(),99          (llvm::Twine(tooling::fixit::getText(*LHS, *Result.Context)) + " * " +100           tooling::fixit::getText(*Arg, *Result.Context))101              .str());102    }103  } else if (const auto *Arg = Result.Nodes.getNodeAs<Expr>("arg")) {104    Hint = FixItHint::CreateReplacement(105        OuterCall->getSourceRange(),106        tooling::fixit::getText(*Arg, *Result.Context));107  }108  diag(OuterCall->getBeginLoc(),109       "remove unnecessary absl::Duration conversions")110      << Hint;111}112 113} // namespace clang::tidy::abseil114