clang-tools  8.0.0svn
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
SourceLocation Loc
&#39;#&#39; location in the include directive
Optional< Expected< tooling::AtomicChanges > > Result
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.
===– 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...