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