xref: /llvm-project/clang-tools-extra/clang-tidy/abseil/DurationConversionCastCheck.cpp (revision 7d2ea6c422d3f5712b7253407005e1a465a76946)
1 //===--- DurationConversionCastCheck.cpp - clang-tidy ---------------------===//
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 
9 #include "DurationConversionCastCheck.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 
16 using namespace clang::ast_matchers;
17 
18 namespace clang::tidy::abseil {
19 
registerMatchers(MatchFinder * Finder)20 void DurationConversionCastCheck::registerMatchers(MatchFinder *Finder) {
21   auto CallMatcher = ignoringImpCasts(callExpr(
22       callee(functionDecl(DurationConversionFunction()).bind("func_decl")),
23       hasArgument(0, expr().bind("arg"))));
24 
25   Finder->addMatcher(
26       expr(anyOf(
27           cxxStaticCastExpr(hasSourceExpression(CallMatcher)).bind("cast_expr"),
28           cStyleCastExpr(hasSourceExpression(CallMatcher)).bind("cast_expr"),
29           cxxFunctionalCastExpr(hasSourceExpression(CallMatcher))
30               .bind("cast_expr"))),
31       this);
32 }
33 
check(const MatchFinder::MatchResult & Result)34 void DurationConversionCastCheck::check(
35     const MatchFinder::MatchResult &Result) {
36   const auto *MatchedCast =
37       Result.Nodes.getNodeAs<ExplicitCastExpr>("cast_expr");
38 
39   if (isInMacro(Result, MatchedCast))
40     return;
41 
42   const auto *FuncDecl = Result.Nodes.getNodeAs<FunctionDecl>("func_decl");
43   const auto *Arg = Result.Nodes.getNodeAs<Expr>("arg");
44   StringRef ConversionFuncName = FuncDecl->getName();
45 
46   std::optional<DurationScale> Scale =
47       getScaleForDurationInverse(ConversionFuncName);
48   if (!Scale)
49     return;
50 
51   // Casting a double to an integer.
52   if (MatchedCast->getTypeAsWritten()->isIntegerType() &&
53       ConversionFuncName.contains("Double")) {
54     llvm::StringRef NewFuncName = getDurationInverseForScale(*Scale).second;
55 
56     diag(MatchedCast->getBeginLoc(),
57          "duration should be converted directly to an integer rather than "
58          "through a type cast")
59         << FixItHint::CreateReplacement(
60                MatchedCast->getSourceRange(),
61                (llvm::Twine(NewFuncName.substr(2)) + "(" +
62                 tooling::fixit::getText(*Arg, *Result.Context) + ")")
63                    .str());
64   }
65 
66   // Casting an integer to a double.
67   if (MatchedCast->getTypeAsWritten()->isRealFloatingType() &&
68       ConversionFuncName.contains("Int64")) {
69     llvm::StringRef NewFuncName = getDurationInverseForScale(*Scale).first;
70 
71     diag(MatchedCast->getBeginLoc(), "duration should be converted directly to "
72                                      "a floating-point number rather than "
73                                      "through a type cast")
74         << FixItHint::CreateReplacement(
75                MatchedCast->getSourceRange(),
76                (llvm::Twine(NewFuncName.substr(2)) + "(" +
77                 tooling::fixit::getText(*Arg, *Result.Context) + ")")
78                    .str());
79   }
80 }
81 
82 } // namespace clang::tidy::abseil
83