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