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 23 using namespace clang; 24 using namespace ento; 25 26 namespace { 27 28 class NoUncountedMemberChecker 29 : public Checker<check::ASTDecl<TranslationUnitDecl>> { 30 private: 31 BugType Bug; 32 mutable BugReporter *BR; 33 34 public: 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 (isRefCountable(MemberCXXRD)) 79 reportBug(Member, MemberType, MemberCXXRD, RD); 80 } 81 } 82 } 83 84 bool shouldSkipDecl(const RecordDecl *RD) const { 85 if (!RD->isThisDeclarationADefinition()) 86 return true; 87 88 if (RD->isImplicit()) 89 return true; 90 91 if (RD->isLambda()) 92 return true; 93 94 // If the construct doesn't have a source file, then it's not something 95 // we want to diagnose. 96 const auto RDLocation = RD->getLocation(); 97 if (!RDLocation.isValid()) 98 return true; 99 100 const auto Kind = RD->getTagKind(); 101 // FIMXE: Should we check union members too? 102 if (Kind != TTK_Struct && Kind != TTK_Class) 103 return true; 104 105 // Ignore CXXRecords that come from system headers. 106 if (BR->getSourceManager().isInSystemHeader(RDLocation)) 107 return true; 108 109 // Ref-counted smartpointers actually have raw-pointer to uncounted type as 110 // a member but we trust them to handle it correctly. 111 return isRefCounted(llvm::dyn_cast_or_null<CXXRecordDecl>(RD)); 112 } 113 114 void reportBug(const FieldDecl *Member, const Type *MemberType, 115 const CXXRecordDecl *MemberCXXRD, 116 const RecordDecl *ClassCXXRD) const { 117 assert(Member); 118 assert(MemberType); 119 assert(MemberCXXRD); 120 121 SmallString<100> Buf; 122 llvm::raw_svector_ostream Os(Buf); 123 124 Os << "Member variable "; 125 printQuotedName(Os, Member); 126 Os << " in "; 127 printQuotedQualifiedName(Os, ClassCXXRD); 128 Os << " is a " 129 << (isa<PointerType>(MemberType) ? "raw pointer" : "reference") 130 << " to ref-countable type "; 131 printQuotedQualifiedName(Os, MemberCXXRD); 132 Os << "; member variables must be ref-counted."; 133 134 PathDiagnosticLocation BSLoc(Member->getSourceRange().getBegin(), 135 BR->getSourceManager()); 136 auto Report = std::make_unique<BasicBugReport>(Bug, Os.str(), BSLoc); 137 Report->addRange(Member->getSourceRange()); 138 BR->emitReport(std::move(Report)); 139 } 140 }; 141 } // namespace 142 143 void ento::registerWebKitNoUncountedMemberChecker(CheckerManager &Mgr) { 144 Mgr.registerChecker<NoUncountedMemberChecker>(); 145 } 146 147 bool ento::shouldRegisterWebKitNoUncountedMemberChecker( 148 const CheckerManager &Mgr) { 149 return true; 150 } 151