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