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: 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 415ffd83dbSDimitry 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 685ffd83dbSDimitry 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 885ffd83dbSDimitry 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 1225ffd83dbSDimitry 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 1515ffd83dbSDimitry Andric void ento::registerNoUncountedMemberChecker(CheckerManager &Mgr) { 1525ffd83dbSDimitry Andric Mgr.registerChecker<NoUncountedMemberChecker>(); 1535ffd83dbSDimitry Andric } 1545ffd83dbSDimitry Andric 1555ffd83dbSDimitry Andric bool ento::shouldRegisterNoUncountedMemberChecker( 1565ffd83dbSDimitry Andric const CheckerManager &Mgr) { 1575ffd83dbSDimitry Andric return true; 1585ffd83dbSDimitry Andric } 159