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