15ffd83dbSDimitry Andric //=======- NoUncountedMembersChecker.cpp -------------------------*- C++ -*-==//
25ffd83dbSDimitry Andric //
35ffd83dbSDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
45ffd83dbSDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
55ffd83dbSDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
65ffd83dbSDimitry Andric //
75ffd83dbSDimitry Andric //===----------------------------------------------------------------------===//
85ffd83dbSDimitry Andric 
95ffd83dbSDimitry Andric #include "ASTUtils.h"
105ffd83dbSDimitry Andric #include "DiagOutputUtils.h"
115ffd83dbSDimitry Andric #include "PtrTypesSemantics.h"
125ffd83dbSDimitry Andric #include "clang/AST/CXXInheritance.h"
135ffd83dbSDimitry Andric #include "clang/AST/Decl.h"
145ffd83dbSDimitry Andric #include "clang/AST/DeclCXX.h"
155ffd83dbSDimitry Andric #include "clang/AST/RecursiveASTVisitor.h"
165ffd83dbSDimitry Andric #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
175ffd83dbSDimitry Andric #include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h"
185ffd83dbSDimitry Andric #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
195ffd83dbSDimitry Andric #include "clang/StaticAnalyzer/Core/Checker.h"
205ffd83dbSDimitry Andric #include "llvm/Support/Casting.h"
21bdd1243dSDimitry Andric #include <optional>
225ffd83dbSDimitry Andric 
235ffd83dbSDimitry Andric using namespace clang;
245ffd83dbSDimitry Andric using namespace ento;
255ffd83dbSDimitry Andric 
265ffd83dbSDimitry Andric namespace {
275ffd83dbSDimitry Andric 
285ffd83dbSDimitry Andric class NoUncountedMemberChecker
295ffd83dbSDimitry Andric     : public Checker<check::ASTDecl<TranslationUnitDecl>> {
305ffd83dbSDimitry Andric private:
315ffd83dbSDimitry Andric   BugType Bug;
325ffd83dbSDimitry Andric   mutable BugReporter *BR;
335ffd83dbSDimitry Andric 
345ffd83dbSDimitry Andric public:
355ffd83dbSDimitry Andric   NoUncountedMemberChecker()
365ffd83dbSDimitry Andric       : Bug(this,
37*0fca6ea1SDimitry Andric             "Member variable is a raw-pointer/reference to reference-countable "
385ffd83dbSDimitry Andric             "type",
395ffd83dbSDimitry Andric             "WebKit coding guidelines") {}
405ffd83dbSDimitry Andric 
415ffd83dbSDimitry Andric   void checkASTDecl(const TranslationUnitDecl *TUD, AnalysisManager &MGR,
425ffd83dbSDimitry Andric                     BugReporter &BRArg) const {
435ffd83dbSDimitry Andric     BR = &BRArg;
445ffd83dbSDimitry Andric 
455ffd83dbSDimitry Andric     // The calls to checkAST* from AnalysisConsumer don't
465ffd83dbSDimitry Andric     // visit template instantiations or lambda classes. We
475ffd83dbSDimitry Andric     // want to visit those, so we make our own RecursiveASTVisitor.
485ffd83dbSDimitry Andric     struct LocalVisitor : public RecursiveASTVisitor<LocalVisitor> {
495ffd83dbSDimitry Andric       const NoUncountedMemberChecker *Checker;
505ffd83dbSDimitry Andric       explicit LocalVisitor(const NoUncountedMemberChecker *Checker)
515ffd83dbSDimitry Andric           : Checker(Checker) {
525ffd83dbSDimitry Andric         assert(Checker);
535ffd83dbSDimitry Andric       }
545ffd83dbSDimitry Andric 
555ffd83dbSDimitry Andric       bool shouldVisitTemplateInstantiations() const { return true; }
565ffd83dbSDimitry Andric       bool shouldVisitImplicitCode() const { return false; }
575ffd83dbSDimitry Andric 
585ffd83dbSDimitry Andric       bool VisitRecordDecl(const RecordDecl *RD) {
595ffd83dbSDimitry Andric         Checker->visitRecordDecl(RD);
605ffd83dbSDimitry Andric         return true;
615ffd83dbSDimitry Andric       }
625ffd83dbSDimitry Andric     };
635ffd83dbSDimitry Andric 
645ffd83dbSDimitry Andric     LocalVisitor visitor(this);
655ffd83dbSDimitry Andric     visitor.TraverseDecl(const_cast<TranslationUnitDecl *>(TUD));
665ffd83dbSDimitry Andric   }
675ffd83dbSDimitry Andric 
685ffd83dbSDimitry Andric   void visitRecordDecl(const RecordDecl *RD) const {
695ffd83dbSDimitry Andric     if (shouldSkipDecl(RD))
705ffd83dbSDimitry Andric       return;
715ffd83dbSDimitry Andric 
72bdd1243dSDimitry Andric     for (auto *Member : RD->fields()) {
735ffd83dbSDimitry Andric       const Type *MemberType = Member->getType().getTypePtrOrNull();
745ffd83dbSDimitry Andric       if (!MemberType)
755ffd83dbSDimitry Andric         continue;
765ffd83dbSDimitry Andric 
775ffd83dbSDimitry Andric       if (auto *MemberCXXRD = MemberType->getPointeeCXXRecordDecl()) {
785ffd83dbSDimitry Andric         // If we don't see the definition we just don't know.
79e8d8bef9SDimitry Andric         if (MemberCXXRD->hasDefinition()) {
80bdd1243dSDimitry Andric             std::optional<bool> isRCAble = isRefCountable(MemberCXXRD);
81e8d8bef9SDimitry Andric             if (isRCAble && *isRCAble)
825ffd83dbSDimitry Andric                 reportBug(Member, MemberType, MemberCXXRD, RD);
835ffd83dbSDimitry Andric         }
845ffd83dbSDimitry Andric       }
855ffd83dbSDimitry Andric     }
86e8d8bef9SDimitry Andric   }
875ffd83dbSDimitry Andric 
885ffd83dbSDimitry Andric   bool shouldSkipDecl(const RecordDecl *RD) const {
895ffd83dbSDimitry Andric     if (!RD->isThisDeclarationADefinition())
905ffd83dbSDimitry Andric       return true;
915ffd83dbSDimitry Andric 
925ffd83dbSDimitry Andric     if (RD->isImplicit())
935ffd83dbSDimitry Andric       return true;
945ffd83dbSDimitry Andric 
955ffd83dbSDimitry Andric     if (RD->isLambda())
965ffd83dbSDimitry Andric       return true;
975ffd83dbSDimitry Andric 
985ffd83dbSDimitry Andric     // If the construct doesn't have a source file, then it's not something
995ffd83dbSDimitry Andric     // we want to diagnose.
1005ffd83dbSDimitry Andric     const auto RDLocation = RD->getLocation();
1015ffd83dbSDimitry Andric     if (!RDLocation.isValid())
1025ffd83dbSDimitry Andric       return true;
1035ffd83dbSDimitry Andric 
1045ffd83dbSDimitry Andric     const auto Kind = RD->getTagKind();
1055ffd83dbSDimitry Andric     // FIMXE: Should we check union members too?
1065f757f3fSDimitry Andric     if (Kind != TagTypeKind::Struct && Kind != TagTypeKind::Class)
1075ffd83dbSDimitry Andric       return true;
1085ffd83dbSDimitry Andric 
1095ffd83dbSDimitry Andric     // Ignore CXXRecords that come from system headers.
1105ffd83dbSDimitry Andric     if (BR->getSourceManager().isInSystemHeader(RDLocation))
1115ffd83dbSDimitry Andric       return true;
1125ffd83dbSDimitry Andric 
1135ffd83dbSDimitry Andric     // Ref-counted smartpointers actually have raw-pointer to uncounted type as
1145ffd83dbSDimitry Andric     // a member but we trust them to handle it correctly.
1155ffd83dbSDimitry Andric     auto CXXRD = llvm::dyn_cast_or_null<CXXRecordDecl>(RD);
1165ffd83dbSDimitry Andric     if (CXXRD)
1175ffd83dbSDimitry Andric       return isRefCounted(CXXRD);
1185ffd83dbSDimitry Andric 
1195ffd83dbSDimitry Andric     return false;
1205ffd83dbSDimitry Andric   }
1215ffd83dbSDimitry Andric 
1225ffd83dbSDimitry Andric   void reportBug(const FieldDecl *Member, const Type *MemberType,
1235ffd83dbSDimitry Andric                  const CXXRecordDecl *MemberCXXRD,
1245ffd83dbSDimitry Andric                  const RecordDecl *ClassCXXRD) const {
1255ffd83dbSDimitry Andric     assert(Member);
1265ffd83dbSDimitry Andric     assert(MemberType);
1275ffd83dbSDimitry Andric     assert(MemberCXXRD);
1285ffd83dbSDimitry Andric 
1295ffd83dbSDimitry Andric     SmallString<100> Buf;
1305ffd83dbSDimitry Andric     llvm::raw_svector_ostream Os(Buf);
1315ffd83dbSDimitry Andric 
1325ffd83dbSDimitry Andric     Os << "Member variable ";
1335ffd83dbSDimitry Andric     printQuotedName(Os, Member);
1345ffd83dbSDimitry Andric     Os << " in ";
1355ffd83dbSDimitry Andric     printQuotedQualifiedName(Os, ClassCXXRD);
1365ffd83dbSDimitry Andric     Os << " is a "
1375ffd83dbSDimitry Andric        << (isa<PointerType>(MemberType) ? "raw pointer" : "reference")
1385ffd83dbSDimitry Andric        << " to ref-countable type ";
1395ffd83dbSDimitry Andric     printQuotedQualifiedName(Os, MemberCXXRD);
1405ffd83dbSDimitry Andric     Os << "; member variables must be ref-counted.";
1415ffd83dbSDimitry Andric 
1425ffd83dbSDimitry Andric     PathDiagnosticLocation BSLoc(Member->getSourceRange().getBegin(),
1435ffd83dbSDimitry Andric                                  BR->getSourceManager());
1445ffd83dbSDimitry Andric     auto Report = std::make_unique<BasicBugReport>(Bug, Os.str(), BSLoc);
1455ffd83dbSDimitry Andric     Report->addRange(Member->getSourceRange());
1465ffd83dbSDimitry Andric     BR->emitReport(std::move(Report));
1475ffd83dbSDimitry Andric   }
1485ffd83dbSDimitry Andric };
1495ffd83dbSDimitry Andric } // namespace
1505ffd83dbSDimitry Andric 
1515ffd83dbSDimitry Andric void ento::registerNoUncountedMemberChecker(CheckerManager &Mgr) {
1525ffd83dbSDimitry Andric   Mgr.registerChecker<NoUncountedMemberChecker>();
1535ffd83dbSDimitry Andric }
1545ffd83dbSDimitry Andric 
1555ffd83dbSDimitry Andric bool ento::shouldRegisterNoUncountedMemberChecker(
1565ffd83dbSDimitry Andric     const CheckerManager &Mgr) {
1575ffd83dbSDimitry Andric   return true;
1585ffd83dbSDimitry Andric }
159