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