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