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