Imported Upstream version 6.10.0.49

Former-commit-id: 1d6753294b2993e1fbf92de9366bb9544db4189b
This commit is contained in:
Xamarin Public Jenkins (auto-signing)
2020-01-16 16:38:04 +00:00
parent d94e79959b
commit 468663ddbb
48518 changed files with 2789335 additions and 61176 deletions

View File

@@ -0,0 +1,132 @@
//===--- BufferDerefCheck.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 "BufferDerefCheck.h"
#include "clang/AST/ASTContext.h"
#include "clang/ASTMatchers/ASTMatchFinder.h"
#include "clang/StaticAnalyzer/Checkers/MPIFunctionClassifier.h"
#include "clang/Tooling/FixIt.h"
using namespace clang::ast_matchers;
namespace clang {
namespace tidy {
namespace mpi {
void BufferDerefCheck::registerMatchers(MatchFinder *Finder) {
Finder->addMatcher(callExpr().bind("CE"), this);
}
void BufferDerefCheck::check(const MatchFinder::MatchResult &Result) {
static ento::mpi::MPIFunctionClassifier FuncClassifier(*Result.Context);
const auto *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 the type and expression of a buffer.
SmallVector<const Type *, 1> BufferTypes;
SmallVector<const Expr *, 1> BufferExprs;
// Adds the type and expression of a buffer that is used in the MPI call
// expression to the captured containers.
auto addBuffer = [&CE, &Result, &BufferTypes,
&BufferExprs](const size_t BufferIdx) {
// 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;
const Expr *ArgExpr = CE->getArg(BufferIdx);
if (!ArgExpr)
return;
const Type *ArgType = ArgExpr->IgnoreImpCasts()->getType().getTypePtr();
if (!ArgType)
return;
BufferExprs.push_back(ArgExpr);
BufferTypes.push_back(ArgType);
};
// Collect buffer types and argument expressions for all buffers used in the
// MPI call expression. The number passed to the lambda corresponds to the
// argument index of the currently verified MPI function call.
if (FuncClassifier.isPointToPointType(Identifier)) {
addBuffer(0);
} else if (FuncClassifier.isCollectiveType(Identifier)) {
if (FuncClassifier.isReduceType(Identifier)) {
addBuffer(0);
addBuffer(1);
} else if (FuncClassifier.isScatterType(Identifier) ||
FuncClassifier.isGatherType(Identifier) ||
FuncClassifier.isAlltoallType(Identifier)) {
addBuffer(0);
addBuffer(3);
} else if (FuncClassifier.isBcastType(Identifier)) {
addBuffer(0);
}
}
checkBuffers(BufferTypes, BufferExprs);
}
void BufferDerefCheck::checkBuffers(ArrayRef<const Type *> BufferTypes,
ArrayRef<const Expr *> BufferExprs) {
for (size_t i = 0; i < BufferTypes.size(); ++i) {
unsigned IndirectionCount = 0;
const Type *BufferType = BufferTypes[i];
llvm::SmallVector<IndirectionType, 1> Indirections;
// Capture the depth and types of indirections for the passed buffer.
while (true) {
if (BufferType->isPointerType()) {
BufferType = BufferType->getPointeeType().getTypePtr();
Indirections.push_back(IndirectionType::Pointer);
} else if (BufferType->isArrayType()) {
BufferType = BufferType->getArrayElementTypeNoTypeQual();
Indirections.push_back(IndirectionType::Array);
} else {
break;
}
++IndirectionCount;
}
if (IndirectionCount > 1) {
// Referencing an array with '&' is valid, as this also points to the
// beginning of the array.
if (IndirectionCount == 2 &&
Indirections[0] == IndirectionType::Pointer &&
Indirections[1] == IndirectionType::Array)
return;
// Build the indirection description in reverse order of discovery.
std::string IndirectionDesc;
for (auto It = Indirections.rbegin(); It != Indirections.rend(); ++It) {
if (!IndirectionDesc.empty())
IndirectionDesc += "->";
if (*It == IndirectionType::Pointer) {
IndirectionDesc += "pointer";
} else {
IndirectionDesc += "array";
}
}
const auto Loc = BufferExprs[i]->getSourceRange().getBegin();
diag(Loc, "buffer is insufficiently dereferenced: %0") << IndirectionDesc;
}
}
}
} // namespace mpi
} // namespace tidy
} // namespace clang

View File

@@ -0,0 +1,51 @@
//===--- BufferDerefCheck.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_BUFFER_DEREF_H
#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MPI_BUFFER_DEREF_H
#include "../ClangTidy.h"
namespace clang {
namespace tidy {
namespace mpi {
/// This check verifies if a buffer passed to an MPI (Message Passing Interface)
/// function is sufficiently dereferenced. Buffers should be passed as a single
/// pointer or array. As MPI function signatures specify void * for their buffer
/// types, insufficiently dereferenced buffers can be passed, like for example
/// as double pointers or multidimensional arrays, without a compiler warning
/// emitted.
///
/// For the user-facing documentation see:
/// http://clang.llvm.org/extra/clang-tidy/checks/mpi-buffer-deref.html
class BufferDerefCheck : public ClangTidyCheck {
public:
BufferDerefCheck(StringRef Name, ClangTidyContext *Context)
: ClangTidyCheck(Name, Context) {}
void registerMatchers(ast_matchers::MatchFinder *Finder) override;
void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
private:
/// Checks for all buffers in an MPI call if they are sufficiently
/// dereferenced.
///
/// \param BufferTypes buffer types
/// \param BufferExprs buffer arguments as expressions
void checkBuffers(ArrayRef<const Type *> BufferTypes,
ArrayRef<const Expr *> BufferExprs);
enum class IndirectionType : unsigned char { Pointer, Array };
};
} // namespace mpi
} // namespace tidy
} // namespace clang
#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MPI_BUFFER_DEREF_H

View File

@@ -0,0 +1,17 @@
set(LLVM_LINK_COMPONENTS support)
add_clang_library(clangTidyMPIModule
BufferDerefCheck.cpp
MPITidyModule.cpp
TypeMismatchCheck.cpp
LINK_LIBS
clangAST
clangASTMatchers
clangBasic
clangLex
clangTidy
clangTidyUtils
clangTooling
clangStaticAnalyzerCheckers
)

View File

@@ -0,0 +1,39 @@
//===--- 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 "BufferDerefCheck.h"
#include "TypeMismatchCheck.h"
namespace clang {
namespace tidy {
namespace mpi {
class MPIModule : public ClangTidyModule {
public:
void addCheckFactories(ClangTidyCheckFactories &CheckFactories) override {
CheckFactories.registerCheck<BufferDerefCheck>("mpi-buffer-deref");
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

View File

@@ -0,0 +1,335 @@
//===--- 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 Template 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 Typedef 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 auto *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, 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

View File

@@ -0,0 +1,51 @@
//===--- 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