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