clang-tools  5.0.0
FoldInitTypeCheck.cpp
Go to the documentation of this file.
1 //===--- FoldInitTypeCheck.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 "FoldInitTypeCheck.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 misc {
19 
20 void FoldInitTypeCheck::registerMatchers(MatchFinder *Finder) {
21  // We match functions of interest and bind the iterator and init value types.
22  // Note: Right now we check only builtin types.
23  const auto BuiltinTypeWithId = [](const char *ID) {
24  return hasCanonicalType(builtinType().bind(ID));
25  };
26  const auto IteratorWithValueType = [&BuiltinTypeWithId](const char *ID) {
27  return anyOf(
28  // Pointer types.
29  pointsTo(BuiltinTypeWithId(ID)),
30  // Iterator types.
31  recordType(hasDeclaration(has(typedefNameDecl(
32  hasName("value_type"), hasType(BuiltinTypeWithId(ID)))))));
33  };
34 
35  const auto IteratorParam = parmVarDecl(
36  hasType(hasCanonicalType(IteratorWithValueType("IterValueType"))));
37  const auto Iterator2Param = parmVarDecl(
38  hasType(hasCanonicalType(IteratorWithValueType("Iter2ValueType"))));
39  const auto InitParam = parmVarDecl(hasType(BuiltinTypeWithId("InitType")));
40 
41  // std::accumulate, std::reduce.
42  Finder->addMatcher(
43  callExpr(callee(functionDecl(
44  hasAnyName("::std::accumulate", "::std::reduce"),
45  hasParameter(0, IteratorParam), hasParameter(2, InitParam))),
46  argumentCountIs(3))
47  .bind("Call"),
48  this);
49  // std::inner_product.
50  Finder->addMatcher(
51  callExpr(callee(functionDecl(hasName("::std::inner_product"),
52  hasParameter(0, IteratorParam),
53  hasParameter(2, Iterator2Param),
54  hasParameter(3, InitParam))),
55  argumentCountIs(4))
56  .bind("Call"),
57  this);
58  // std::reduce with a policy.
59  Finder->addMatcher(
60  callExpr(callee(functionDecl(hasName("::std::reduce"),
61  hasParameter(1, IteratorParam),
62  hasParameter(3, InitParam))),
63  argumentCountIs(4))
64  .bind("Call"),
65  this);
66  // std::inner_product with a policy.
67  Finder->addMatcher(
68  callExpr(callee(functionDecl(hasName("::std::inner_product"),
69  hasParameter(1, IteratorParam),
70  hasParameter(3, Iterator2Param),
71  hasParameter(4, InitParam))),
72  argumentCountIs(5))
73  .bind("Call"),
74  this);
75 }
76 
77 /// Returns true if ValueType is allowed to fold into InitType, i.e. if:
78 /// static_cast<InitType>(ValueType{some_value})
79 /// does not result in trucation.
80 static bool isValidBuiltinFold(const BuiltinType &ValueType,
81  const BuiltinType &InitType,
82  const ASTContext &Context) {
83  const auto ValueTypeSize = Context.getTypeSize(&ValueType);
84  const auto InitTypeSize = Context.getTypeSize(&InitType);
85  // It's OK to fold a float into a float of bigger or equal size, but not OK to
86  // fold into an int.
87  if (ValueType.isFloatingPoint())
88  return InitType.isFloatingPoint() && InitTypeSize >= ValueTypeSize;
89  // It's OK to fold an int into:
90  // - an int of the same size and signedness.
91  // - a bigger int, regardless of signedness.
92  // - FIXME: should it be a warning to fold into floating point?
93  if (ValueType.isInteger()) {
94  if (InitType.isInteger()) {
95  if (InitType.isSignedInteger() == ValueType.isSignedInteger())
96  return InitTypeSize >= ValueTypeSize;
97  return InitTypeSize > ValueTypeSize;
98  }
99  if (InitType.isFloatingPoint())
100  return InitTypeSize >= ValueTypeSize;
101  }
102  return false;
103 }
104 
105 /// Prints a diagnostic if IterValueType doe snot fold into IterValueType (see
106 // isValidBuiltinFold for details).
107 void FoldInitTypeCheck::doCheck(const BuiltinType &IterValueType,
108  const BuiltinType &InitType,
109  const ASTContext &Context,
110  const CallExpr &CallNode) {
111  if (!isValidBuiltinFold(IterValueType, InitType, Context)) {
112  diag(CallNode.getExprLoc(), "folding type %0 into type %1 might result in "
113  "loss of precision")
114  << IterValueType.desugar() << InitType.desugar();
115  }
116 }
117 
118 void FoldInitTypeCheck::check(const MatchFinder::MatchResult &Result) {
119  // Given the iterator and init value type retreived by the matchers,
120  // we check that the ::value_type of the iterator is compatible with
121  // the init value type.
122  const auto *InitType = Result.Nodes.getNodeAs<BuiltinType>("InitType");
123  const auto *IterValueType =
124  Result.Nodes.getNodeAs<BuiltinType>("IterValueType");
125  assert(InitType != nullptr);
126  assert(IterValueType != nullptr);
127 
128  const auto *CallNode = Result.Nodes.getNodeAs<CallExpr>("Call");
129  assert(CallNode != nullptr);
130 
131  doCheck(*IterValueType, *InitType, *Result.Context, *CallNode);
132 
133  if (const auto *Iter2ValueType =
134  Result.Nodes.getNodeAs<BuiltinType>("Iter2ValueType"))
135  doCheck(*Iter2ValueType, *InitType, *Result.Context, *CallNode);
136 }
137 
138 } // namespace misc
139 } // namespace tidy
140 } // namespace clang
std::unique_ptr< ast_matchers::MatchFinder > Finder
Definition: ClangTidy.cpp:275
ClangTidyContext & Context
Definition: ClangTidy.cpp:87
static bool isValidBuiltinFold(const BuiltinType &ValueType, const BuiltinType &InitType, const ASTContext &Context)
Returns true if ValueType is allowed to fold into InitType, i.e.