clang-tools 6.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 bugprone {
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 bugprone
139 } // namespace tidy
140 } // namespace clang
clang::ast_matchers
Definition: ClangTidyDiagnosticConsumer.h:27
clang::tidy::bugprone::isValidBuiltinFold
static bool isValidBuiltinFold(const BuiltinType &ValueType, const BuiltinType &InitType, const ASTContext &Context)
Returns true if ValueType is allowed to fold into InitType, i.e.
Definition: FoldInitTypeCheck.cpp:80
clang
Definition: AndroidTidyModule.cpp:28
FoldInitTypeCheck.h