11512f9a0SAlexander Droste //===--- BufferDerefCheck.cpp - clang-tidy---------------------------------===//
21512f9a0SAlexander Droste //
32946cd70SChandler Carruth // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
42946cd70SChandler Carruth // See https://llvm.org/LICENSE.txt for license information.
52946cd70SChandler Carruth // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
61512f9a0SAlexander Droste //
71512f9a0SAlexander Droste //===----------------------------------------------------------------------===//
81512f9a0SAlexander Droste
91512f9a0SAlexander Droste #include "BufferDerefCheck.h"
101512f9a0SAlexander Droste #include "clang/AST/ASTContext.h"
111512f9a0SAlexander Droste #include "clang/ASTMatchers/ASTMatchFinder.h"
121512f9a0SAlexander Droste #include "clang/Tooling/FixIt.h"
131512f9a0SAlexander Droste
141512f9a0SAlexander Droste using namespace clang::ast_matchers;
151512f9a0SAlexander Droste
16*7d2ea6c4SCarlos Galvez namespace clang::tidy::mpi {
171512f9a0SAlexander Droste
registerMatchers(MatchFinder * Finder)181512f9a0SAlexander Droste void BufferDerefCheck::registerMatchers(MatchFinder *Finder) {
191512f9a0SAlexander Droste Finder->addMatcher(callExpr().bind("CE"), this);
201512f9a0SAlexander Droste }
211512f9a0SAlexander Droste
check(const MatchFinder::MatchResult & Result)221512f9a0SAlexander Droste void BufferDerefCheck::check(const MatchFinder::MatchResult &Result) {
231512f9a0SAlexander Droste const auto *CE = Result.Nodes.getNodeAs<CallExpr>("CE");
241512f9a0SAlexander Droste if (!CE->getDirectCallee())
251512f9a0SAlexander Droste return;
261512f9a0SAlexander Droste
2702d7ef31SNathan James if (!FuncClassifier)
2802d7ef31SNathan James FuncClassifier.emplace(*Result.Context);
2902d7ef31SNathan James
301512f9a0SAlexander Droste const IdentifierInfo *Identifier = CE->getDirectCallee()->getIdentifier();
3102d7ef31SNathan James if (!Identifier || !FuncClassifier->isMPIType(Identifier))
321512f9a0SAlexander Droste return;
331512f9a0SAlexander Droste
341512f9a0SAlexander Droste // These containers are used, to capture the type and expression of a buffer.
351512f9a0SAlexander Droste SmallVector<const Type *, 1> BufferTypes;
361512f9a0SAlexander Droste SmallVector<const Expr *, 1> BufferExprs;
371512f9a0SAlexander Droste
381512f9a0SAlexander Droste // Adds the type and expression of a buffer that is used in the MPI call
391512f9a0SAlexander Droste // expression to the captured containers.
40ab2d3ce4SAlexander Kornienko auto AddBuffer = [&CE, &Result, &BufferTypes,
411512f9a0SAlexander Droste &BufferExprs](const size_t BufferIdx) {
421512f9a0SAlexander Droste // Skip null pointer constants and in place 'operators'.
431512f9a0SAlexander Droste if (CE->getArg(BufferIdx)->isNullPointerConstant(
441512f9a0SAlexander Droste *Result.Context, Expr::NPC_ValueDependentIsNull) ||
451512f9a0SAlexander Droste tooling::fixit::getText(*CE->getArg(BufferIdx), *Result.Context) ==
461512f9a0SAlexander Droste "MPI_IN_PLACE")
471512f9a0SAlexander Droste return;
481512f9a0SAlexander Droste
491512f9a0SAlexander Droste const Expr *ArgExpr = CE->getArg(BufferIdx);
501512f9a0SAlexander Droste if (!ArgExpr)
511512f9a0SAlexander Droste return;
521512f9a0SAlexander Droste const Type *ArgType = ArgExpr->IgnoreImpCasts()->getType().getTypePtr();
531512f9a0SAlexander Droste if (!ArgType)
541512f9a0SAlexander Droste return;
551512f9a0SAlexander Droste BufferExprs.push_back(ArgExpr);
561512f9a0SAlexander Droste BufferTypes.push_back(ArgType);
571512f9a0SAlexander Droste };
581512f9a0SAlexander Droste
591512f9a0SAlexander Droste // Collect buffer types and argument expressions for all buffers used in the
601512f9a0SAlexander Droste // MPI call expression. The number passed to the lambda corresponds to the
611512f9a0SAlexander Droste // argument index of the currently verified MPI function call.
6202d7ef31SNathan James if (FuncClassifier->isPointToPointType(Identifier)) {
63ab2d3ce4SAlexander Kornienko AddBuffer(0);
6402d7ef31SNathan James } else if (FuncClassifier->isCollectiveType(Identifier)) {
6502d7ef31SNathan James if (FuncClassifier->isReduceType(Identifier)) {
66ab2d3ce4SAlexander Kornienko AddBuffer(0);
67ab2d3ce4SAlexander Kornienko AddBuffer(1);
6802d7ef31SNathan James } else if (FuncClassifier->isScatterType(Identifier) ||
6902d7ef31SNathan James FuncClassifier->isGatherType(Identifier) ||
7002d7ef31SNathan James FuncClassifier->isAlltoallType(Identifier)) {
71ab2d3ce4SAlexander Kornienko AddBuffer(0);
72ab2d3ce4SAlexander Kornienko AddBuffer(3);
7302d7ef31SNathan James } else if (FuncClassifier->isBcastType(Identifier)) {
74ab2d3ce4SAlexander Kornienko AddBuffer(0);
751512f9a0SAlexander Droste }
761512f9a0SAlexander Droste }
771512f9a0SAlexander Droste
781512f9a0SAlexander Droste checkBuffers(BufferTypes, BufferExprs);
791512f9a0SAlexander Droste }
801512f9a0SAlexander Droste
checkBuffers(ArrayRef<const Type * > BufferTypes,ArrayRef<const Expr * > BufferExprs)811512f9a0SAlexander Droste void BufferDerefCheck::checkBuffers(ArrayRef<const Type *> BufferTypes,
821512f9a0SAlexander Droste ArrayRef<const Expr *> BufferExprs) {
83ab2d3ce4SAlexander Kornienko for (size_t I = 0; I < BufferTypes.size(); ++I) {
841512f9a0SAlexander Droste unsigned IndirectionCount = 0;
85ab2d3ce4SAlexander Kornienko const Type *BufferType = BufferTypes[I];
861512f9a0SAlexander Droste llvm::SmallVector<IndirectionType, 1> Indirections;
871512f9a0SAlexander Droste
881512f9a0SAlexander Droste // Capture the depth and types of indirections for the passed buffer.
891512f9a0SAlexander Droste while (true) {
901512f9a0SAlexander Droste if (BufferType->isPointerType()) {
911512f9a0SAlexander Droste BufferType = BufferType->getPointeeType().getTypePtr();
921512f9a0SAlexander Droste Indirections.push_back(IndirectionType::Pointer);
931512f9a0SAlexander Droste } else if (BufferType->isArrayType()) {
941512f9a0SAlexander Droste BufferType = BufferType->getArrayElementTypeNoTypeQual();
951512f9a0SAlexander Droste Indirections.push_back(IndirectionType::Array);
961512f9a0SAlexander Droste } else {
971512f9a0SAlexander Droste break;
981512f9a0SAlexander Droste }
991512f9a0SAlexander Droste ++IndirectionCount;
1001512f9a0SAlexander Droste }
1011512f9a0SAlexander Droste
1021512f9a0SAlexander Droste if (IndirectionCount > 1) {
1031512f9a0SAlexander Droste // Referencing an array with '&' is valid, as this also points to the
1041512f9a0SAlexander Droste // beginning of the array.
1051512f9a0SAlexander Droste if (IndirectionCount == 2 &&
1061512f9a0SAlexander Droste Indirections[0] == IndirectionType::Pointer &&
1071512f9a0SAlexander Droste Indirections[1] == IndirectionType::Array)
1081512f9a0SAlexander Droste return;
1091512f9a0SAlexander Droste
1101512f9a0SAlexander Droste // Build the indirection description in reverse order of discovery.
1111512f9a0SAlexander Droste std::string IndirectionDesc;
1121512f9a0SAlexander Droste for (auto It = Indirections.rbegin(); It != Indirections.rend(); ++It) {
1131512f9a0SAlexander Droste if (!IndirectionDesc.empty())
1141512f9a0SAlexander Droste IndirectionDesc += "->";
1151512f9a0SAlexander Droste if (*It == IndirectionType::Pointer) {
1161512f9a0SAlexander Droste IndirectionDesc += "pointer";
1171512f9a0SAlexander Droste } else {
1181512f9a0SAlexander Droste IndirectionDesc += "array";
1191512f9a0SAlexander Droste }
1201512f9a0SAlexander Droste }
1211512f9a0SAlexander Droste
122ab2d3ce4SAlexander Kornienko const auto Loc = BufferExprs[I]->getSourceRange().getBegin();
1231512f9a0SAlexander Droste diag(Loc, "buffer is insufficiently dereferenced: %0") << IndirectionDesc;
1241512f9a0SAlexander Droste }
1251512f9a0SAlexander Droste }
1261512f9a0SAlexander Droste }
1271512f9a0SAlexander Droste
onEndOfTranslationUnit()12802d7ef31SNathan James void BufferDerefCheck::onEndOfTranslationUnit() { FuncClassifier.reset(); }
129*7d2ea6c4SCarlos Galvez } // namespace clang::tidy::mpi
130