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/Support/Casting.h" 21 #include <optional> 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 we don't see the definition we just don't know. 79 if (MemberCXXRD->hasDefinition()) { 80 std::optional<bool> isRCAble = isRefCountable(MemberCXXRD); 81 if (isRCAble && *isRCAble) 82 reportBug(Member, MemberType, MemberCXXRD, RD); 83 } 84 } 85 } 86 } 87 88 bool shouldSkipDecl(const RecordDecl *RD) const { 89 if (!RD->isThisDeclarationADefinition()) 90 return true; 91 92 if (RD->isImplicit()) 93 return true; 94 95 if (RD->isLambda()) 96 return true; 97 98 // If the construct doesn't have a source file, then it's not something 99 // we want to diagnose. 100 const auto RDLocation = RD->getLocation(); 101 if (!RDLocation.isValid()) 102 return true; 103 104 const auto Kind = RD->getTagKind(); 105 // FIMXE: Should we check union members too? 106 if (Kind != TagTypeKind::Struct && Kind != TagTypeKind::Class) 107 return true; 108 109 // Ignore CXXRecords that come from system headers. 110 if (BR->getSourceManager().isInSystemHeader(RDLocation)) 111 return true; 112 113 // Ref-counted smartpointers actually have raw-pointer to uncounted type as 114 // a member but we trust them to handle it correctly. 115 auto CXXRD = llvm::dyn_cast_or_null<CXXRecordDecl>(RD); 116 if (CXXRD) 117 return isRefCounted(CXXRD); 118 119 return false; 120 } 121 122 void reportBug(const FieldDecl *Member, const Type *MemberType, 123 const CXXRecordDecl *MemberCXXRD, 124 const RecordDecl *ClassCXXRD) const { 125 assert(Member); 126 assert(MemberType); 127 assert(MemberCXXRD); 128 129 SmallString<100> Buf; 130 llvm::raw_svector_ostream Os(Buf); 131 132 Os << "Member variable "; 133 printQuotedName(Os, Member); 134 Os << " in "; 135 printQuotedQualifiedName(Os, ClassCXXRD); 136 Os << " is a " 137 << (isa<PointerType>(MemberType) ? "raw pointer" : "reference") 138 << " to ref-countable type "; 139 printQuotedQualifiedName(Os, MemberCXXRD); 140 Os << "; member variables must be ref-counted."; 141 142 PathDiagnosticLocation BSLoc(Member->getSourceRange().getBegin(), 143 BR->getSourceManager()); 144 auto Report = std::make_unique<BasicBugReport>(Bug, Os.str(), BSLoc); 145 Report->addRange(Member->getSourceRange()); 146 BR->emitReport(std::move(Report)); 147 } 148 }; 149 } // namespace 150 151 void ento::registerNoUncountedMemberChecker(CheckerManager &Mgr) { 152 Mgr.registerChecker<NoUncountedMemberChecker>(); 153 } 154 155 bool ento::shouldRegisterNoUncountedMemberChecker( 156 const CheckerManager &Mgr) { 157 return true; 158 } 159