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