clang-tools 22.0.0git
DurationUnnecessaryConversionCheck.cpp
Go to the documentation of this file.
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-exception
6//
7//===----------------------------------------------------------------------===//
8
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
20 for (const auto &Scale : {"Hours", "Minutes", "Seconds", "Milliseconds",
21 "Microseconds", "Nanoseconds"}) {
22 std::string DurationFactory = (llvm::Twine("::absl::") + Scale).str();
23 std::string FloatConversion =
24 (llvm::Twine("::absl::ToDouble") + Scale).str();
25 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
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::abseil
void registerMatchers(ast_matchers::MatchFinder *Finder) override
void check(const ast_matchers::MatchFinder::MatchResult &Result) override
bool isInMacro(const MatchFinder::MatchResult &Result, const Expr *E)