1 //===--- NarrowingConversionsCheck.cpp - clang-tidy------------------------===//
2 //
3 //                     The LLVM Compiler Infrastructure
4 //
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
7 //
8 //===----------------------------------------------------------------------===//
9 
10 #include "NarrowingConversionsCheck.h"
11 #include "clang/AST/ASTContext.h"
12 #include "clang/ASTMatchers/ASTMatchFinder.h"
13 
14 using namespace clang::ast_matchers;
15 
16 namespace clang {
17 namespace tidy {
18 namespace cppcoreguidelines {
19 
20 // FIXME: Check double -> float truncation. Pay attention to casts:
21 void NarrowingConversionsCheck::registerMatchers(MatchFinder *Finder) {
22   // ceil() and floor() are guaranteed to return integers, even though the type
23   // is not integral.
24   const auto IsCeilFloorCall = callExpr(callee(functionDecl(
25       hasAnyName("::ceil", "::std::ceil", "::floor", "::std::floor"))));
26 
27   const auto IsFloatExpr =
28       expr(hasType(realFloatingPointType()), unless(IsCeilFloorCall));
29 
30   // casts:
31   //   i = 0.5;
32   //   void f(int); f(0.5);
33   Finder->addMatcher(implicitCastExpr(hasImplicitDestinationType(isInteger()),
34                                       hasSourceExpression(IsFloatExpr),
35                                       unless(hasParent(castExpr())),
36                                       unless(isInTemplateInstantiation()))
37                          .bind("cast"),
38                      this);
39 
40   // Binary operators:
41   //   i += 0.5;
42   Finder->addMatcher(
43       binaryOperator(isAssignmentOperator(),
44                      // The `=` case generates an implicit cast which is covered
45                      // by the previous matcher.
46                      unless(hasOperatorName("=")),
47                      hasLHS(hasType(isInteger())), hasRHS(IsFloatExpr),
48                      unless(isInTemplateInstantiation()))
49           .bind("op"),
50       this);
51 }
52 
53 void NarrowingConversionsCheck::check(const MatchFinder::MatchResult &Result) {
54   if (const auto *Op = Result.Nodes.getNodeAs<BinaryOperator>("op")) {
55     if (Op->getLocStart().isMacroID())
56       return;
57     diag(Op->getOperatorLoc(), "narrowing conversion from %0 to %1")
58         << Op->getRHS()->getType() << Op->getLHS()->getType();
59     return;
60   }
61   const auto *Cast = Result.Nodes.getNodeAs<ImplicitCastExpr>("cast");
62   if (Cast->getLocStart().isMacroID())
63     return;
64   diag(Cast->getExprLoc(), "narrowing conversion from %0 to %1")
65       << Cast->getSubExpr()->getType() << Cast->getType();
66 }
67 
68 } // namespace cppcoreguidelines
69 } // namespace tidy
70 } // namespace clang
71