Coverage Report

Created: 2023-09-12 09:32

/Users/buildslave/jenkins/workspace/coverage/llvm-project/clang/lib/StaticAnalyzer/Checkers/WebKit/NoUncountedMembersChecker.cpp
Line
Count
Source (jump to first uncovered line)
1
//=======- NoUncountedMembersChecker.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/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
17
#include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h"
18
#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
19
#include "clang/StaticAnalyzer/Core/Checker.h"
20
#include "llvm/ADT/DenseSet.h"
21
#include "llvm/Support/Casting.h"
22
#include <optional>
23
24
using namespace clang;
25
using namespace ento;
26
27
namespace {
28
29
class NoUncountedMemberChecker
30
    : public Checker<check::ASTDecl<TranslationUnitDecl>> {
31
private:
32
  BugType Bug;
33
  mutable BugReporter *BR;
34
35
public:
36
  NoUncountedMemberChecker()
37
      : Bug(this,
38
            "Member variable is a raw-poiner/reference to reference-countable "
39
            "type",
40
2
            "WebKit coding guidelines") {}
41
42
  void checkASTDecl(const TranslationUnitDecl *TUD, AnalysisManager &MGR,
43
2
                    BugReporter &BRArg) const {
44
2
    BR = &BRArg;
45
46
    // The calls to checkAST* from AnalysisConsumer don't
47
    // visit template instantiations or lambda classes. We
48
    // want to visit those, so we make our own RecursiveASTVisitor.
49
2
    struct LocalVisitor : public RecursiveASTVisitor<LocalVisitor> {
50
2
      const NoUncountedMemberChecker *Checker;
51
2
      explicit LocalVisitor(const NoUncountedMemberChecker *Checker)
52
2
          : Checker(Checker) {
53
2
        assert(Checker);
54
2
      }
55
56
15
      bool shouldVisitTemplateInstantiations() const { return true; }
57
135
      bool shouldVisitImplicitCode() const { return false; }
58
59
13
      bool VisitRecordDecl(const RecordDecl *RD) {
60
13
        Checker->visitRecordDecl(RD);
61
13
        return true;
62
13
      }
63
2
    };
64
65
2
    LocalVisitor visitor(this);
66
2
    visitor.TraverseDecl(const_cast<TranslationUnitDecl *>(TUD));
67
2
  }
68
69
13
  void visitRecordDecl(const RecordDecl *RD) const {
70
13
    if (shouldSkipDecl(RD))
71
6
      return;
72
73
10
    
for (auto *Member : RD->fields())7
{
74
10
      const Type *MemberType = Member->getType().getTypePtrOrNull();
75
10
      if (!MemberType)
76
0
        continue;
77
78
10
      if (auto *MemberCXXRD = MemberType->getPointeeCXXRecordDecl()) {
79
        // If we don't see the definition we just don't know.
80
4
        if (MemberCXXRD->hasDefinition()) {
81
3
            std::optional<bool> isRCAble = isRefCountable(MemberCXXRD);
82
3
            if (isRCAble && *isRCAble)
83
3
                reportBug(Member, MemberType, MemberCXXRD, RD);
84
3
        }
85
4
      }
86
10
    }
87
7
  }
88
89
13
  bool shouldSkipDecl(const RecordDecl *RD) const {
90
13
    if (!RD->isThisDeclarationADefinition())
91
1
      return true;
92
93
12
    if (RD->isImplicit())
94
0
      return true;
95
96
12
    if (RD->isLambda())
97
0
      return true;
98
99
    // If the construct doesn't have a source file, then it's not something
100
    // we want to diagnose.
101
12
    const auto RDLocation = RD->getLocation();
102
12
    if (!RDLocation.isValid())
103
0
      return true;
104
105
12
    const auto Kind = RD->getTagKind();
106
    // FIMXE: Should we check union members too?
107
12
    if (Kind != TTK_Struct && 
Kind != TTK_Class4
)
108
3
      return true;
109
110
    // Ignore CXXRecords that come from system headers.
111
9
    if (BR->getSourceManager().isInSystemHeader(RDLocation))
112
0
      return true;
113
114
    // Ref-counted smartpointers actually have raw-pointer to uncounted type as
115
    // a member but we trust them to handle it correctly.
116
9
    auto CXXRD = llvm::dyn_cast_or_null<CXXRecordDecl>(RD);
117
9
    if (CXXRD)
118
9
      return isRefCounted(CXXRD);
119
120
0
    return false;
121
9
  }
122
123
  void reportBug(const FieldDecl *Member, const Type *MemberType,
124
                 const CXXRecordDecl *MemberCXXRD,
125
3
                 const RecordDecl *ClassCXXRD) const {
126
3
    assert(Member);
127
3
    assert(MemberType);
128
3
    assert(MemberCXXRD);
129
130
3
    SmallString<100> Buf;
131
3
    llvm::raw_svector_ostream Os(Buf);
132
133
3
    Os << "Member variable ";
134
3
    printQuotedName(Os, Member);
135
3
    Os << " in ";
136
3
    printQuotedQualifiedName(Os, ClassCXXRD);
137
3
    Os << " is a "
138
3
       << (isa<PointerType>(MemberType) ? 
"raw pointer"2
:
"reference"1
)
139
3
       << " to ref-countable type ";
140
3
    printQuotedQualifiedName(Os, MemberCXXRD);
141
3
    Os << "; member variables must be ref-counted.";
142
143
3
    PathDiagnosticLocation BSLoc(Member->getSourceRange().getBegin(),
144
3
                                 BR->getSourceManager());
145
3
    auto Report = std::make_unique<BasicBugReport>(Bug, Os.str(), BSLoc);
146
3
    Report->addRange(Member->getSourceRange());
147
3
    BR->emitReport(std::move(Report));
148
3
  }
149
};
150
} // namespace
151
152
2
void ento::registerNoUncountedMemberChecker(CheckerManager &Mgr) {
153
2
  Mgr.registerChecker<NoUncountedMemberChecker>();
154
2
}
155
156
bool ento::shouldRegisterNoUncountedMemberChecker(
157
4
    const CheckerManager &Mgr) {
158
4
  return true;
159
4
}