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