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::tidy::mpi {
17
18void BufferDerefCheck::registerMatchers(MatchFinder *Finder) {
19 Finder->addMatcher(NodeMatch: callExpr().bind(ID: "CE"), Action: this);
20}
21
22void BufferDerefCheck::check(const MatchFinder::MatchResult &Result) {
23 const auto *CE = Result.Nodes.getNodeAs<CallExpr>(ID: "CE");
24 if (!CE->getDirectCallee())
25 return;
26
27 if (!FuncClassifier)
28 FuncClassifier.emplace(args&: *Result.Context);
29
30 const IdentifierInfo *Identifier = CE->getDirectCallee()->getIdentifier();
31 if (!Identifier || !FuncClassifier->isMPIType(IdentInfo: 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(Arg: BufferIdx)->isNullPointerConstant(
44 Ctx&: *Result.Context, NPC: Expr::NPC_ValueDependentIsNull) ||
45 tooling::fixit::getText(Node: *CE->getArg(Arg: BufferIdx), Context: *Result.Context) ==
46 "MPI_IN_PLACE")
47 return;
48
49 const Expr *ArgExpr = CE->getArg(Arg: BufferIdx);
50 if (!ArgExpr)
51 return;
52 const Type *ArgType = ArgExpr->IgnoreImpCasts()->getType().getTypePtr();
53 if (!ArgType)
54 return;
55 BufferExprs.push_back(Elt: ArgExpr);
56 BufferTypes.push_back(Elt: 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(IdentInfo: Identifier)) {
63 AddBuffer(0);
64 } else if (FuncClassifier->isCollectiveType(IdentInfo: Identifier)) {
65 if (FuncClassifier->isReduceType(IdentInfo: Identifier)) {
66 AddBuffer(0);
67 AddBuffer(1);
68 } else if (FuncClassifier->isScatterType(IdentInfo: Identifier) ||
69 FuncClassifier->isGatherType(IdentInfo: Identifier) ||
70 FuncClassifier->isAlltoallType(IdentInfo: Identifier)) {
71 AddBuffer(0);
72 AddBuffer(3);
73 } else if (FuncClassifier->isBcastType(IdentInfo: Identifier)) {
74 AddBuffer(0);
75 }
76 }
77
78 checkBuffers(BufferTypes, BufferExprs);
79}
80
81void 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(Elt: IndirectionType::Pointer);
93 } else if (BufferType->isArrayType()) {
94 BufferType = BufferType->getArrayElementTypeNoTypeQual();
95 Indirections.push_back(Elt: 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
128void BufferDerefCheck::onEndOfTranslationUnit() { FuncClassifier.reset(); }
129} // namespace clang::tidy::mpi
130

source code of clang-tools-extra/clang-tidy/mpi/BufferDerefCheck.cpp