• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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