12#include "clang/AST/ASTContext.h"
13#include "clang/ASTMatchers/ASTMatchFinder.h"
14#include "clang/Tooling/FixIt.h"
21 for (
const auto &Scale : {
"Hours",
"Minutes",
"Seconds",
"Milliseconds",
22 "Microseconds",
"Nanoseconds"}) {
23 std::string DurationFactory = (llvm::Twine(
"::absl::") + Scale).str();
24 std::string FloatConversion =
25 (llvm::Twine(
"::absl::ToDouble") + Scale).str();
26 std::string IntegerConversion =
27 (llvm::Twine(
"::absl::ToInt64") + Scale).str();
31 auto FactoryMatcher = ignoringElidableConstructorCall(
32 callExpr(callee(functionDecl(hasName(DurationFactory))),
33 hasArgument(0, ignoringImpCasts(integerLiteral(equals(1))))));
37 auto InverseFunctionMatcher = callExpr(
38 callee(functionDecl(hasAnyName(FloatConversion, IntegerConversion))),
39 hasArgument(0, expr().bind(
"arg")));
43 auto DivisionOperatorMatcher = cxxOperatorCallExpr(
44 hasOverloadedOperatorName(
"/"), hasArgument(0, expr().bind(
"arg")),
45 hasArgument(1, FactoryMatcher));
49 auto FdivMatcher = callExpr(
50 callee(functionDecl(hasName(
"::absl::FDivDuration"))),
51 hasArgument(0, expr().bind(
"arg")), hasArgument(1, FactoryMatcher));
55 auto ScalarMatcher = ignoringImpCasts(
56 binaryOperator(hasOperatorName(
"*"),
57 hasEitherOperand(expr(ignoringParenImpCasts(
58 callExpr(callee(functionDecl(hasAnyName(
59 FloatConversion, IntegerConversion))),
60 hasArgument(0, expr().bind(
"arg")))
61 .bind(
"inner_call")))))
65 callExpr(callee(functionDecl(hasName(DurationFactory))),
66 hasArgument(0, anyOf(InverseFunctionMatcher,
67 DivisionOperatorMatcher, FdivMatcher,
75 const MatchFinder::MatchResult &Result) {
76 const auto *OuterCall = Result.Nodes.getNodeAs<Expr>(
"call");
82 if (
const auto *Binop = Result.Nodes.getNodeAs<BinaryOperator>(
"binop")) {
83 const auto *Arg = Result.Nodes.getNodeAs<Expr>(
"arg");
84 const auto *InnerCall = Result.Nodes.getNodeAs<Expr>(
"inner_call");
85 const Expr *LHS = Binop->getLHS();
86 const Expr *RHS = Binop->getRHS();
88 if (LHS->IgnoreParenImpCasts() == InnerCall) {
89 Hint = FixItHint::CreateReplacement(
90 OuterCall->getSourceRange(),
91 (llvm::Twine(tooling::fixit::getText(*Arg, *Result.Context)) +
" * " +
92 tooling::fixit::getText(*RHS, *Result.Context))
95 assert(RHS->IgnoreParenImpCasts() == InnerCall &&
96 "Inner call should be find on the RHS");
98 Hint = FixItHint::CreateReplacement(
99 OuterCall->getSourceRange(),
100 (llvm::Twine(tooling::fixit::getText(*LHS, *Result.Context)) +
" * " +
101 tooling::fixit::getText(*Arg, *Result.Context))
104 }
else if (
const auto *Arg = Result.Nodes.getNodeAs<Expr>(
"arg")) {
105 Hint = FixItHint::CreateReplacement(
106 OuterCall->getSourceRange(),
107 tooling::fixit::getText(*Arg, *Result.Context));
109 diag(OuterCall->getBeginLoc(),
110 "remove unnecessary absl::Duration conversions")
DiagnosticBuilder diag(SourceLocation Loc, StringRef Description, DiagnosticIDs::Level Level=DiagnosticIDs::Warning)
Add a diagnostic with the check's name.
void registerMatchers(ast_matchers::MatchFinder *Finder) override
Override this to register AST matchers with Finder.
void check(const ast_matchers::MatchFinder::MatchResult &Result) override
ClangTidyChecks that register ASTMatchers should do the actual work in here.
bool isInMacro(const MatchFinder::MatchResult &Result, const Expr *E)