xref: /llvm-project/clang-tools-extra/clang-tidy/mpi/BufferDerefCheck.cpp (revision 7d2ea6c422d3f5712b7253407005e1a465a76946)
1 //===--- BufferDerefCheck.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 "BufferDerefCheck.h"
10 #include "clang/AST/ASTContext.h"
11 #include "clang/ASTMatchers/ASTMatchFinder.h"
12 #include "clang/Tooling/FixIt.h"
13 
14 using namespace clang::ast_matchers;
15 
16 namespace clang::tidy::mpi {
17 
registerMatchers(MatchFinder * Finder)18 void BufferDerefCheck::registerMatchers(MatchFinder *Finder) {
19   Finder->addMatcher(callExpr().bind("CE"), this);
20 }
21 
check(const MatchFinder::MatchResult & Result)22 void BufferDerefCheck::check(const MatchFinder::MatchResult &Result) {
23   const auto *CE = Result.Nodes.getNodeAs<CallExpr>("CE");
24   if (!CE->getDirectCallee())
25     return;
26 
27   if (!FuncClassifier)
28     FuncClassifier.emplace(*Result.Context);
29 
30   const IdentifierInfo *Identifier = CE->getDirectCallee()->getIdentifier();
31   if (!Identifier || !FuncClassifier->isMPIType(Identifier))
32     return;
33 
34   // These containers are used, to capture the type and expression of a buffer.
35   SmallVector<const Type *, 1> BufferTypes;
36   SmallVector<const Expr *, 1> BufferExprs;
37 
38   // Adds the type and expression of a buffer that is used in the MPI call
39   // expression to the captured containers.
40   auto AddBuffer = [&CE, &Result, &BufferTypes,
41                     &BufferExprs](const size_t BufferIdx) {
42     // Skip null pointer constants and in place 'operators'.
43     if (CE->getArg(BufferIdx)->isNullPointerConstant(
44             *Result.Context, Expr::NPC_ValueDependentIsNull) ||
45         tooling::fixit::getText(*CE->getArg(BufferIdx), *Result.Context) ==
46             "MPI_IN_PLACE")
47       return;
48 
49     const Expr *ArgExpr = CE->getArg(BufferIdx);
50     if (!ArgExpr)
51       return;
52     const Type *ArgType = ArgExpr->IgnoreImpCasts()->getType().getTypePtr();
53     if (!ArgType)
54       return;
55     BufferExprs.push_back(ArgExpr);
56     BufferTypes.push_back(ArgType);
57   };
58 
59   // Collect buffer types and argument expressions for all buffers used in the
60   // MPI call expression. The number passed to the lambda corresponds to the
61   // argument index of the currently verified MPI function call.
62   if (FuncClassifier->isPointToPointType(Identifier)) {
63     AddBuffer(0);
64   } else if (FuncClassifier->isCollectiveType(Identifier)) {
65     if (FuncClassifier->isReduceType(Identifier)) {
66       AddBuffer(0);
67       AddBuffer(1);
68     } else if (FuncClassifier->isScatterType(Identifier) ||
69                FuncClassifier->isGatherType(Identifier) ||
70                FuncClassifier->isAlltoallType(Identifier)) {
71       AddBuffer(0);
72       AddBuffer(3);
73     } else if (FuncClassifier->isBcastType(Identifier)) {
74       AddBuffer(0);
75     }
76   }
77 
78   checkBuffers(BufferTypes, BufferExprs);
79 }
80 
checkBuffers(ArrayRef<const Type * > BufferTypes,ArrayRef<const Expr * > BufferExprs)81 void BufferDerefCheck::checkBuffers(ArrayRef<const Type *> BufferTypes,
82                                     ArrayRef<const Expr *> BufferExprs) {
83   for (size_t I = 0; I < BufferTypes.size(); ++I) {
84     unsigned IndirectionCount = 0;
85     const Type *BufferType = BufferTypes[I];
86     llvm::SmallVector<IndirectionType, 1> Indirections;
87 
88     // Capture the depth and types of indirections for the passed buffer.
89     while (true) {
90       if (BufferType->isPointerType()) {
91         BufferType = BufferType->getPointeeType().getTypePtr();
92         Indirections.push_back(IndirectionType::Pointer);
93       } else if (BufferType->isArrayType()) {
94         BufferType = BufferType->getArrayElementTypeNoTypeQual();
95         Indirections.push_back(IndirectionType::Array);
96       } else {
97         break;
98       }
99       ++IndirectionCount;
100     }
101 
102     if (IndirectionCount > 1) {
103       // Referencing an array with '&' is valid, as this also points to the
104       // beginning of the array.
105       if (IndirectionCount == 2 &&
106           Indirections[0] == IndirectionType::Pointer &&
107           Indirections[1] == IndirectionType::Array)
108         return;
109 
110       // Build the indirection description in reverse order of discovery.
111       std::string IndirectionDesc;
112       for (auto It = Indirections.rbegin(); It != Indirections.rend(); ++It) {
113         if (!IndirectionDesc.empty())
114           IndirectionDesc += "->";
115         if (*It == IndirectionType::Pointer) {
116           IndirectionDesc += "pointer";
117         } else {
118           IndirectionDesc += "array";
119         }
120       }
121 
122       const auto Loc = BufferExprs[I]->getSourceRange().getBegin();
123       diag(Loc, "buffer is insufficiently dereferenced: %0") << IndirectionDesc;
124     }
125   }
126 }
127 
onEndOfTranslationUnit()128 void BufferDerefCheck::onEndOfTranslationUnit() { FuncClassifier.reset(); }
129 } // namespace clang::tidy::mpi
130