Coverage Report

Created: 2023-09-30 09:22

/Users/buildslave/jenkins/workspace/coverage/llvm-project/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedCallArgsChecker.cpp
Line
Count
Source (jump to first uncovered line)
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"
12
#include "clang/AST/CXXInheritance.h"
13
#include "clang/AST/Decl.h"
14
#include "clang/AST/DeclCXX.h"
15
#include "clang/AST/RecursiveASTVisitor.h"
16
#include "clang/Basic/SourceLocation.h"
17
#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
18
#include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h"
19
#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
20
#include "clang/StaticAnalyzer/Core/Checker.h"
21
#include "llvm/ADT/DenseSet.h"
22
#include <optional>
23
24
using namespace clang;
25
using namespace ento;
26
27
namespace {
28
29
class UncountedCallArgsChecker
30
    : public Checker<check::ASTDecl<TranslationUnitDecl>> {
31
  BugType Bug{this,
32
            "Uncounted call argument for a raw pointer/reference parameter",
33
            "WebKit coding guidelines"};
34
  mutable BugReporter *BR;
35
36
public:
37
38
  void checkASTDecl(const TranslationUnitDecl *TUD, AnalysisManager &MGR,
39
1
                    BugReporter &BRArg) const {
40
1
    BR = &BRArg;
41
42
    // The calls to checkAST* from AnalysisConsumer don't
43
    // visit template instantiations or lambda classes. We
44
    // want to visit those, so we make our own RecursiveASTVisitor.
45
1
    struct LocalVisitor : public RecursiveASTVisitor<LocalVisitor> {
46
1
      const UncountedCallArgsChecker *Checker;
47
1
      explicit LocalVisitor(const UncountedCallArgsChecker *Checker)
48
1
          : Checker(Checker) {
49
1
        assert(Checker);
50
1
      }
51
52
16
      bool shouldVisitTemplateInstantiations() const { return true; }
53
347
      bool shouldVisitImplicitCode() const { return false; }
54
55
82
      bool VisitCallExpr(const CallExpr *CE) {
56
82
        Checker->visitCallExpr(CE);
57
82
        return true;
58
82
      }
59
1
    };
60
61
1
    LocalVisitor visitor(this);
62
1
    visitor.TraverseDecl(const_cast<TranslationUnitDecl *>(TUD));
63
1
  }
64
65
82
  void visitCallExpr(const CallExpr *CE) const {
66
82
    if (shouldSkipCall(CE))
67
13
      return;
68
69
69
    if (auto *F = CE->getDirectCallee()) {
70
      // Skip the first argument for overloaded member operators (e. g. lambda
71
      // or std::function call operator).
72
69
      unsigned ArgIdx = isa<CXXOperatorCallExpr>(CE) && 
isa_and_nonnull<CXXMethodDecl>(F)9
;
73
74
69
      for (auto P = F->param_begin();
75
           // FIXME: Also check variadic function parameters.
76
           // FIXME: Also check default function arguments. Probably a different
77
           // checker. In case there are default arguments the call can have
78
           // fewer arguments than the callee has parameters.
79
115
           P < F->param_end() && 
ArgIdx < CE->getNumArgs()46
;
++P, ++ArgIdx46
) {
80
        // TODO: attributes.
81
        // if ((*P)->hasAttr<SafeRefCntblRawPtrAttr>())
82
        //  continue;
83
84
46
        const auto *ArgType = (*P)->getType().getTypePtrOrNull();
85
46
        if (!ArgType)
86
0
          continue; // FIXME? Should we bail?
87
88
        // FIXME: more complex types (arrays, references to raw pointers, etc)
89
46
        std::optional<bool> IsUncounted = isUncountedPtr(ArgType);
90
46
        if (!IsUncounted || !(*IsUncounted))
91
3
          continue;
92
93
43
        const auto *Arg = CE->getArg(ArgIdx);
94
95
43
        std::pair<const clang::Expr *, bool> ArgOrigin =
96
43
            tryToFindPtrOrigin(Arg, true);
97
98
        // Temporary ref-counted object created as part of the call argument
99
        // would outlive the call.
100
43
        if (ArgOrigin.second)
101
5
          continue;
102
103
38
        if (isa<CXXNullPtrLiteralExpr>(ArgOrigin.first)) {
104
          // foo(nullptr)
105
1
          continue;
106
1
        }
107
37
        if (isa<IntegerLiteral>(ArgOrigin.first)) {
108
          // FIXME: Check the value.
109
          // foo(NULL)
110
1
          continue;
111
1
        }
112
113
36
        if (isASafeCallArg(ArgOrigin.first))
114
18
          continue;
115
116
18
        reportBug(Arg, *P);
117
18
      }
118
69
    }
119
69
  }
120
121
82
  bool shouldSkipCall(const CallExpr *CE) const {
122
82
    if (CE->getNumArgs() == 0)
123
26
      return false;
124
125
    // If an assignment is problematic we should warn about the sole existence
126
    // of object on LHS.
127
56
    if (auto *MemberOp = dyn_cast<CXXOperatorCallExpr>(CE)) {
128
      // Note: assignemnt to built-in type isn't derived from CallExpr.
129
9
      if (MemberOp->isAssignmentOp())
130
0
        return false;
131
9
    }
132
133
56
    const auto *Callee = CE->getDirectCallee();
134
56
    if (!Callee)
135
0
      return false;
136
137
56
    auto overloadedOperatorType = Callee->getOverloadedOperator();
138
56
    if (overloadedOperatorType == OO_EqualEqual ||
139
56
        overloadedOperatorType == OO_ExclaimEqual ||
140
56
        overloadedOperatorType == OO_LessEqual ||
141
56
        overloadedOperatorType == OO_GreaterEqual ||
142
56
        overloadedOperatorType == OO_Spaceship ||
143
56
        overloadedOperatorType == OO_AmpAmp ||
144
56
        overloadedOperatorType == OO_PipePipe)
145
0
      return true;
146
147
56
    if (isCtorOfRefCounted(Callee))
148
2
      return true;
149
150
54
    auto name = safeGetName(Callee);
151
54
    if (name == "adoptRef" || name == "getPtr" || name == "WeakPtr" ||
152
54
        name == "makeWeakPtr" || 
name == "downcast"52
||
name == "bitwise_cast"46
||
153
54
        
name == "is"43
||
name == "equal"43
||
name == "hash"43
||
154
54
        
name == "isType"43
155
        // FIXME: Most/all of these should be implemented via attributes.
156
54
        || 
name == "equalIgnoringASCIICase"43
||
157
54
        
name == "equalIgnoringASCIICaseCommon"43
||
158
54
        
name == "equalIgnoringNullity"43
)
159
11
      return true;
160
161
43
    return false;
162
54
  }
163
164
18
  void reportBug(const Expr *CallArg, const ParmVarDecl *Param) const {
165
18
    assert(CallArg);
166
167
18
    SmallString<100> Buf;
168
18
    llvm::raw_svector_ostream Os(Buf);
169
170
18
    const std::string paramName = safeGetName(Param);
171
18
    Os << "Call argument";
172
18
    if (!paramName.empty()) {
173
7
      Os << " for parameter ";
174
7
      printQuotedQualifiedName(Os, Param);
175
7
    }
176
18
    Os << " is uncounted and unsafe.";
177
178
18
    const SourceLocation SrcLocToReport =
179
18
        isa<CXXDefaultArgExpr>(CallArg) ? 
Param->getDefaultArg()->getExprLoc()1
180
18
                                        : 
CallArg->getSourceRange().getBegin()17
;
181
182
18
    PathDiagnosticLocation BSLoc(SrcLocToReport, BR->getSourceManager());
183
18
    auto Report = std::make_unique<BasicBugReport>(Bug, Os.str(), BSLoc);
184
18
    Report->addRange(CallArg->getSourceRange());
185
18
    BR->emitReport(std::move(Report));
186
18
  }
187
};
188
} // namespace
189
190
1
void ento::registerUncountedCallArgsChecker(CheckerManager &Mgr) {
191
1
  Mgr.registerChecker<UncountedCallArgsChecker>();
192
1
}
193
194
2
bool ento::shouldRegisterUncountedCallArgsChecker(const CheckerManager &) {
195
2
  return true;
196
2
}