clang  15.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 "llvm/ADT/DenseSet.h"
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 
35 public:
36 
37  void checkASTDecl(const TranslationUnitDecl *TUD, AnalysisManager &MGR,
38  BugReporter &BRArg) const {
39  BR = &BRArg;
40 
41  // The calls to checkAST* from AnalysisConsumer don't
42  // visit template instantiations or lambda classes. We
43  // want to visit those, so we make our own RecursiveASTVisitor.
44  struct LocalVisitor : public RecursiveASTVisitor<LocalVisitor> {
45  const UncountedCallArgsChecker *Checker;
46  explicit LocalVisitor(const UncountedCallArgsChecker *Checker)
47  : Checker(Checker) {
48  assert(Checker);
49  }
50 
51  bool shouldVisitTemplateInstantiations() const { return true; }
52  bool shouldVisitImplicitCode() const { return false; }
53 
54  bool VisitCallExpr(const CallExpr *CE) {
55  Checker->visitCallExpr(CE);
56  return true;
57  }
58  };
59 
60  LocalVisitor visitor(this);
61  visitor.TraverseDecl(const_cast<TranslationUnitDecl *>(TUD));
62  }
63 
64  void visitCallExpr(const CallExpr *CE) const {
65  if (shouldSkipCall(CE))
66  return;
67 
68  if (auto *F = CE->getDirectCallee()) {
69  // Skip the first argument for overloaded member operators (e. g. lambda
70  // or std::function call operator).
71  unsigned ArgIdx = isa<CXXOperatorCallExpr>(CE) && isa_and_nonnull<CXXMethodDecl>(F);
72 
73  for (auto P = F->param_begin();
74  // FIXME: Also check variadic function parameters.
75  // FIXME: Also check default function arguments. Probably a different
76  // checker. In case there are default arguments the call can have
77  // fewer arguments than the callee has parameters.
78  P < F->param_end() && ArgIdx < CE->getNumArgs(); ++P, ++ArgIdx) {
79  // TODO: attributes.
80  // if ((*P)->hasAttr<SafeRefCntblRawPtrAttr>())
81  // continue;
82 
83  const auto *ArgType = (*P)->getType().getTypePtrOrNull();
84  if (!ArgType)
85  continue; // FIXME? Should we bail?
86 
87  // FIXME: more complex types (arrays, references to raw pointers, etc)
88  Optional<bool> IsUncounted = isUncountedPtr(ArgType);
89  if (!IsUncounted || !(*IsUncounted))
90  continue;
91 
92  const auto *Arg = CE->getArg(ArgIdx);
93 
94  std::pair<const clang::Expr *, bool> ArgOrigin =
95  tryToFindPtrOrigin(Arg, true);
96 
97  // Temporary ref-counted object created as part of the call argument
98  // would outlive the call.
99  if (ArgOrigin.second)
100  continue;
101 
102  if (isa<CXXNullPtrLiteralExpr>(ArgOrigin.first)) {
103  // foo(nullptr)
104  continue;
105  }
106  if (isa<IntegerLiteral>(ArgOrigin.first)) {
107  // FIXME: Check the value.
108  // foo(NULL)
109  continue;
110  }
111 
112  if (isASafeCallArg(ArgOrigin.first))
113  continue;
114 
115  reportBug(Arg, *P);
116  }
117  }
118  }
119 
120  bool shouldSkipCall(const CallExpr *CE) const {
121  if (CE->getNumArgs() == 0)
122  return false;
123 
124  // If an assignment is problematic we should warn about the sole existence
125  // of object on LHS.
126  if (auto *MemberOp = dyn_cast<CXXOperatorCallExpr>(CE)) {
127  // Note: assignemnt to built-in type isn't derived from CallExpr.
128  if (MemberOp->isAssignmentOp())
129  return false;
130  }
131 
132  const auto *Callee = CE->getDirectCallee();
133  if (!Callee)
134  return false;
135 
136  auto overloadedOperatorType = Callee->getOverloadedOperator();
137  if (overloadedOperatorType == OO_EqualEqual ||
138  overloadedOperatorType == OO_ExclaimEqual ||
139  overloadedOperatorType == OO_LessEqual ||
140  overloadedOperatorType == OO_GreaterEqual ||
141  overloadedOperatorType == OO_Spaceship ||
142  overloadedOperatorType == OO_AmpAmp ||
143  overloadedOperatorType == OO_PipePipe)
144  return true;
145 
146  if (isCtorOfRefCounted(Callee))
147  return true;
148 
149  auto name = safeGetName(Callee);
150  if (name == "adoptRef" || name == "getPtr" || name == "WeakPtr" ||
151  name == "makeWeakPtr" || name == "downcast" || name == "bitwise_cast" ||
152  name == "is" || name == "equal" || name == "hash" ||
153  name == "isType"
154  // FIXME: Most/all of these should be implemented via attributes.
155  || name == "equalIgnoringASCIICase" ||
156  name == "equalIgnoringASCIICaseCommon" ||
157  name == "equalIgnoringNullity")
158  return true;
159 
160  return false;
161  }
162 
163  void reportBug(const Expr *CallArg, const ParmVarDecl *Param) const {
164  assert(CallArg);
165 
166  SmallString<100> Buf;
167  llvm::raw_svector_ostream Os(Buf);
168 
169  const std::string paramName = safeGetName(Param);
170  Os << "Call argument";
171  if (!paramName.empty()) {
172  Os << " for parameter ";
173  printQuotedQualifiedName(Os, Param);
174  }
175  Os << " is uncounted and unsafe.";
176 
177  const SourceLocation SrcLocToReport =
178  isa<CXXDefaultArgExpr>(CallArg) ? Param->getDefaultArg()->getExprLoc()
179  : CallArg->getSourceRange().getBegin();
180 
181  PathDiagnosticLocation BSLoc(SrcLocToReport, BR->getSourceManager());
182  auto Report = std::make_unique<BasicBugReport>(Bug, Os.str(), BSLoc);
183  Report->addRange(CallArg->getSourceRange());
184  BR->emitReport(std::move(Report));
185  }
186 };
187 } // namespace
188 
189 void ento::registerUncountedCallArgsChecker(CheckerManager &Mgr) {
190  Mgr.registerChecker<UncountedCallArgsChecker>();
191 }
192 
193 bool ento::shouldRegisterUncountedCallArgsChecker(const CheckerManager &) {
194  return true;
195 }
string
string(SUBSTRING ${CMAKE_CURRENT_BINARY_DIR} 0 ${PATH_LIB_START} PATH_HEAD) string(SUBSTRING $
Definition: CMakeLists.txt:22
clang::SourceLocation
Encodes a location in the source.
Definition: SourceLocation.h:86
clang::SourceRange::getBegin
SourceLocation getBegin() const
Definition: SourceLocation.h:219
CXXInheritance.h
clang::Stmt::getSourceRange
SourceRange getSourceRange() const LLVM_READONLY
SourceLocation tokens are not useful in isolation - they are low level value objects created/interpre...
Definition: Stmt.cpp:324
clang::isUncountedPtr
llvm::Optional< bool > isUncountedPtr(const Type *T)
Definition: PtrTypesSemantics.cpp:127
clang::ParmVarDecl
Represents a parameter to a function.
Definition: Decl.h:1680
clang::ParmVarDecl::getDefaultArg
Expr * getDefaultArg()
Definition: Decl.cpp:2864
DeclCXX.h
llvm::Optional< bool >
clang::tryToFindPtrOrigin
std::pair< const Expr *, bool > tryToFindPtrOrigin(const Expr *E, bool StopAtFirstRefCountedObj)
This function de-facto defines a set of transformations that we consider safe (in heuristical sense).
Definition: ASTUtils.cpp:20
clang::isASafeCallArg
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:82
Decl.h
clang::TranslationUnitDecl
The top declaration context.
Definition: Decl.h:80
clang::CallExpr::getDirectCallee
FunctionDecl * getDirectCallee()
If the callee is a FunctionDecl, return it. Otherwise return null.
Definition: Expr.h:2971
BuiltinCheckerRegistration.h
clang::isCtorOfRefCounted
bool isCtorOfRefCounted(const clang::FunctionDecl *F)
Definition: PtrTypesSemantics.cpp:98
clang::syntax::NodeRole::Callee
@ Callee
DiagOutputUtils.h
BugReporter.h
clang::RecursiveASTVisitor
A class that does preorder or postorder depth-first traversal on the entire Clang AST and visits each...
Definition: RecursiveASTVisitor.h:165
clang::CallExpr::getArg
Expr * getArg(unsigned Arg)
getArg - Return the specified argument.
Definition: Expr.h:2992
clang::safeGetName
std::string safeGetName(const T *ASTNode)
Definition: ASTUtils.h:65
llvm::SmallString
Definition: LLVM.h:37
PtrTypesSemantics.h
SourceLocation.h
P
StringRef P
Definition: ASTMatchersInternal.cpp:563
BugType.h
clang::CallExpr::getNumArgs
unsigned getNumArgs() const
getNumArgs - Return the number of actual arguments to this call.
Definition: Expr.h:2979
clang::printQuotedQualifiedName
void printQuotedQualifiedName(llvm::raw_ostream &Os, const NamedDeclDerivedT &D)
Definition: DiagOutputUtils.h:18
clang::Expr::getExprLoc
SourceLocation getExprLoc() const LLVM_READONLY
getExprLoc - Return the preferred location for the arrow when diagnosing a problem with a generic exp...
Definition: Expr.cpp:247
Checker.h
ASTUtils.h
clang
Definition: CalledOnceCheck.h:17
RecursiveASTVisitor.h
clang::Expr
This represents one expression.
Definition: Expr.h:109
clang::transformer::name
RangeSelector name(std::string ID)
Given a node with a "name", (like NamedDecl, DeclRefExpr, CxxCtorInitializer, and TypeLoc) selects th...
Definition: RangeSelector.cpp:200
clang::CallExpr
CallExpr - Represents a function call (C99 6.5.2.2, C++ [expr.call]).
Definition: Expr.h:2801