xref: /llvm-project/clang-tools-extra/clang-tidy/mpi/BufferDerefCheck.cpp (revision 7d2ea6c422d3f5712b7253407005e1a465a76946)
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