clang  19.0.0git
UncountedCallArgsChecker.cpp
Go to the documentation of this file.
1 //=======- UncountedCallArgsChecker.cpp --------------------------*- C++ -*-==//
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 "ASTUtils.h"
10 #include "DiagOutputUtils.h"
11 #include "PtrTypesSemantics.h"
13 #include "clang/AST/Decl.h"
14 #include "clang/AST/DeclCXX.h"
21 #include <optional>
22 
23 using namespace clang;
24 using namespace ento;
25 
26 namespace {
27 
28 class UncountedCallArgsChecker
29  : public Checker<check::ASTDecl<TranslationUnitDecl>> {
30  BugType Bug{this,
31  "Uncounted call argument for a raw pointer/reference parameter",
32  "WebKit coding guidelines"};
33  mutable BugReporter *BR;
34 
36 
37 public:
38 
39  void checkASTDecl(const TranslationUnitDecl *TUD, AnalysisManager &MGR,
40  BugReporter &BRArg) const {
41  BR = &BRArg;
42 
43  // The calls to checkAST* from AnalysisConsumer don't
44  // visit template instantiations or lambda classes. We
45  // want to visit those, so we make our own RecursiveASTVisitor.
46  struct LocalVisitor : public RecursiveASTVisitor<LocalVisitor> {
47  const UncountedCallArgsChecker *Checker;
48  explicit LocalVisitor(const UncountedCallArgsChecker *Checker)
49  : Checker(Checker) {
50  assert(Checker);
51  }
52 
53  bool shouldVisitTemplateInstantiations() const { return true; }
54  bool shouldVisitImplicitCode() const { return false; }
55 
56  bool TraverseClassTemplateDecl(ClassTemplateDecl *Decl) {
58  return true;
60  Decl);
61  }
62 
63  bool VisitCallExpr(const CallExpr *CE) {
64  Checker->visitCallExpr(CE);
65  return true;
66  }
67  };
68 
69  LocalVisitor visitor(this);
70  visitor.TraverseDecl(const_cast<TranslationUnitDecl *>(TUD));
71  }
72 
73  void visitCallExpr(const CallExpr *CE) const {
74  if (shouldSkipCall(CE))
75  return;
76 
77  if (auto *F = CE->getDirectCallee()) {
78  // Skip the first argument for overloaded member operators (e. g. lambda
79  // or std::function call operator).
80  unsigned ArgIdx = isa<CXXOperatorCallExpr>(CE) && isa_and_nonnull<CXXMethodDecl>(F);
81 
82  if (auto *MemberCallExpr = dyn_cast<CXXMemberCallExpr>(CE)) {
83  if (auto *MD = MemberCallExpr->getMethodDecl()) {
84  auto name = safeGetName(MD);
85  if (name == "ref" || name == "deref")
86  return;
87  }
88  auto *E = MemberCallExpr->getImplicitObjectArgument();
89  QualType ArgType = MemberCallExpr->getObjectType();
90  std::optional<bool> IsUncounted =
91  isUncounted(ArgType->getAsCXXRecordDecl());
92  if (IsUncounted && *IsUncounted && !isPtrOriginSafe(E))
93  reportBugOnThis(E);
94  }
95 
96  for (auto P = F->param_begin();
97  // FIXME: Also check variadic function parameters.
98  // FIXME: Also check default function arguments. Probably a different
99  // checker. In case there are default arguments the call can have
100  // fewer arguments than the callee has parameters.
101  P < F->param_end() && ArgIdx < CE->getNumArgs(); ++P, ++ArgIdx) {
102  // TODO: attributes.
103  // if ((*P)->hasAttr<SafeRefCntblRawPtrAttr>())
104  // continue;
105 
106  const auto *ArgType = (*P)->getType().getTypePtrOrNull();
107  if (!ArgType)
108  continue; // FIXME? Should we bail?
109 
110  // FIXME: more complex types (arrays, references to raw pointers, etc)
111  std::optional<bool> IsUncounted = isUncountedPtr(ArgType);
112  if (!IsUncounted || !(*IsUncounted))
113  continue;
114 
115  const auto *Arg = CE->getArg(ArgIdx);
116 
117  if (auto *defaultArg = dyn_cast<CXXDefaultArgExpr>(Arg))
118  Arg = defaultArg->getExpr();
119 
120  if (isPtrOriginSafe(Arg))
121  continue;
122 
123  reportBug(Arg, *P);
124  }
125  }
126  }
127 
128  bool isPtrOriginSafe(const Expr *Arg) const {
129  return tryToFindPtrOrigin(Arg, /*StopAtFirstRefCountedObj=*/true,
130  [](const clang::Expr *ArgOrigin, bool IsSafe) {
131  if (IsSafe)
132  return true;
133  if (isa<CXXNullPtrLiteralExpr>(ArgOrigin)) {
134  // foo(nullptr)
135  return true;
136  }
137  if (isa<IntegerLiteral>(ArgOrigin)) {
138  // FIXME: Check the value.
139  // foo(NULL)
140  return true;
141  }
142  if (isASafeCallArg(ArgOrigin))
143  return true;
144  return false;
145  });
146  }
147 
148  bool shouldSkipCall(const CallExpr *CE) const {
149  const auto *Callee = CE->getDirectCallee();
150 
152  return true;
153 
154  if (Callee && TFA.isTrivial(Callee))
155  return true;
156 
157  if (CE->getNumArgs() == 0)
158  return false;
159 
160  // If an assignment is problematic we should warn about the sole existence
161  // of object on LHS.
162  if (auto *MemberOp = dyn_cast<CXXOperatorCallExpr>(CE)) {
163  // Note: assignemnt to built-in type isn't derived from CallExpr.
164  if (MemberOp->getOperator() ==
165  OO_Equal) { // Ignore assignment to Ref/RefPtr.
166  auto *callee = MemberOp->getDirectCallee();
167  if (auto *calleeDecl = dyn_cast<CXXMethodDecl>(callee)) {
168  if (const CXXRecordDecl *classDecl = calleeDecl->getParent()) {
169  if (isRefCounted(classDecl))
170  return true;
171  }
172  }
173  }
174  if (MemberOp->isAssignmentOp())
175  return false;
176  }
177 
178  if (!Callee)
179  return false;
180 
181  if (isMethodOnWTFContainerType(Callee))
182  return true;
183 
184  auto overloadedOperatorType = Callee->getOverloadedOperator();
185  if (overloadedOperatorType == OO_EqualEqual ||
186  overloadedOperatorType == OO_ExclaimEqual ||
187  overloadedOperatorType == OO_LessEqual ||
188  overloadedOperatorType == OO_GreaterEqual ||
189  overloadedOperatorType == OO_Spaceship ||
190  overloadedOperatorType == OO_AmpAmp ||
191  overloadedOperatorType == OO_PipePipe)
192  return true;
193 
194  if (isCtorOfRefCounted(Callee))
195  return true;
196 
197  auto name = safeGetName(Callee);
198  if (name == "adoptRef" || name == "getPtr" || name == "WeakPtr" ||
199  name == "dynamicDowncast" || name == "downcast" ||
200  name == "checkedDowncast" || name == "uncheckedDowncast" ||
201  name == "bitwise_cast" || name == "is" || name == "equal" ||
202  name == "hash" || name == "isType" ||
203  // FIXME: Most/all of these should be implemented via attributes.
204  name == "equalIgnoringASCIICase" ||
205  name == "equalIgnoringASCIICaseCommon" ||
206  name == "equalIgnoringNullity" || name == "toString")
207  return true;
208 
209  return false;
210  }
211 
212  bool isMethodOnWTFContainerType(const FunctionDecl *Decl) const {
213  if (!isa<CXXMethodDecl>(Decl))
214  return false;
215  auto *ClassDecl = Decl->getParent();
216  if (!ClassDecl || !isa<CXXRecordDecl>(ClassDecl))
217  return false;
218 
219  auto *NsDecl = ClassDecl->getParent();
220  if (!NsDecl || !isa<NamespaceDecl>(NsDecl))
221  return false;
222 
223  auto MethodName = safeGetName(Decl);
224  auto ClsNameStr = safeGetName(ClassDecl);
225  StringRef ClsName = ClsNameStr; // FIXME: Make safeGetName return StringRef.
226  auto NamespaceName = safeGetName(NsDecl);
227  // FIXME: These should be implemented via attributes.
228  return NamespaceName == "WTF" &&
229  (MethodName == "find" || MethodName == "findIf" ||
230  MethodName == "reverseFind" || MethodName == "reverseFindIf" ||
231  MethodName == "findIgnoringASCIICase" || MethodName == "get" ||
232  MethodName == "inlineGet" || MethodName == "contains" ||
233  MethodName == "containsIf" ||
234  MethodName == "containsIgnoringASCIICase" ||
235  MethodName == "startsWith" || MethodName == "endsWith" ||
236  MethodName == "startsWithIgnoringASCIICase" ||
237  MethodName == "endsWithIgnoringASCIICase" ||
238  MethodName == "substring") &&
239  (ClsName.ends_with("Vector") || ClsName.ends_with("Set") ||
240  ClsName.ends_with("Map") || ClsName == "StringImpl" ||
241  ClsName.ends_with("String"));
242  }
243 
244  void reportBug(const Expr *CallArg, const ParmVarDecl *Param) const {
245  assert(CallArg);
246 
247  SmallString<100> Buf;
248  llvm::raw_svector_ostream Os(Buf);
249 
250  const std::string paramName = safeGetName(Param);
251  Os << "Call argument";
252  if (!paramName.empty()) {
253  Os << " for parameter ";
254  printQuotedQualifiedName(Os, Param);
255  }
256  Os << " is uncounted and unsafe.";
257 
258  const SourceLocation SrcLocToReport =
259  isa<CXXDefaultArgExpr>(CallArg) ? Param->getDefaultArg()->getExprLoc()
260  : CallArg->getSourceRange().getBegin();
261 
262  PathDiagnosticLocation BSLoc(SrcLocToReport, BR->getSourceManager());
263  auto Report = std::make_unique<BasicBugReport>(Bug, Os.str(), BSLoc);
264  Report->addRange(CallArg->getSourceRange());
265  BR->emitReport(std::move(Report));
266  }
267 
268  void reportBugOnThis(const Expr *CallArg) const {
269  assert(CallArg);
270 
271  const SourceLocation SrcLocToReport = CallArg->getSourceRange().getBegin();
272 
273  PathDiagnosticLocation BSLoc(SrcLocToReport, BR->getSourceManager());
274  auto Report = std::make_unique<BasicBugReport>(
275  Bug, "Call argument for 'this' parameter is uncounted and unsafe.",
276  BSLoc);
277  Report->addRange(CallArg->getSourceRange());
278  BR->emitReport(std::move(Report));
279  }
280 };
281 } // namespace
282 
283 void ento::registerUncountedCallArgsChecker(CheckerManager &Mgr) {
284  Mgr.registerChecker<UncountedCallArgsChecker>();
285 }
286 
287 bool ento::shouldRegisterUncountedCallArgsChecker(const CheckerManager &) {
288  return true;
289 }
StringRef P
Defines the C++ Decl subclasses, other than those for templates (found in DeclTemplate....
Defines the clang::SourceLocation class and associated facilities.
Represents a C++ struct/union/class.
Definition: DeclCXX.h:258
CallExpr - Represents a function call (C99 6.5.2.2, C++ [expr.call]).
Definition: Expr.h:2872
unsigned getNumArgs() const
getNumArgs - Return the number of actual arguments to this call.
Definition: Expr.h:3050
Expr * getArg(unsigned Arg)
getArg - Return the specified argument.
Definition: Expr.h:3063
FunctionDecl * getDirectCallee()
If the callee is a FunctionDecl, return it. Otherwise return null.
Definition: Expr.h:3042
Declaration of a class template.
DeclContext * getParent()
getParent - Returns the containing DeclContext.
Definition: DeclBase.h:2066
Decl - This represents one declaration (or definition), e.g.
Definition: DeclBase.h:86
This represents one expression.
Definition: Expr.h:110
SourceLocation getExprLoc() const LLVM_READONLY
getExprLoc - Return the preferred location for the arrow when diagnosing a problem with a generic exp...
Definition: Expr.cpp:277
Represents a function declaration or definition.
Definition: Decl.h:1972
Represents a parameter to a function.
Definition: Decl.h:1762
Expr * getDefaultArg()
Definition: Decl.cpp:2972
A (possibly-)qualified type.
Definition: Type.h:940
A class that does preorder or postorder depth-first traversal on the entire Clang AST and visits each...
Encodes a location in the source.
bool isInSystemHeader(SourceLocation Loc) const
Returns if a SourceLocation is in a system header.
SourceLocation getBegin() const
SourceRange getSourceRange() const LLVM_READONLY
SourceLocation tokens are not useful in isolation - they are low level value objects created/interpre...
Definition: Stmt.cpp:326
The top declaration context.
Definition: Decl.h:84
An inter-procedural analysis facility that detects functions with "trivial" behavior with respect to ...
bool isTrivial(const Decl *D) const
CXXRecordDecl * getAsCXXRecordDecl() const
Retrieves the CXXRecordDecl that this type refers to, either because the type is a RecordType or beca...
Definition: Type.cpp:1881
BugReporter is a utility class for generating PathDiagnostics for analysis.
Definition: BugReporter.h:585
const SourceManager & getSourceManager()
Definition: BugReporter.h:623
virtual void emitReport(std::unique_ptr< BugReport > R)
Add the given report to the set of reports tracked by BugReporter.
CHECKER * registerChecker(AT &&... Args)
Used to register checkers.
RangeSelector name(std::string ID)
Given a node with a "name", (like NamedDecl, DeclRefExpr, CxxCtorInitializer, and TypeLoc) selects th...
The JSON file list parser is used to communicate input to InstallAPI.
std::optional< bool > isUncountedPtr(const Type *T)
bool isCtorOfRefCounted(const clang::FunctionDecl *F)
std::optional< bool > isUncounted(const CXXRecordDecl *Class)
void printQuotedQualifiedName(llvm::raw_ostream &Os, const NamedDeclDerivedT &D)
bool isASafeCallArg(const Expr *E)
For E referring to a ref-countable/-counted pointer/reference we return whether it's a safe call argu...
Definition: ASTUtils.cpp:115
bool isRefCounted(const CXXRecordDecl *R)
bool isRefType(const std::string &Name)
bool tryToFindPtrOrigin(const Expr *E, bool StopAtFirstRefCountedObj, std::function< bool(const clang::Expr *, bool)> callback)
This function de-facto defines a set of transformations that we consider safe (in heuristical sense).
Definition: ASTUtils.cpp:19
std::string safeGetName(const T *ASTNode)
Definition: ASTUtils.h:68