15ffd83dbSDimitry Andric //=======- NoUncountedMembersChecker.cpp -------------------------*- C++ -*-==//
25ffd83dbSDimitry Andric //
35ffd83dbSDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
45ffd83dbSDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
55ffd83dbSDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
65ffd83dbSDimitry Andric //
75ffd83dbSDimitry Andric //===----------------------------------------------------------------------===//
85ffd83dbSDimitry Andric
95ffd83dbSDimitry Andric #include "ASTUtils.h"
105ffd83dbSDimitry Andric #include "DiagOutputUtils.h"
115ffd83dbSDimitry Andric #include "PtrTypesSemantics.h"
125ffd83dbSDimitry Andric #include "clang/AST/CXXInheritance.h"
135ffd83dbSDimitry Andric #include "clang/AST/Decl.h"
145ffd83dbSDimitry Andric #include "clang/AST/DeclCXX.h"
155ffd83dbSDimitry Andric #include "clang/AST/RecursiveASTVisitor.h"
165ffd83dbSDimitry Andric #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
175ffd83dbSDimitry Andric #include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h"
185ffd83dbSDimitry Andric #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
195ffd83dbSDimitry Andric #include "clang/StaticAnalyzer/Core/Checker.h"
205ffd83dbSDimitry Andric #include "llvm/Support/Casting.h"
21bdd1243dSDimitry Andric #include <optional>
225ffd83dbSDimitry Andric
235ffd83dbSDimitry Andric using namespace clang;
245ffd83dbSDimitry Andric using namespace ento;
255ffd83dbSDimitry Andric
265ffd83dbSDimitry Andric namespace {
275ffd83dbSDimitry Andric
285ffd83dbSDimitry Andric class NoUncountedMemberChecker
295ffd83dbSDimitry Andric : public Checker<check::ASTDecl<TranslationUnitDecl>> {
305ffd83dbSDimitry Andric private:
315ffd83dbSDimitry Andric BugType Bug;
325ffd83dbSDimitry Andric mutable BugReporter *BR;
335ffd83dbSDimitry Andric
345ffd83dbSDimitry Andric public:
NoUncountedMemberChecker()355ffd83dbSDimitry Andric NoUncountedMemberChecker()
365ffd83dbSDimitry Andric : Bug(this,
37*0fca6ea1SDimitry Andric "Member variable is a raw-pointer/reference to reference-countable "
385ffd83dbSDimitry Andric "type",
395ffd83dbSDimitry Andric "WebKit coding guidelines") {}
405ffd83dbSDimitry Andric
checkASTDecl(const TranslationUnitDecl * TUD,AnalysisManager & MGR,BugReporter & BRArg) const415ffd83dbSDimitry Andric void checkASTDecl(const TranslationUnitDecl *TUD, AnalysisManager &MGR,
425ffd83dbSDimitry Andric BugReporter &BRArg) const {
435ffd83dbSDimitry Andric BR = &BRArg;
445ffd83dbSDimitry Andric
455ffd83dbSDimitry Andric // The calls to checkAST* from AnalysisConsumer don't
465ffd83dbSDimitry Andric // visit template instantiations or lambda classes. We
475ffd83dbSDimitry Andric // want to visit those, so we make our own RecursiveASTVisitor.
485ffd83dbSDimitry Andric struct LocalVisitor : public RecursiveASTVisitor<LocalVisitor> {
495ffd83dbSDimitry Andric const NoUncountedMemberChecker *Checker;
505ffd83dbSDimitry Andric explicit LocalVisitor(const NoUncountedMemberChecker *Checker)
515ffd83dbSDimitry Andric : Checker(Checker) {
525ffd83dbSDimitry Andric assert(Checker);
535ffd83dbSDimitry Andric }
545ffd83dbSDimitry Andric
555ffd83dbSDimitry Andric bool shouldVisitTemplateInstantiations() const { return true; }
565ffd83dbSDimitry Andric bool shouldVisitImplicitCode() const { return false; }
575ffd83dbSDimitry Andric
585ffd83dbSDimitry Andric bool VisitRecordDecl(const RecordDecl *RD) {
595ffd83dbSDimitry Andric Checker->visitRecordDecl(RD);
605ffd83dbSDimitry Andric return true;
615ffd83dbSDimitry Andric }
625ffd83dbSDimitry Andric };
635ffd83dbSDimitry Andric
645ffd83dbSDimitry Andric LocalVisitor visitor(this);
655ffd83dbSDimitry Andric visitor.TraverseDecl(const_cast<TranslationUnitDecl *>(TUD));
665ffd83dbSDimitry Andric }
675ffd83dbSDimitry Andric
visitRecordDecl(const RecordDecl * RD) const685ffd83dbSDimitry Andric void visitRecordDecl(const RecordDecl *RD) const {
695ffd83dbSDimitry Andric if (shouldSkipDecl(RD))
705ffd83dbSDimitry Andric return;
715ffd83dbSDimitry Andric
72bdd1243dSDimitry Andric for (auto *Member : RD->fields()) {
735ffd83dbSDimitry Andric const Type *MemberType = Member->getType().getTypePtrOrNull();
745ffd83dbSDimitry Andric if (!MemberType)
755ffd83dbSDimitry Andric continue;
765ffd83dbSDimitry Andric
775ffd83dbSDimitry Andric if (auto *MemberCXXRD = MemberType->getPointeeCXXRecordDecl()) {
785ffd83dbSDimitry Andric // If we don't see the definition we just don't know.
79e8d8bef9SDimitry Andric if (MemberCXXRD->hasDefinition()) {
80bdd1243dSDimitry Andric std::optional<bool> isRCAble = isRefCountable(MemberCXXRD);
81e8d8bef9SDimitry Andric if (isRCAble && *isRCAble)
825ffd83dbSDimitry Andric reportBug(Member, MemberType, MemberCXXRD, RD);
835ffd83dbSDimitry Andric }
845ffd83dbSDimitry Andric }
855ffd83dbSDimitry Andric }
86e8d8bef9SDimitry Andric }
875ffd83dbSDimitry Andric
shouldSkipDecl(const RecordDecl * RD) const885ffd83dbSDimitry Andric bool shouldSkipDecl(const RecordDecl *RD) const {
895ffd83dbSDimitry Andric if (!RD->isThisDeclarationADefinition())
905ffd83dbSDimitry Andric return true;
915ffd83dbSDimitry Andric
925ffd83dbSDimitry Andric if (RD->isImplicit())
935ffd83dbSDimitry Andric return true;
945ffd83dbSDimitry Andric
955ffd83dbSDimitry Andric if (RD->isLambda())
965ffd83dbSDimitry Andric return true;
975ffd83dbSDimitry Andric
985ffd83dbSDimitry Andric // If the construct doesn't have a source file, then it's not something
995ffd83dbSDimitry Andric // we want to diagnose.
1005ffd83dbSDimitry Andric const auto RDLocation = RD->getLocation();
1015ffd83dbSDimitry Andric if (!RDLocation.isValid())
1025ffd83dbSDimitry Andric return true;
1035ffd83dbSDimitry Andric
1045ffd83dbSDimitry Andric const auto Kind = RD->getTagKind();
1055ffd83dbSDimitry Andric // FIMXE: Should we check union members too?
1065f757f3fSDimitry Andric if (Kind != TagTypeKind::Struct && Kind != TagTypeKind::Class)
1075ffd83dbSDimitry Andric return true;
1085ffd83dbSDimitry Andric
1095ffd83dbSDimitry Andric // Ignore CXXRecords that come from system headers.
1105ffd83dbSDimitry Andric if (BR->getSourceManager().isInSystemHeader(RDLocation))
1115ffd83dbSDimitry Andric return true;
1125ffd83dbSDimitry Andric
1135ffd83dbSDimitry Andric // Ref-counted smartpointers actually have raw-pointer to uncounted type as
1145ffd83dbSDimitry Andric // a member but we trust them to handle it correctly.
1155ffd83dbSDimitry Andric auto CXXRD = llvm::dyn_cast_or_null<CXXRecordDecl>(RD);
1165ffd83dbSDimitry Andric if (CXXRD)
1175ffd83dbSDimitry Andric return isRefCounted(CXXRD);
1185ffd83dbSDimitry Andric
1195ffd83dbSDimitry Andric return false;
1205ffd83dbSDimitry Andric }
1215ffd83dbSDimitry Andric
reportBug(const FieldDecl * Member,const Type * MemberType,const CXXRecordDecl * MemberCXXRD,const RecordDecl * ClassCXXRD) const1225ffd83dbSDimitry Andric void reportBug(const FieldDecl *Member, const Type *MemberType,
1235ffd83dbSDimitry Andric const CXXRecordDecl *MemberCXXRD,
1245ffd83dbSDimitry Andric const RecordDecl *ClassCXXRD) const {
1255ffd83dbSDimitry Andric assert(Member);
1265ffd83dbSDimitry Andric assert(MemberType);
1275ffd83dbSDimitry Andric assert(MemberCXXRD);
1285ffd83dbSDimitry Andric
1295ffd83dbSDimitry Andric SmallString<100> Buf;
1305ffd83dbSDimitry Andric llvm::raw_svector_ostream Os(Buf);
1315ffd83dbSDimitry Andric
1325ffd83dbSDimitry Andric Os << "Member variable ";
1335ffd83dbSDimitry Andric printQuotedName(Os, Member);
1345ffd83dbSDimitry Andric Os << " in ";
1355ffd83dbSDimitry Andric printQuotedQualifiedName(Os, ClassCXXRD);
1365ffd83dbSDimitry Andric Os << " is a "
1375ffd83dbSDimitry Andric << (isa<PointerType>(MemberType) ? "raw pointer" : "reference")
1385ffd83dbSDimitry Andric << " to ref-countable type ";
1395ffd83dbSDimitry Andric printQuotedQualifiedName(Os, MemberCXXRD);
1405ffd83dbSDimitry Andric Os << "; member variables must be ref-counted.";
1415ffd83dbSDimitry Andric
1425ffd83dbSDimitry Andric PathDiagnosticLocation BSLoc(Member->getSourceRange().getBegin(),
1435ffd83dbSDimitry Andric BR->getSourceManager());
1445ffd83dbSDimitry Andric auto Report = std::make_unique<BasicBugReport>(Bug, Os.str(), BSLoc);
1455ffd83dbSDimitry Andric Report->addRange(Member->getSourceRange());
1465ffd83dbSDimitry Andric BR->emitReport(std::move(Report));
1475ffd83dbSDimitry Andric }
1485ffd83dbSDimitry Andric };
1495ffd83dbSDimitry Andric } // namespace
1505ffd83dbSDimitry Andric
registerNoUncountedMemberChecker(CheckerManager & Mgr)1515ffd83dbSDimitry Andric void ento::registerNoUncountedMemberChecker(CheckerManager &Mgr) {
1525ffd83dbSDimitry Andric Mgr.registerChecker<NoUncountedMemberChecker>();
1535ffd83dbSDimitry Andric }
1545ffd83dbSDimitry Andric
shouldRegisterNoUncountedMemberChecker(const CheckerManager & Mgr)1555ffd83dbSDimitry Andric bool ento::shouldRegisterNoUncountedMemberChecker(
1565ffd83dbSDimitry Andric const CheckerManager &Mgr) {
1575ffd83dbSDimitry Andric return true;
1585ffd83dbSDimitry Andric }
159