This revision was automatically updated to reflect the committed changes. Closed by commit rL277516: [clang-tidy] MPITypeMismatchCheck (authored by alexfh).
Changed prior to commit: https://reviews.llvm.org/D21962?vs=65404&id=66544#toc Repository: rL LLVM https://reviews.llvm.org/D21962 Files: clang-tools-extra/trunk/clang-tidy/CMakeLists.txt clang-tools-extra/trunk/clang-tidy/mpi/CMakeLists.txt clang-tools-extra/trunk/clang-tidy/mpi/MPITidyModule.cpp clang-tools-extra/trunk/clang-tidy/mpi/TypeMismatchCheck.cpp clang-tools-extra/trunk/clang-tidy/mpi/TypeMismatchCheck.h clang-tools-extra/trunk/clang-tidy/plugin/CMakeLists.txt clang-tools-extra/trunk/clang-tidy/tool/CMakeLists.txt clang-tools-extra/trunk/clang-tidy/tool/ClangTidyMain.cpp clang-tools-extra/trunk/docs/clang-tidy/checks/list.rst clang-tools-extra/trunk/docs/clang-tidy/checks/mpi-type-mismatch.rst clang-tools-extra/trunk/test/clang-tidy/Inputs/mpi-type-mismatch/mpimock.h clang-tools-extra/trunk/test/clang-tidy/mpi-type-mismatch.cpp
Index: clang-tools-extra/trunk/docs/clang-tidy/checks/mpi-type-mismatch.rst =================================================================== --- clang-tools-extra/trunk/docs/clang-tidy/checks/mpi-type-mismatch.rst +++ clang-tools-extra/trunk/docs/clang-tidy/checks/mpi-type-mismatch.rst @@ -0,0 +1,20 @@ +.. title:: clang-tidy - mpi-type-mismatch + +mpi-type-mismatch +================= + +This check verifies if buffer type and MPI (Message Passing Interface) datatype +pairs match for used MPI functions. All MPI datatypes defined by the MPI +standard (3.1) are verified by this check. User defined typedefs, custom MPI +datatypes and null pointer constants are skipped, in the course of verification. + +Example: +.. code:: c++ + + // In this case, the buffer type matches MPI datatype. + char buf; + MPI_Send(&buf, 1, MPI_CHAR, 0, 0, MPI_COMM_WORLD); + + // In the following case, the buffer type does not match MPI datatype. + int buf; + MPI_Send(&buf, 1, MPI_CHAR, 0, 0, MPI_COMM_WORLD); Index: clang-tools-extra/trunk/docs/clang-tidy/checks/list.rst =================================================================== --- clang-tools-extra/trunk/docs/clang-tidy/checks/list.rst +++ clang-tools-extra/trunk/docs/clang-tidy/checks/list.rst @@ -110,6 +110,7 @@ modernize-use-nullptr modernize-use-override modernize-use-using + mpi-type-mismatch performance-faster-string-find performance-for-range-copy performance-implicit-cast-in-loop Index: clang-tools-extra/trunk/test/clang-tidy/mpi-type-mismatch.cpp =================================================================== --- clang-tools-extra/trunk/test/clang-tidy/mpi-type-mismatch.cpp +++ clang-tools-extra/trunk/test/clang-tidy/mpi-type-mismatch.cpp @@ -0,0 +1,255 @@ +// RUN: %check_clang_tidy %s mpi-type-mismatch %t -- -- -I %S/Inputs/mpi-type-mismatch + +#include "mpimock.h" + +void charNegativeTest() { + int buf; + MPI_Send(&buf, 1, MPI_CHAR, 0, 0, MPI_COMM_WORLD); + // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: buffer type 'int' does not match the MPI datatype 'MPI_CHAR' + + short buf2; + MPI_Send(&buf2, 1, MPI_CHAR, 0, 0, MPI_COMM_WORLD); + // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: buffer type 'short' does not match the MPI datatype 'MPI_CHAR' + + long buf3; + MPI_Send(&buf3, 1, MPI_CHAR, 0, 0, MPI_COMM_WORLD); + // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: buffer type 'long' does not match the MPI datatype 'MPI_CHAR' + + int8_t buf4; + MPI_Send(&buf4, 1, MPI_CHAR, 0, 0, MPI_COMM_WORLD); + // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: buffer type 'int8_t' does not match the MPI datatype 'MPI_CHAR' + + uint16_t buf5; + MPI_Send(&buf5, 1, MPI_CHAR, 0, 0, MPI_COMM_WORLD); + // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: buffer type 'uint16_t' does not match the MPI datatype 'MPI_CHAR' + + long double _Complex buf6; + MPI_Send(&buf6, 1, MPI_CHAR, 0, 0, MPI_COMM_WORLD); + // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: buffer type 'long double _Complex' does not match the MPI datatype 'MPI_CHAR' + + std::complex<float> buf7; + MPI_Send(&buf7, 1, MPI_CHAR, 0, 0, MPI_COMM_WORLD); + // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: buffer type 'complex<float>' does not match the MPI datatype 'MPI_CHAR' +} + +void intNegativeTest() { + unsigned char buf; + MPI_Send(&buf, 1, MPI_INT, 0, 0, MPI_COMM_WORLD); + // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: buffer type 'unsigned char' does not match the MPI datatype 'MPI_INT' + + unsigned buf2; + MPI_Send(&buf2, 1, MPI_INT, 0, 0, MPI_COMM_WORLD); + // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: buffer type 'unsigned int' does not match the MPI datatype 'MPI_INT' + + short buf3; + MPI_Send(&buf3, 1, MPI_INT, 0, 0, MPI_COMM_WORLD); + // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: buffer type 'short' does not match the MPI datatype 'MPI_INT' + + long buf4; + MPI_Send(&buf4, 1, MPI_INT, 0, 0, MPI_COMM_WORLD); + // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: buffer type 'long' does not match the MPI datatype 'MPI_INT' + + int8_t buf5; + MPI_Send(&buf5, 1, MPI_INT, 0, 0, MPI_COMM_WORLD); + // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: buffer type 'int8_t' does not match the MPI datatype 'MPI_INT' + + uint16_t buf6; + MPI_Send(&buf6, 1, MPI_INT, 0, 0, MPI_COMM_WORLD); + // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: buffer type 'uint16_t' does not match the MPI datatype 'MPI_INT' + + long double _Complex buf7; + MPI_Send(&buf7, 1, MPI_INT, 0, 0, MPI_COMM_WORLD); + // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: buffer type 'long double _Complex' does not match the MPI datatype 'MPI_INT' + + std::complex<float> buf8; + MPI_Send(&buf8, 1, MPI_INT, 0, 0, MPI_COMM_WORLD); + // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: buffer type 'complex<float>' does not match the MPI datatype 'MPI_INT' +} + +void longNegativeTest() { + char buf; + MPI_Send(&buf, 1, MPI_LONG, 0, 0, MPI_COMM_WORLD); + // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: buffer type 'char' does not match the MPI datatype 'MPI_LONG' + + unsigned buf2; + MPI_Send(&buf2, 1, MPI_LONG, 0, 0, MPI_COMM_WORLD); + // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: buffer type 'unsigned int' does not match the MPI datatype 'MPI_LONG' + + unsigned short buf3; + MPI_Send(&buf3, 1, MPI_LONG, 0, 0, MPI_COMM_WORLD); + // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: buffer type 'unsigned short' does not match the MPI datatype 'MPI_LONG' + + unsigned long buf4; + MPI_Send(&buf4, 1, MPI_LONG, 0, 0, MPI_COMM_WORLD); + // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: buffer type 'unsigned long' does not match the MPI datatype 'MPI_LONG' + + int8_t buf5; + MPI_Send(&buf5, 1, MPI_LONG, 0, 0, MPI_COMM_WORLD); + // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: buffer type 'int8_t' does not match the MPI datatype 'MPI_LONG' + + uint16_t buf6; + MPI_Send(&buf6, 1, MPI_LONG, 0, 0, MPI_COMM_WORLD); + // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: buffer type 'uint16_t' does not match the MPI datatype 'MPI_LONG' + + long double _Complex buf7; + MPI_Send(&buf7, 1, MPI_LONG, 0, 0, MPI_COMM_WORLD); + // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: buffer type 'long double _Complex' does not match the MPI datatype 'MPI_LONG' + + std::complex<float> buf8; + MPI_Send(&buf8, 1, MPI_LONG, 0, 0, MPI_COMM_WORLD); + // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: buffer type 'complex<float>' does not match the MPI datatype 'MPI_LONG' +} + +void int8_tNegativeTest() { + char buf; + MPI_Send(&buf, 1, MPI_INT8_T, 0, 0, MPI_COMM_WORLD); + // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: buffer type 'char' does not match the MPI datatype 'MPI_INT8_T' + + unsigned buf2; + MPI_Send(&buf2, 1, MPI_INT8_T, 0, 0, MPI_COMM_WORLD); + // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: buffer type 'unsigned int' does not match the MPI datatype 'MPI_INT8_T' + + short buf3; + MPI_Send(&buf3, 1, MPI_INT8_T, 0, 0, MPI_COMM_WORLD); + // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: buffer type 'short' does not match the MPI datatype 'MPI_INT8_T' + + unsigned long buf4; + MPI_Send(&buf4, 1, MPI_INT8_T, 0, 0, MPI_COMM_WORLD); + // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: buffer type 'unsigned long' does not match the MPI datatype 'MPI_INT8_T' + + uint8_t buf5; + MPI_Send(&buf5, 1, MPI_INT8_T, 0, 0, MPI_COMM_WORLD); + // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: buffer type 'uint8_t' does not match the MPI datatype 'MPI_INT8_T' + + uint16_t buf6; + MPI_Send(&buf6, 1, MPI_INT8_T, 0, 0, MPI_COMM_WORLD); + // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: buffer type 'uint16_t' does not match the MPI datatype 'MPI_INT8_T' + + long double _Complex buf7; + MPI_Send(&buf7, 1, MPI_INT8_T, 0, 0, MPI_COMM_WORLD); + // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: buffer type 'long double _Complex' does not match the MPI datatype 'MPI_INT8_T' + + std::complex<float> buf8; + MPI_Send(&buf8, 1, MPI_INT8_T, 0, 0, MPI_COMM_WORLD); + // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: buffer type 'complex<float>' does not match the MPI datatype 'MPI_INT8_T' +} + +void complex_c_long_double_complexNegativeTest() { + char buf; + MPI_Send(&buf, 1, MPI_C_LONG_DOUBLE_COMPLEX, 0, 0, MPI_COMM_WORLD); + // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: buffer type 'char' does not match the MPI datatype 'MPI_C_LONG_DOUBLE_COMPLEX' + + unsigned buf2; + MPI_Send(&buf2, 1, MPI_C_LONG_DOUBLE_COMPLEX, 0, 0, MPI_COMM_WORLD); + // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: buffer type 'unsigned int' does not match the MPI datatype 'MPI_C_LONG_DOUBLE_COMPLEX' + + short buf3; + MPI_Send(&buf3, 1, MPI_C_LONG_DOUBLE_COMPLEX, 0, 0, MPI_COMM_WORLD); + // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: buffer type 'short' does not match the MPI datatype 'MPI_C_LONG_DOUBLE_COMPLEX' + + unsigned long buf4; + MPI_Send(&buf4, 1, MPI_C_LONG_DOUBLE_COMPLEX, 0, 0, MPI_COMM_WORLD); + // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: buffer type 'unsigned long' does not match the MPI datatype 'MPI_C_LONG_DOUBLE_COMPLEX' + + uint8_t buf5; + MPI_Send(&buf5, 1, MPI_C_LONG_DOUBLE_COMPLEX, 0, 0, MPI_COMM_WORLD); + // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: buffer type 'uint8_t' does not match the MPI datatype 'MPI_C_LONG_DOUBLE_COMPLEX' + + uint16_t buf6; + MPI_Send(&buf6, 1, MPI_C_LONG_DOUBLE_COMPLEX, 0, 0, MPI_COMM_WORLD); + // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: buffer type 'uint16_t' does not match the MPI datatype 'MPI_C_LONG_DOUBLE_COMPLEX' + + double _Complex buf7; + MPI_Send(&buf7, 1, MPI_C_LONG_DOUBLE_COMPLEX, 0, 0, MPI_COMM_WORLD); + // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: buffer type 'double _Complex' does not match the MPI datatype 'MPI_C_LONG_DOUBLE_COMPLEX' + + std::complex<float> buf8; + MPI_Send(&buf8, 1, MPI_C_LONG_DOUBLE_COMPLEX, 0, 0, MPI_COMM_WORLD); + // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: buffer type 'complex<float>' does not match the MPI datatype 'MPI_C_LONG_DOUBLE_COMPLEX' +} + +void complex_cxx_float_complexNegativeTest() { + char buf; + MPI_Send(&buf, 1, MPI_CXX_FLOAT_COMPLEX, 0, 0, MPI_COMM_WORLD); + // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: buffer type 'char' does not match the MPI datatype 'MPI_CXX_FLOAT_COMPLEX' + + unsigned buf2; + MPI_Send(&buf2, 1, MPI_CXX_FLOAT_COMPLEX, 0, 0, MPI_COMM_WORLD); + // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: buffer type 'unsigned int' does not match the MPI datatype 'MPI_CXX_FLOAT_COMPLEX' + + short buf3; + MPI_Send(&buf3, 1, MPI_CXX_FLOAT_COMPLEX, 0, 0, MPI_COMM_WORLD); + // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: buffer type 'short' does not match the MPI datatype 'MPI_CXX_FLOAT_COMPLEX' + + unsigned long buf4; + MPI_Send(&buf4, 1, MPI_CXX_FLOAT_COMPLEX, 0, 0, MPI_COMM_WORLD); + // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: buffer type 'unsigned long' does not match the MPI datatype 'MPI_CXX_FLOAT_COMPLEX' + + uint8_t buf5; + MPI_Send(&buf5, 1, MPI_CXX_FLOAT_COMPLEX, 0, 0, MPI_COMM_WORLD); + // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: buffer type 'uint8_t' does not match the MPI datatype 'MPI_CXX_FLOAT_COMPLEX' + + uint16_t buf6; + MPI_Send(&buf6, 1, MPI_CXX_FLOAT_COMPLEX, 0, 0, MPI_COMM_WORLD); + // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: buffer type 'uint16_t' does not match the MPI datatype 'MPI_CXX_FLOAT_COMPLEX' + + double _Complex buf7; + MPI_Send(&buf7, 1, MPI_CXX_FLOAT_COMPLEX, 0, 0, MPI_COMM_WORLD); + // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: buffer type 'double _Complex' does not match the MPI datatype 'MPI_CXX_FLOAT_COMPLEX' + + std::complex<double> buf8; + MPI_Send(&buf8, 1, MPI_CXX_FLOAT_COMPLEX, 0, 0, MPI_COMM_WORLD); + // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: buffer type 'complex<double>' does not match the MPI datatype 'MPI_CXX_FLOAT_COMPLEX' +} + +void skippedTypesTests() { + // typedefs, user defined MPI and nullptr types are skipped + typedef char CHAR; + CHAR buf; + MPI_Send(&buf, 1, MPI_CXX_FLOAT_COMPLEX, 0, 0, MPI_COMM_WORLD); + + typedef unsigned UNSIGNED; + UNSIGNED buf2; + MPI_Send(&buf2, 1, MPI_CXX_FLOAT_COMPLEX, 0, 0, MPI_COMM_WORLD); + +#define _MPI_LONG MPI_LONG + int buf3; + MPI_Send(&buf3, 1, _MPI_LONG, 0, 0, MPI_COMM_WORLD); + +#define _MPI_CXX_FLOAT_COMPLEX MPI_CXX_FLOAT_COMPLEX + short buf4; + MPI_Send(&buf4, 1, _MPI_CXX_FLOAT_COMPLEX, 0, 0, MPI_COMM_WORLD); + + MPI_Send(NULL, 1, MPI_LONG, 0, 0, MPI_COMM_WORLD); +} + +void positiveTests() { + char buf; + MPI_Send(&buf, 1, MPI_CHAR, 0, 0, MPI_COMM_WORLD); + + int buf2; + MPI_Send(&buf2, 1, MPI_INT, 0, 0, MPI_COMM_WORLD); + + long buf3; + MPI_Send(&buf3, 1, MPI_LONG, 0, 0, MPI_COMM_WORLD); + + int8_t buf4; + MPI_Send(&buf4, 1, MPI_INT8_T, 0, 0, MPI_COMM_WORLD); + + long double _Complex buf5; + MPI_Send(&buf5, 1, MPI_C_LONG_DOUBLE_COMPLEX, 0, 0, MPI_COMM_WORLD); + + std::complex<float> buf6; + MPI_Send(&buf6, 1, MPI_CXX_FLOAT_COMPLEX, 0, 0, MPI_COMM_WORLD); + + uint8_t buf7; + MPI_Send(&buf7, 1, MPI_UINT8_T, 0, 0, MPI_COMM_WORLD); + + uint16_t buf8; + MPI_Send(&buf8, 1, MPI_UINT16_T, 0, 0, MPI_COMM_WORLD); + + // On some systems like PPC or ARM, 'char' is unsigned by default which is why + // distinct signedness for the buffer and MPI type is tolerated. + unsigned char buf9; + MPI_Send(&buf9, 1, MPI_CHAR, 0, 0, MPI_COMM_WORLD); +} Index: clang-tools-extra/trunk/test/clang-tidy/Inputs/mpi-type-mismatch/mpimock.h =================================================================== --- clang-tools-extra/trunk/test/clang-tidy/Inputs/mpi-type-mismatch/mpimock.h +++ clang-tools-extra/trunk/test/clang-tidy/Inputs/mpi-type-mismatch/mpimock.h @@ -0,0 +1,62 @@ +// This Message Passing Interface mock header is used, to mock typedefs, +// constants and functions, required for integration tests being part of +// clang-tidy MPI checks. + +#ifndef MPIMOCK_H +#define MPIMOCK_H + +#define NULL 0 + +// These typedefs are used to mock MPI types, fixed width integer types and the +// templated C++ complex number type. +typedef int MPI_Datatype; +typedef int MPI_Comm; +typedef int MPI_Request; +typedef int MPI_Status; +typedef int MPI_Op; +typedef int int8_t; +typedef int uint8_t; +typedef int uint16_t; +typedef int int64_t; +namespace std { template<class T> struct complex { T real; T imag; }; } + +// These defines are used to mock MPI constants. +#define MPI_DATATYPE_NULL 0 +#define MPI_CHAR 0 +#define MPI_BYTE 0 +#define MPI_INT 0 +#define MPI_LONG 0 +#define MPI_LONG_DOUBLE 0 +#define MPI_UNSIGNED 0 +#define MPI_INT8_T 0 +#define MPI_UINT8_T 0 +#define MPI_UINT16_T 0 +#define MPI_C_LONG_DOUBLE_COMPLEX 0 +#define MPI_FLOAT 0 +#define MPI_DOUBLE 0 +#define MPI_CXX_BOOL 0 +#define MPI_CXX_FLOAT_COMPLEX 0 +#define MPI_CXX_DOUBLE_COMPLEX 0 +#define MPI_CXX_LONG_DOUBLE_COMPLEX 0 +#define MPI_IN_PLACE 0 +#define MPI_COMM_WORLD 0 +#define MPI_STATUS_IGNORE 0 +#define MPI_STATUSES_IGNORE 0 +#define MPI_SUM 0 + +// These declarations are used to mock MPI functions. +int MPI_Comm_size(MPI_Comm, int *); +int MPI_Comm_rank(MPI_Comm, int *); +int MPI_Send(const void *, int, MPI_Datatype, int, int, MPI_Comm); +int MPI_Recv(void *, int, MPI_Datatype, int, int, MPI_Comm, MPI_Status *); +int MPI_Isend(const void *, int, MPI_Datatype, int, int, MPI_Comm, + MPI_Request *); +int MPI_Irecv(void *, int, MPI_Datatype, int, int, MPI_Comm, MPI_Request *); +int MPI_Wait(MPI_Request *, MPI_Status *); +int MPI_Waitall(int, MPI_Request[], MPI_Status[]); +int MPI_Reduce(const void *, void *, int, MPI_Datatype, MPI_Op, int, MPI_Comm); +int MPI_Ireduce(const void *, void *, int, MPI_Datatype, MPI_Op, int, MPI_Comm, + MPI_Request *); +int MPI_Bcast(void *, int count, MPI_Datatype, int, MPI_Comm); + +#endif // end of include guard: MPIMOCK_H Index: clang-tools-extra/trunk/clang-tidy/plugin/CMakeLists.txt =================================================================== --- clang-tools-extra/trunk/clang-tidy/plugin/CMakeLists.txt +++ clang-tools-extra/trunk/clang-tidy/plugin/CMakeLists.txt @@ -15,6 +15,7 @@ clangTidyLLVMModule clangTidyMiscModule clangTidyModernizeModule + clangTidyMPIModule clangTidyPerformanceModule clangTidyReadabilityModule clangTooling Index: clang-tools-extra/trunk/clang-tidy/mpi/TypeMismatchCheck.cpp =================================================================== --- clang-tools-extra/trunk/clang-tidy/mpi/TypeMismatchCheck.cpp +++ clang-tools-extra/trunk/clang-tidy/mpi/TypeMismatchCheck.cpp @@ -0,0 +1,336 @@ +//===--- TypeMismatchCheck.cpp - clang-tidy--------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "TypeMismatchCheck.h" +#include "clang/Lex/Lexer.h" +#include "clang/StaticAnalyzer/Checkers/MPIFunctionClassifier.h" +#include "clang/Tooling/FixIt.h" +#include <map> +#include <unordered_set> + +using namespace clang::ast_matchers; + +namespace clang { +namespace tidy { +namespace mpi { + +/// Check if a BuiltinType::Kind matches the MPI datatype. +/// +/// \param MultiMap datatype group +/// \param Kind buffer type kind +/// \param MPIDatatype name of the MPI datatype +/// +/// \returns true if the pair matches +static bool +isMPITypeMatching(const std::multimap<BuiltinType::Kind, std::string> &MultiMap, + const BuiltinType::Kind Kind, + const std::string &MPIDatatype) { + auto ItPair = MultiMap.equal_range(Kind); + while (ItPair.first != ItPair.second) { + if (ItPair.first->second == MPIDatatype) + return true; + ++ItPair.first; + } + return false; +} + +/// Check if the MPI datatype is a standard type. +/// +/// \param MPIDatatype name of the MPI datatype +/// +/// \returns true if the type is a standard type +static bool isStandardMPIDatatype(const std::string &MPIDatatype) { + static std::unordered_set<std::string> AllTypes = { + "MPI_C_BOOL", + "MPI_CHAR", + "MPI_SIGNED_CHAR", + "MPI_UNSIGNED_CHAR", + "MPI_WCHAR", + "MPI_INT", + "MPI_LONG", + "MPI_SHORT", + "MPI_LONG_LONG", + "MPI_LONG_LONG_INT", + "MPI_UNSIGNED", + "MPI_UNSIGNED_SHORT", + "MPI_UNSIGNED_LONG", + "MPI_UNSIGNED_LONG_LONG", + "MPI_FLOAT", + "MPI_DOUBLE", + "MPI_LONG_DOUBLE", + "MPI_C_COMPLEX", + "MPI_C_FLOAT_COMPLEX", + "MPI_C_DOUBLE_COMPLEX", + "MPI_C_LONG_DOUBLE_COMPLEX", + "MPI_INT8_T", + "MPI_INT16_T", + "MPI_INT32_T", + "MPI_INT64_T", + "MPI_UINT8_T", + "MPI_UINT16_T", + "MPI_UINT32_T", + "MPI_UINT64_T", + "MPI_CXX_BOOL", + "MPI_CXX_FLOAT_COMPLEX", + "MPI_CXX_DOUBLE_COMPLEX", + "MPI_CXX_LONG_DOUBLE_COMPLEX"}; + + return AllTypes.find(MPIDatatype) != AllTypes.end(); +} + +/// Check if a BuiltinType matches the MPI datatype. +/// +/// \param Builtin the builtin type +/// \param BufferTypeName buffer type name, gets assigned +/// \param MPIDatatype name of the MPI datatype +/// \param LO language options +/// +/// \returns true if the type matches +static bool isBuiltinTypeMatching(const BuiltinType *Builtin, + std::string &BufferTypeName, + const std::string &MPIDatatype, + const LangOptions &LO) { + static std::multimap<BuiltinType::Kind, std::string> BuiltinMatches = { + // On some systems like PPC or ARM, 'char' is unsigned by default which is + // why distinct signedness for the buffer and MPI type is tolerated. + {BuiltinType::SChar, "MPI_CHAR"}, + {BuiltinType::SChar, "MPI_SIGNED_CHAR"}, + {BuiltinType::SChar, "MPI_UNSIGNED_CHAR"}, + {BuiltinType::Char_S, "MPI_CHAR"}, + {BuiltinType::Char_S, "MPI_SIGNED_CHAR"}, + {BuiltinType::Char_S, "MPI_UNSIGNED_CHAR"}, + {BuiltinType::UChar, "MPI_CHAR"}, + {BuiltinType::UChar, "MPI_SIGNED_CHAR"}, + {BuiltinType::UChar, "MPI_UNSIGNED_CHAR"}, + {BuiltinType::Char_U, "MPI_CHAR"}, + {BuiltinType::Char_U, "MPI_SIGNED_CHAR"}, + {BuiltinType::Char_U, "MPI_UNSIGNED_CHAR"}, + {BuiltinType::WChar_S, "MPI_WCHAR"}, + {BuiltinType::WChar_U, "MPI_WCHAR"}, + {BuiltinType::Bool, "MPI_C_BOOL"}, + {BuiltinType::Bool, "MPI_CXX_BOOL"}, + {BuiltinType::Short, "MPI_SHORT"}, + {BuiltinType::Int, "MPI_INT"}, + {BuiltinType::Long, "MPI_LONG"}, + {BuiltinType::LongLong, "MPI_LONG_LONG"}, + {BuiltinType::LongLong, "MPI_LONG_LONG_INT"}, + {BuiltinType::UShort, "MPI_UNSIGNED_SHORT"}, + {BuiltinType::UInt, "MPI_UNSIGNED"}, + {BuiltinType::ULong, "MPI_UNSIGNED_LONG"}, + {BuiltinType::ULongLong, "MPI_UNSIGNED_LONG_LONG"}, + {BuiltinType::Float, "MPI_FLOAT"}, + {BuiltinType::Double, "MPI_DOUBLE"}, + {BuiltinType::LongDouble, "MPI_LONG_DOUBLE"}}; + + if (!isMPITypeMatching(BuiltinMatches, Builtin->getKind(), MPIDatatype)) { + BufferTypeName = Builtin->getName(LO); + return false; + } + + return true; +} + +/// Check if a complex float/double/long double buffer type matches +/// the MPI datatype. +/// +/// \param Complex buffer type +/// \param BufferTypeName buffer type name, gets assigned +/// \param MPIDatatype name of the MPI datatype +/// \param LO language options +/// +/// \returns true if the type matches or the buffer type is unknown +static bool isCComplexTypeMatching(const ComplexType *const Complex, + std::string &BufferTypeName, + const std::string &MPIDatatype, + const LangOptions &LO) { + static std::multimap<BuiltinType::Kind, std::string> ComplexCMatches = { + {BuiltinType::Float, "MPI_C_COMPLEX"}, + {BuiltinType::Float, "MPI_C_FLOAT_COMPLEX"}, + {BuiltinType::Double, "MPI_C_DOUBLE_COMPLEX"}, + {BuiltinType::LongDouble, "MPI_C_LONG_DOUBLE_COMPLEX"}}; + + const auto *Builtin = + Complex->getElementType().getTypePtr()->getAs<BuiltinType>(); + + if (Builtin && + !isMPITypeMatching(ComplexCMatches, Builtin->getKind(), MPIDatatype)) { + BufferTypeName = (llvm::Twine(Builtin->getName(LO)) + " _Complex").str(); + return false; + } + return true; +} + +/// Check if a complex<float/double/long double> templated buffer type matches +/// the MPI datatype. +/// +/// \param Complex buffer type +/// \param BufferTypeName buffer type name, gets assigned +/// \param MPIDatatype name of the MPI datatype +/// \param LO language options +/// +/// \returns true if the type matches or the buffer type is unknown +static bool +isCXXComplexTypeMatching(const TemplateSpecializationType *const Template, + std::string &BufferTypeName, + const std::string &MPIDatatype, + const LangOptions &LO) { + static std::multimap<BuiltinType::Kind, std::string> ComplexCXXMatches = { + {BuiltinType::Float, "MPI_CXX_FLOAT_COMPLEX"}, + {BuiltinType::Double, "MPI_CXX_DOUBLE_COMPLEX"}, + {BuiltinType::LongDouble, "MPI_CXX_LONG_DOUBLE_COMPLEX"}}; + + if (Template->getAsCXXRecordDecl()->getName() != "complex") + return true; + + const auto *Builtin = + Template->getArg(0).getAsType().getTypePtr()->getAs<BuiltinType>(); + + if (Builtin && + !isMPITypeMatching(ComplexCXXMatches, Builtin->getKind(), MPIDatatype)) { + BufferTypeName = + (llvm::Twine("complex<") + Builtin->getName(LO) + ">").str(); + return false; + } + + return true; +} + +/// Check if a fixed size width buffer type matches the MPI datatype. +/// +/// \param Complex buffer type +/// \param BufferTypeName buffer type name, gets assigned +/// \param MPIDatatype name of the MPI datatype +/// +/// \returns true if the type matches or the buffer type is unknown +static bool isTypedefTypeMatching(const TypedefType *const Typedef, + std::string &BufferTypeName, + const std::string &MPIDatatype) { + static llvm::StringMap<std::string> FixedWidthMatches = { + {"int8_t", "MPI_INT8_T"}, {"int16_t", "MPI_INT16_T"}, + {"int32_t", "MPI_INT32_T"}, {"int64_t", "MPI_INT64_T"}, + {"uint8_t", "MPI_UINT8_T"}, {"uint16_t", "MPI_UINT16_T"}, + {"uint32_t", "MPI_UINT32_T"}, {"uint64_t", "MPI_UINT64_T"}}; + + const auto it = FixedWidthMatches.find(Typedef->getDecl()->getName()); + // Check if the typedef is known and not matching the MPI datatype. + if (it != FixedWidthMatches.end() && it->getValue() != MPIDatatype) { + BufferTypeName = Typedef->getDecl()->getName(); + return false; + } + return true; +} + +/// Get the unqualified, dereferenced type of an argument. +/// +/// \param CE call expression +/// \param idx argument index +/// +/// \returns type of the argument +static const Type *argumentType(const CallExpr *const CE, const size_t idx) { + const QualType QT = CE->getArg(idx)->IgnoreImpCasts()->getType(); + return QT.getTypePtr()->getPointeeOrArrayElementType(); +} + +void TypeMismatchCheck::registerMatchers(MatchFinder *Finder) { + Finder->addMatcher(callExpr().bind("CE"), this); +} + +void TypeMismatchCheck::check(const MatchFinder::MatchResult &Result) { + static ento::mpi::MPIFunctionClassifier FuncClassifier(*Result.Context); + const CallExpr *const CE = Result.Nodes.getNodeAs<CallExpr>("CE"); + if (!CE->getDirectCallee()) + return; + + const IdentifierInfo *Identifier = CE->getDirectCallee()->getIdentifier(); + if (!Identifier || !FuncClassifier.isMPIType(Identifier)) + return; + + // These containers are used, to capture buffer, MPI datatype pairs. + SmallVector<const Type *, 1> BufferTypes; + SmallVector<const Expr *, 1> BufferExprs; + SmallVector<StringRef, 1> MPIDatatypes; + + // Adds a buffer, MPI datatype pair of an MPI call expression to the + // containers. For buffers, the type and expression is captured. + auto addPair = [&CE, &Result, &BufferTypes, &BufferExprs, &MPIDatatypes]( + const size_t BufferIdx, const size_t DatatypeIdx) { + // Skip null pointer constants and in place 'operators'. + if (CE->getArg(BufferIdx)->isNullPointerConstant( + *Result.Context, Expr::NPC_ValueDependentIsNull) || + tooling::fixit::getText(*CE->getArg(BufferIdx), *Result.Context) == + "MPI_IN_PLACE") + return; + + StringRef MPIDatatype = + tooling::fixit::getText(*CE->getArg(DatatypeIdx), *Result.Context); + + const Type *ArgType = argumentType(CE, BufferIdx); + // Skip unknown MPI datatypes and void pointers. + if (!isStandardMPIDatatype(MPIDatatype) || ArgType->isVoidType()) + return; + + BufferTypes.push_back(ArgType); + BufferExprs.push_back(CE->getArg(BufferIdx)); + MPIDatatypes.push_back(MPIDatatype); + }; + + // Collect all buffer, MPI datatype pairs for the inspected call expression. + if (FuncClassifier.isPointToPointType(Identifier)) { + addPair(0, 2); + } else if (FuncClassifier.isCollectiveType(Identifier)) { + if (FuncClassifier.isReduceType(Identifier)) { + addPair(0, 3); + addPair(1, 3); + } else if (FuncClassifier.isScatterType(Identifier) || + FuncClassifier.isGatherType(Identifier) || + FuncClassifier.isAlltoallType(Identifier)) { + addPair(0, 2); + addPair(3, 5); + } else if (FuncClassifier.isBcastType(Identifier)) { + addPair(0, 2); + } + } + checkArguments(BufferTypes, BufferExprs, MPIDatatypes, + Result.Context->getLangOpts()); +} + +void TypeMismatchCheck::checkArguments(ArrayRef<const Type *> BufferTypes, + ArrayRef<const Expr *> BufferExprs, + ArrayRef<StringRef> MPIDatatypes, + const LangOptions &LO) { + std::string BufferTypeName; + + for (size_t i = 0; i < MPIDatatypes.size(); ++i) { + const Type *const BT = BufferTypes[i]; + bool Error = false; + + if (const auto *Typedef = BT->getAs<TypedefType>()) { + Error = !isTypedefTypeMatching(Typedef, BufferTypeName, MPIDatatypes[i]); + } else if (const auto *Complex = BT->getAs<ComplexType>()) { + Error = + !isCComplexTypeMatching(Complex, BufferTypeName, MPIDatatypes[i], LO); + } else if (const auto *Template = BT->getAs<TemplateSpecializationType>()) { + Error = !isCXXComplexTypeMatching(Template, BufferTypeName, + MPIDatatypes[i], LO); + } else if (const auto *Builtin = BT->getAs<BuiltinType>()) { + Error = + !isBuiltinTypeMatching(Builtin, BufferTypeName, MPIDatatypes[i], LO); + } + + if (Error) { + const auto Loc = BufferExprs[i]->getSourceRange().getBegin(); + diag(Loc, "buffer type '%0' does not match the MPI datatype '%1'") + << BufferTypeName << MPIDatatypes[i]; + } + } +} + +} // namespace mpi +} // namespace tidy +} // namespace clang Index: clang-tools-extra/trunk/clang-tidy/mpi/MPITidyModule.cpp =================================================================== --- clang-tools-extra/trunk/clang-tidy/mpi/MPITidyModule.cpp +++ clang-tools-extra/trunk/clang-tidy/mpi/MPITidyModule.cpp @@ -0,0 +1,37 @@ +//===--- MPITidyModule.cpp - clang-tidy -----------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "../ClangTidy.h" +#include "../ClangTidyModule.h" +#include "../ClangTidyModuleRegistry.h" +#include "TypeMismatchCheck.h" + +namespace clang { +namespace tidy { +namespace mpi { + +class MPIModule : public ClangTidyModule { +public: + void addCheckFactories(ClangTidyCheckFactories &CheckFactories) override { + CheckFactories.registerCheck<TypeMismatchCheck>("mpi-type-mismatch"); + } +}; + +} // namespace mpi + +// Register the MPITidyModule using this statically initialized variable. +static ClangTidyModuleRegistry::Add<mpi::MPIModule> + X("mpi-module", "Adds MPI clang-tidy checks."); + +// This anchor is used to force the linker to link in the generated object file +// and thus register the MPIModule. +volatile int MPIModuleAnchorSource = 0; + +} // namespace tidy +} // namespace clang Index: clang-tools-extra/trunk/clang-tidy/mpi/CMakeLists.txt =================================================================== --- clang-tools-extra/trunk/clang-tidy/mpi/CMakeLists.txt +++ clang-tools-extra/trunk/clang-tidy/mpi/CMakeLists.txt @@ -0,0 +1,15 @@ +set(LLVM_LINK_COMPONENTS support) + +add_clang_library(clangTidyMPIModule + MPITidyModule.cpp + TypeMismatchCheck.cpp + + LINK_LIBS + clangAST + clangASTMatchers + clangBasic + clangLex + clangTidy + clangTidyUtils + clangTooling + ) Index: clang-tools-extra/trunk/clang-tidy/mpi/TypeMismatchCheck.h =================================================================== --- clang-tools-extra/trunk/clang-tidy/mpi/TypeMismatchCheck.h +++ clang-tools-extra/trunk/clang-tidy/mpi/TypeMismatchCheck.h @@ -0,0 +1,52 @@ +//===--- TypeMismatchCheck.h - clang-tidy------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MPI_TYPE_MISMATCH_H +#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MPI_TYPE_MISMATCH_H + +#include "../ClangTidy.h" +#include "clang/ASTMatchers/ASTMatchFinder.h" + +namespace clang { +namespace tidy { +namespace mpi { + +/// This check verifies if buffer type and MPI (Message Passing Interface) +/// datatype pairs match. All MPI datatypes defined by the MPI standard (3.1) +/// are verified by this check. User defined typedefs, custom MPI datatypes and +/// null pointer constants are skipped, in the course of verification. +/// +/// For the user-facing documentation see: +/// http://clang.llvm.org/extra/clang-tidy/checks/mpi-type-mismatch.html +class TypeMismatchCheck : public ClangTidyCheck { +public: + TypeMismatchCheck(StringRef Name, ClangTidyContext *Context) + : ClangTidyCheck(Name, Context) {} + + void registerMatchers(ast_matchers::MatchFinder *Finder) override; + void check(const ast_matchers::MatchFinder::MatchResult &Result) override; + +private: + /// Check if the buffer type MPI datatype pairs match. + /// + /// \param BufferTypes buffer types + /// \param BufferExprs buffer arguments as expressions + /// \param MPIDatatypes MPI datatype + /// \param LO language options + void checkArguments(ArrayRef<const Type *> BufferTypes, + ArrayRef<const Expr *> BufferExprs, + ArrayRef<StringRef> MPIDatatypes, + const LangOptions &LO); +}; + +} // namespace mpi +} // namespace tidy +} // namespace clang + +#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MPI_TYPE_MISMATCH_H Index: clang-tools-extra/trunk/clang-tidy/tool/CMakeLists.txt =================================================================== --- clang-tools-extra/trunk/clang-tidy/tool/CMakeLists.txt +++ clang-tools-extra/trunk/clang-tidy/tool/CMakeLists.txt @@ -20,6 +20,7 @@ clangTidyLLVMModule clangTidyMiscModule clangTidyModernizeModule + clangTidyMPIModule clangTidyPerformanceModule clangTidyReadabilityModule clangTooling Index: clang-tools-extra/trunk/clang-tidy/tool/ClangTidyMain.cpp =================================================================== --- clang-tools-extra/trunk/clang-tidy/tool/ClangTidyMain.cpp +++ clang-tools-extra/trunk/clang-tidy/tool/ClangTidyMain.cpp @@ -454,6 +454,11 @@ static int LLVM_ATTRIBUTE_UNUSED ModernizeModuleAnchorDestination = ModernizeModuleAnchorSource; +// This anchor is used to force the linker to link the MPIModule. +extern volatile int MPIModuleAnchorSource; +static int LLVM_ATTRIBUTE_UNUSED MPIModuleAnchorDestination = + MPIModuleAnchorSource; + // This anchor is used to force the linker to link the PerformanceModule. extern volatile int PerformanceModuleAnchorSource; static int LLVM_ATTRIBUTE_UNUSED PerformanceModuleAnchorDestination = Index: clang-tools-extra/trunk/clang-tidy/CMakeLists.txt =================================================================== --- clang-tools-extra/trunk/clang-tidy/CMakeLists.txt +++ clang-tools-extra/trunk/clang-tidy/CMakeLists.txt @@ -34,6 +34,7 @@ add_subdirectory(google) add_subdirectory(misc) add_subdirectory(modernize) +add_subdirectory(mpi) add_subdirectory(performance) add_subdirectory(readability) add_subdirectory(utils)
_______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits