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