clang-tools 6.0.0

TypeMismatchCheck.cpp
Go to the documentation of this file.
1 //===--- TypeMismatchCheck.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 "TypeMismatchCheck.h"
11 #include "clang/Lex/Lexer.h"
12 #include "clang/StaticAnalyzer/Checkers/MPIFunctionClassifier.h"
13 #include "clang/Tooling/FixIt.h"
14 #include <map>
15 #include <unordered_set>
16
17 using namespace clang::ast_matchers;
18
19 namespace clang {
20 namespace tidy {
21 namespace mpi {
22
23 /// Check if a BuiltinType::Kind matches the MPI datatype.
24 ///
25 /// \param MultiMap datatype group
26 /// \param Kind buffer type kind
27 /// \param MPIDatatype name of the MPI datatype
28 ///
29 /// \returns true if the pair matches
30 static bool
31 isMPITypeMatching(const std::multimap<BuiltinType::Kind, std::string> &MultiMap,
32 const BuiltinType::Kind Kind,
33 const std::string &MPIDatatype) {
34 auto ItPair = MultiMap.equal_range(Kind);
35 while (ItPair.first != ItPair.second) {
36 if (ItPair.first->second == MPIDatatype)
37 return true;
38 ++ItPair.first;
39 }
40 return false;
41 }
42
43 /// Check if the MPI datatype is a standard type.
44 ///
45 /// \param MPIDatatype name of the MPI datatype
46 ///
47 /// \returns true if the type is a standard type
48 static bool isStandardMPIDatatype(const std::string &MPIDatatype) {
49 static std::unordered_set<std::string> AllTypes = {
50 "MPI_C_BOOL",
51 "MPI_CHAR",
52 "MPI_SIGNED_CHAR",
53 "MPI_UNSIGNED_CHAR",
54 "MPI_WCHAR",
55 "MPI_INT",
56 "MPI_LONG",
57 "MPI_SHORT",
58 "MPI_LONG_LONG",
59 "MPI_LONG_LONG_INT",
60 "MPI_UNSIGNED",
61 "MPI_UNSIGNED_SHORT",
62 "MPI_UNSIGNED_LONG",
63 "MPI_UNSIGNED_LONG_LONG",
64 "MPI_FLOAT",
65 "MPI_DOUBLE",
66 "MPI_LONG_DOUBLE",
67 "MPI_C_COMPLEX",
68 "MPI_C_FLOAT_COMPLEX",
69 "MPI_C_DOUBLE_COMPLEX",
70 "MPI_C_LONG_DOUBLE_COMPLEX",
71 "MPI_INT8_T",
72 "MPI_INT16_T",
73 "MPI_INT32_T",
74 "MPI_INT64_T",
75 "MPI_UINT8_T",
76 "MPI_UINT16_T",
77 "MPI_UINT32_T",
78 "MPI_UINT64_T",
79 "MPI_CXX_BOOL",
80 "MPI_CXX_FLOAT_COMPLEX",
81 "MPI_CXX_DOUBLE_COMPLEX",
82 "MPI_CXX_LONG_DOUBLE_COMPLEX"};
83
84 return AllTypes.find(MPIDatatype) != AllTypes.end();
85 }
86
87 /// Check if a BuiltinType matches the MPI datatype.
88 ///
89 /// \param Builtin the builtin type
90 /// \param BufferTypeName buffer type name, gets assigned
91 /// \param MPIDatatype name of the MPI datatype
92 /// \param LO language options
93 ///
94 /// \returns true if the type matches
95 static bool isBuiltinTypeMatching(const BuiltinType *Builtin,
96 std::string &BufferTypeName,
97 const std::string &MPIDatatype,
98 const LangOptions &LO) {
99 static std::multimap<BuiltinType::Kind, std::string> BuiltinMatches = {
100 // On some systems like PPC or ARM, 'char' is unsigned by default which is
101 // why distinct signedness for the buffer and MPI type is tolerated.
102 {BuiltinType::SChar, "MPI_CHAR"},
103 {BuiltinType::SChar, "MPI_SIGNED_CHAR"},
104 {BuiltinType::SChar, "MPI_UNSIGNED_CHAR"},
105 {BuiltinType::Char_S, "MPI_CHAR"},
106 {BuiltinType::Char_S, "MPI_SIGNED_CHAR"},
107 {BuiltinType::Char_S, "MPI_UNSIGNED_CHAR"},
108 {BuiltinType::UChar, "MPI_CHAR"},
109 {BuiltinType::UChar, "MPI_SIGNED_CHAR"},
110 {BuiltinType::UChar, "MPI_UNSIGNED_CHAR"},
111 {BuiltinType::Char_U, "MPI_CHAR"},
112 {BuiltinType::Char_U, "MPI_SIGNED_CHAR"},
113 {BuiltinType::Char_U, "MPI_UNSIGNED_CHAR"},
114 {BuiltinType::WChar_S, "MPI_WCHAR"},
115 {BuiltinType::WChar_U, "MPI_WCHAR"},
116 {BuiltinType::Bool, "MPI_C_BOOL"},
117 {BuiltinType::Bool, "MPI_CXX_BOOL"},
118 {BuiltinType::Short, "MPI_SHORT"},
119 {BuiltinType::Int, "MPI_INT"},
120 {BuiltinType::Long, "MPI_LONG"},
121 {BuiltinType::LongLong, "MPI_LONG_LONG"},
122 {BuiltinType::LongLong, "MPI_LONG_LONG_INT"},
123 {BuiltinType::UShort, "MPI_UNSIGNED_SHORT"},
124 {BuiltinType::UInt, "MPI_UNSIGNED"},
125 {BuiltinType::ULong, "MPI_UNSIGNED_LONG"},
126 {BuiltinType::ULongLong, "MPI_UNSIGNED_LONG_LONG"},
127 {BuiltinType::Float, "MPI_FLOAT"},
128 {BuiltinType::Double, "MPI_DOUBLE"},
129 {BuiltinType::LongDouble, "MPI_LONG_DOUBLE"}};
130
131 if (!isMPITypeMatching(BuiltinMatches, Builtin->getKind(), MPIDatatype)) {
132 BufferTypeName = Builtin->getName(LO);
133 return false;
134 }
135
136 return true;
137 }
138
139 /// Check if a complex float/double/long double buffer type matches
140 /// the MPI datatype.
141 ///
142 /// \param Complex buffer type
143 /// \param BufferTypeName buffer type name, gets assigned
144 /// \param MPIDatatype name of the MPI datatype
145 /// \param LO language options
146 ///
147 /// \returns true if the type matches or the buffer type is unknown
148 static bool isCComplexTypeMatching(const ComplexType *const Complex,
149 std::string &BufferTypeName,
150 const std::string &MPIDatatype,
151 const LangOptions &LO) {
152 static std::multimap<BuiltinType::Kind, std::string> ComplexCMatches = {
153 {BuiltinType::Float, "MPI_C_COMPLEX"},
154 {BuiltinType::Float, "MPI_C_FLOAT_COMPLEX"},
155 {BuiltinType::Double, "MPI_C_DOUBLE_COMPLEX"},
156 {BuiltinType::LongDouble, "MPI_C_LONG_DOUBLE_COMPLEX"}};
157
158 const auto *Builtin =
159 Complex->getElementType().getTypePtr()->getAs<BuiltinType>();
160
161 if (Builtin &&
162 !isMPITypeMatching(ComplexCMatches, Builtin->getKind(), MPIDatatype)) {
163 BufferTypeName = (llvm::Twine(Builtin->getName(LO)) + " _Complex").str();
164 return false;
165 }
166 return true;
167 }
168
169 /// Check if a complex<float/double/long double> templated buffer type matches
170 /// the MPI datatype.
171 ///
172 /// \param Template buffer type
173 /// \param BufferTypeName buffer type name, gets assigned
174 /// \param MPIDatatype name of the MPI datatype
175 /// \param LO language options
176 ///
177 /// \returns true if the type matches or the buffer type is unknown
178 static bool
179 isCXXComplexTypeMatching(const TemplateSpecializationType *const Template,
180 std::string &BufferTypeName,
181 const std::string &MPIDatatype,
182 const LangOptions &LO) {
183 static std::multimap<BuiltinType::Kind, std::string> ComplexCXXMatches = {
184 {BuiltinType::Float, "MPI_CXX_FLOAT_COMPLEX"},
185 {BuiltinType::Double, "MPI_CXX_DOUBLE_COMPLEX"},
186 {BuiltinType::LongDouble, "MPI_CXX_LONG_DOUBLE_COMPLEX"}};
187
188 if (Template->getAsCXXRecordDecl()->getName() != "complex")
189 return true;
190
191 const auto *Builtin =
192 Template->getArg(0).getAsType().getTypePtr()->getAs<BuiltinType>();
193
194 if (Builtin &&
195 !isMPITypeMatching(ComplexCXXMatches, Builtin->getKind(), MPIDatatype)) {
196 BufferTypeName =
197 (llvm::Twine("complex<") + Builtin->getName(LO) + ">").str();
198 return false;
199 }
200
201 return true;
202 }
203
204 /// Check if a fixed size width buffer type matches the MPI datatype.
205 ///
206 /// \param Typedef buffer type
207 /// \param BufferTypeName buffer type name, gets assigned
208 /// \param MPIDatatype name of the MPI datatype
209 ///
210 /// \returns true if the type matches or the buffer type is unknown
211 static bool isTypedefTypeMatching(const TypedefType *const Typedef,
212 std::string &BufferTypeName,
213 const std::string &MPIDatatype) {
214 static llvm::StringMap<std::string> FixedWidthMatches = {
215 {"int8_t", "MPI_INT8_T"}, {"int16_t", "MPI_INT16_T"},
216 {"int32_t", "MPI_INT32_T"}, {"int64_t", "MPI_INT64_T"},
217 {"uint8_t", "MPI_UINT8_T"}, {"uint16_t", "MPI_UINT16_T"},
218 {"uint32_t", "MPI_UINT32_T"}, {"uint64_t", "MPI_UINT64_T"}};
219
220 const auto it = FixedWidthMatches.find(Typedef->getDecl()->getName());
221 // Check if the typedef is known and not matching the MPI datatype.
222 if (it != FixedWidthMatches.end() && it->getValue() != MPIDatatype) {
223 BufferTypeName = Typedef->getDecl()->getName();
224 return false;
225 }
226 return true;
227 }
228
229 /// Get the unqualified, dereferenced type of an argument.
230 ///
231 /// \param CE call expression
232 /// \param idx argument index
233 ///
234 /// \returns type of the argument
235 static const Type *argumentType(const CallExpr *const CE, const size_t idx) {
236 const QualType QT = CE->getArg(idx)->IgnoreImpCasts()->getType();
237 return QT.getTypePtr()->getPointeeOrArrayElementType();
238 }
239
240 void TypeMismatchCheck::registerMatchers(MatchFinder *Finder) {
241 Finder->addMatcher(callExpr().bind("CE"), this);
242 }
243
244 void TypeMismatchCheck::check(const MatchFinder::MatchResult &Result) {
245 static ento::mpi::MPIFunctionClassifier FuncClassifier(*Result.Context);
246 const auto *const CE = Result.Nodes.getNodeAs<CallExpr>("CE");
247 if (!CE->getDirectCallee())
248 return;
249
250 const IdentifierInfo *Identifier = CE->getDirectCallee()->getIdentifier();
251 if (!Identifier || !FuncClassifier.isMPIType(Identifier))
252 return;
253
254 // These containers are used, to capture buffer, MPI datatype pairs.
255 SmallVector<const Type *, 1> BufferTypes;
256 SmallVector<const Expr *, 1> BufferExprs;
257 SmallVector<StringRef, 1> MPIDatatypes;
258
259 // Adds a buffer, MPI datatype pair of an MPI call expression to the
260 // containers. For buffers, the type and expression is captured.
261 auto addPair = [&CE, &Result, &BufferTypes, &BufferExprs, &MPIDatatypes](
262 const size_t BufferIdx, const size_t DatatypeIdx) {
263 // Skip null pointer constants and in place 'operators'.
264 if (CE->getArg(BufferIdx)->isNullPointerConstant(
265 *Result.Context, Expr::NPC_ValueDependentIsNull) ||
266 tooling::fixit::getText(*CE->getArg(BufferIdx), *Result.Context) ==
267 "MPI_IN_PLACE")
268 return;
269
270 StringRef MPIDatatype =
271 tooling::fixit::getText(*CE->getArg(DatatypeIdx), *Result.Context);
272
273 const Type *ArgType = argumentType(CE, BufferIdx);
274 // Skip unknown MPI datatypes and void pointers.
275 if (!isStandardMPIDatatype(MPIDatatype) || ArgType->isVoidType())
276 return;
277
278 BufferTypes.push_back(ArgType);
279 BufferExprs.push_back(CE->getArg(BufferIdx));
280 MPIDatatypes.push_back(MPIDatatype);
281 };
282
283 // Collect all buffer, MPI datatype pairs for the inspected call expression.
284 if (FuncClassifier.isPointToPointType(Identifier)) {
285 addPair(0, 2);
286 } else if (FuncClassifier.isCollectiveType(Identifier)) {
287 if (FuncClassifier.isReduceType(Identifier)) {
288 addPair(0, 3);
289 addPair(1, 3);
290 } else if (FuncClassifier.isScatterType(Identifier) ||
291 FuncClassifier.isGatherType(Identifier) ||
292 FuncClassifier.isAlltoallType(Identifier)) {
293 addPair(0, 2);
294 addPair(3, 5);
295 } else if (FuncClassifier.isBcastType(Identifier)) {
296 addPair(0, 2);
297 }
298 }
299 checkArguments(BufferTypes, BufferExprs, MPIDatatypes, getLangOpts());
300 }
301
302 void TypeMismatchCheck::checkArguments(ArrayRef<const Type *> BufferTypes,
303 ArrayRef<const Expr *> BufferExprs,
304 ArrayRef<StringRef> MPIDatatypes,
305 const LangOptions &LO) {
306 std::string BufferTypeName;
307
308 for (size_t i = 0; i < MPIDatatypes.size(); ++i) {
309 const Type *const BT = BufferTypes[i];
310 bool Error = false;
311
312 if (const auto *Typedef = BT->getAs<TypedefType>()) {
313 Error = !isTypedefTypeMatching(Typedef, BufferTypeName, MPIDatatypes[i]);
314 } else if (const auto *Complex = BT->getAs<ComplexType>()) {
315 Error =
316 !isCComplexTypeMatching(Complex, BufferTypeName, MPIDatatypes[i], LO);
317 } else if (const auto *Template = BT->getAs<TemplateSpecializationType>()) {
318 Error = !isCXXComplexTypeMatching(Template, BufferTypeName,
319 MPIDatatypes[i], LO);
320 } else if (const auto *Builtin = BT->getAs<BuiltinType>()) {
321 Error =
322 !isBuiltinTypeMatching(Builtin, BufferTypeName, MPIDatatypes[i], LO);
323 }
324
325 if (Error) {
326 const auto Loc = BufferExprs[i]->getSourceRange().getBegin();
327 diag(Loc, "buffer type '%0' does not match the MPI datatype '%1'")
328 << BufferTypeName << MPIDatatypes[i];
329 }
330 }
331 }
332
333 } // namespace mpi
334 } // namespace tidy
335 } // namespace clang
Loc
SourceLocation Loc
&#39;#&#39; location in the include directive
Definition: IncludeOrderCheck.cpp:36
clang::ast_matchers
Definition: ClangTidyDiagnosticConsumer.h:27
clang::tidy::mpi::isTypedefTypeMatching
static bool isTypedefTypeMatching(const TypedefType *const Typedef, std::string &BufferTypeName, const std::string &MPIDatatype)
Check if a fixed size width buffer type matches the MPI datatype.
Definition: TypeMismatchCheck.cpp:211
clang::tidy::mpi::isMPITypeMatching
static bool isMPITypeMatching(const std::multimap< BuiltinType::Kind, std::string > &MultiMap, const BuiltinType::Kind Kind, const std::string &MPIDatatype)
Check if a BuiltinType::Kind matches the MPI datatype.
Definition: TypeMismatchCheck.cpp:31
clang::tidy::mpi::isStandardMPIDatatype
static bool isStandardMPIDatatype(const std::string &MPIDatatype)
Check if the MPI datatype is a standard type.
Definition: TypeMismatchCheck.cpp:48
Kind
BindArgumentKind Kind
Definition: AvoidBindCheck.cpp:41
clang::tidy::mpi::isCComplexTypeMatching
static bool isCComplexTypeMatching(const ComplexType *const Complex, std::string &BufferTypeName, const std::string &MPIDatatype, const LangOptions &LO)
Check if a complex float/double/long double buffer type matches the MPI datatype. ...
Definition: TypeMismatchCheck.cpp:148
clang::tidy::mpi::argumentType
static const Type * argumentType(const CallExpr *const CE, const size_t idx)
Get the unqualified, dereferenced type of an argument.
Definition: TypeMismatchCheck.cpp:235
clang::tidy::mpi::isBuiltinTypeMatching
static bool isBuiltinTypeMatching(const BuiltinType *Builtin, std::string &BufferTypeName, const std::string &MPIDatatype, const LangOptions &LO)
Check if a BuiltinType matches the MPI datatype.
Definition: TypeMismatchCheck.cpp:95
TypeMismatchCheck.h
clang
Definition: AndroidTidyModule.cpp:28
clang::tidy::mpi::isCXXComplexTypeMatching
static bool isCXXComplexTypeMatching(const TemplateSpecializationType *const Template, std::string &BufferTypeName, const std::string &MPIDatatype, const LangOptions &LO)
Check if a complex<float/double/long double> templated buffer type matches the MPI datatype...
Definition: TypeMismatchCheck.cpp:179