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 | |
18 | void BufferDerefCheck::registerMatchers(MatchFinder *Finder) { |
19 | Finder->addMatcher(NodeMatch: callExpr().bind(ID: "CE" ), Action: this); |
20 | } |
21 | |
22 | void 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 | |
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(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 | |
128 | void BufferDerefCheck::onEndOfTranslationUnit() { FuncClassifier.reset(); } |
129 | } // namespace clang::tidy::mpi |
130 | |