Index: clang/include/clang/StaticAnalyzer/Checkers/Checkers.td =================================================================== --- clang/include/clang/StaticAnalyzer/Checkers/Checkers.td +++ clang/include/clang/StaticAnalyzer/Checkers/Checkers.td @@ -114,6 +114,9 @@ def Fuchsia : Package<"fuchsia">; def FuchsiaAlpha : Package<"fuchsia">, ParentPackage; +def WebKit : Package<"webkit">; +def WebKitAlpha : Package<"webkit">, ParentPackage; + //===----------------------------------------------------------------------===// // Core Checkers. //===----------------------------------------------------------------------===// @@ -1501,3 +1504,15 @@ } // end fuchsia +//===----------------------------------------------------------------------===// +// WebKit checkers. +//===----------------------------------------------------------------------===// + +let ParentPackage = WebKit in { + +// TODO documentation? + +def WebKitRefCntblBaseVirtualDtorChecker : Checker<"WebKitRefCntblBaseVirtualDtor">, + HelpText<"Check for any ref-countable base class having virtual destructor.">, + Documentation; +} // end webkit Index: clang/lib/StaticAnalyzer/Checkers/CMakeLists.txt =================================================================== --- clang/lib/StaticAnalyzer/Checkers/CMakeLists.txt +++ clang/lib/StaticAnalyzer/Checkers/CMakeLists.txt @@ -120,6 +120,9 @@ VLASizeChecker.cpp ValistChecker.cpp VirtualCallChecker.cpp + WebKit/ASTUtils.cpp + WebKit/PtrTypesSemantics.cpp + WebKit/RefCntblBaseVirtualDtorChecker.cpp LINK_LIBS clangAST Index: clang/lib/StaticAnalyzer/Checkers/WebKit/ASTUtils.h =================================================================== --- /dev/null +++ clang/lib/StaticAnalyzer/Checkers/WebKit/ASTUtils.h @@ -0,0 +1,70 @@ +//=======- ASTUtis.h ---------------------------------------------*- C++ -*-==// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_ANALYZER_WEBKIT_ASTUTILS_H +#define LLVM_CLANG_ANALYZER_WEBKIT_ASTUTILS_H + +#include "clang/AST/Decl.h" +#include "llvm/ADT/APInt.h" +#include "llvm/Support/Casting.h" + +#include +#include + +namespace clang { +class CXXRecordDecl; +class CXXBaseSpecifier; +class FunctionDecl; +class CXXMethodDecl; +class Expr; + +/// If passed expression is of type uncounted pointer/reference we try to find +/// the origin of this pointer. Example: Origin can be a local variable, nullptr +/// constant or this-pointer. +/// +/// Certain subexpression nodes represent transformations that don't affect +/// where the memory address originates from. We try to traverse such +/// subexpressions to get to the relevant child nodes. Whenever we encounter a +/// subexpression that either can't be ignored, we don't model its semantics or +/// that has multiple children we stop. +/// +/// \p E is an expression of uncounted pointer/reference type. +/// If \p StopAtFirstRefCountedObj is true and we encounter a subexpression that +/// represents ref-counted object during the traversal we return relevant +/// sub-expression and true. +/// +/// \returns subexpression that we traversed to and if \p +/// StopAtFirstRefCountedObj is true we also return whether we stopped early. +std::pair +tryToFindPtrOrigin(const clang::Expr *E, bool StopAtFirstRefCountedObj); + +/// For \p E referring to a ref-countable/-counted pointer/reference we return +/// whether it's a safe call argument. Examples: function parameter or +/// this-pointer. The logic relies on the set of recursive rules we enforce for +/// WebKit codebase. +/// +/// \returns Whether \p E is a safe call arugment. +bool isASafeCallArg(const clang::Expr *E); + +/// \returns name of AST node or empty string. +template std::string safeGetName(const T *ASTNode) { + const auto *const ND = llvm::dyn_cast_or_null(ASTNode); + if (!ND) + return ""; + + // In case F is for example "operator|" the getName() method below would + // assert. + if (!ND->getDeclName().isIdentifier()) + return ""; + + return ND->getName().str(); +} + +} // namespace clang + +#endif Index: clang/lib/StaticAnalyzer/Checkers/WebKit/ASTUtils.cpp =================================================================== --- /dev/null +++ clang/lib/StaticAnalyzer/Checkers/WebKit/ASTUtils.cpp @@ -0,0 +1,91 @@ +//=======- ASTUtils.cpp ------------------------------------------*- C++ -*-==// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "ASTUtils.h" +#include "PtrTypesSemantics.h" +#include "clang/AST/CXXInheritance.h" +#include "clang/AST/Decl.h" +#include "clang/AST/DeclCXX.h" +#include "clang/AST/ExprCXX.h" + +using llvm::Optional; +namespace clang { + +std::pair +tryToFindPtrOrigin(const Expr *E, bool StopAtFirstRefCountedObj) { + while (E) { + if (auto *cast = dyn_cast(E)) { + if (StopAtFirstRefCountedObj) { + if (auto *ConversionFunc = + dyn_cast_or_null(cast->getConversionFunction())) { + if (isCtorOfRefCounted(ConversionFunc)) + return {E, true}; + } + } + E = cast->getSubExpr(); + continue; + } + if (auto *call = dyn_cast(E)) { + if (auto *memberCall = dyn_cast(call)) { + if (isGetterOfRefCounted(memberCall->getMethodDecl())) { + E = memberCall->getImplicitObjectArgument(); + if (StopAtFirstRefCountedObj) { + return {E, true}; + } + continue; + } + } + + if (auto *operatorCall = dyn_cast(E)) { + if (operatorCall->getNumArgs() == 1) { + E = operatorCall->getArg(0); + continue; + } + } + + if (auto *callee = call->getDirectCallee()) { + if (isCtorOfRefCounted(callee)) { + if (StopAtFirstRefCountedObj) + return {E, true}; + + E = call->getArg(0); + continue; + } + + if (isPtrConversion(callee)) { + E = call->getArg(0); + continue; + } + } + } + if (auto *unaryOp = dyn_cast(E)) { + // FIXME: Currently accepts ANY unary operator. Is it OK? + E = unaryOp->getSubExpr(); + continue; + } + + break; + } + // Some other expression. + return {E, false}; +} + +bool isASafeCallArg(const Expr *E) { + assert(E); + if (auto *Ref = dyn_cast(E)) { + if (auto *D = dyn_cast_or_null(Ref->getFoundDecl())) { + if (isa(D) || D->isLocalVarDecl()) + return true; + } + } + + // TODO: checker for method calls on non-refcounted objects + return isa(E); +} + +} // namespace clang Index: clang/lib/StaticAnalyzer/Checkers/WebKit/DiagOutputUtils.h =================================================================== --- /dev/null +++ clang/lib/StaticAnalyzer/Checkers/WebKit/DiagOutputUtils.h @@ -0,0 +1,28 @@ +//=======- DiagOutputUtils.h -------------------------------------*- C++ -*-==// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_ANALYZER_WEBKIT_DIAGPRINTUTILS_H +#define LLVM_CLANG_ANALYZER_WEBKIT_DIAGPRINTUTILS_H + +#include "clang/AST/Decl.h" +#include "llvm/Support/raw_ostream.h" + +namespace clang { + +template +void printQuotedQualifiedName(llvm::raw_ostream &Os, + const NamedDeclDerivedT &D) { + Os << "'"; + D->getNameForDiagnostic(Os, D->getASTContext().getPrintingPolicy(), + /*Qualified=*/true); + Os << "'"; +} + +} // namespace clang + +#endif Index: clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.h =================================================================== --- /dev/null +++ clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.h @@ -0,0 +1,59 @@ +//=======- PtrTypesSemantics.cpp ---------------------------------*- C++ -*-==// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_ANALYZER_WEBKIT_PTRTYPESEMANTICS_H +#define LLVM_CLANG_ANALYZER_WEBKIT_PTRTYPESEMANTICS_H + +namespace clang { +class CXXBaseSpecifier; +class CXXMethodDecl; +class CXXRecordDecl; +class Expr; +class FunctionDecl; +class Type; + +// Ref-countability of a type is implicitly defined by Ref and RefPtr +// implementation. It can be modeled as: type T having public methods ref() and +// deref() + +// In WebKit there are two ref-counted templated smart pointers: RefPtr and +// Ref. + +/// \returns CXXRecordDecl of the base if the type is ref-countable, nullptr if +/// not. +const clang::CXXRecordDecl *isRefCountable(const clang::CXXBaseSpecifier *Base); + +/// \returns true if \p Class is ref-countable, false if not. +/// Asserts that \p Class IS a definition. +bool isRefCountable(const clang::CXXRecordDecl *Class); + +/// \returns true if \p Class is ref-counted, false if not. +bool isRefCounted(const clang::CXXRecordDecl *Class); + +/// \returns true if \p Class is ref-countable AND not ref-counted, false if +/// not. Asserts that \p Class IS a definition. +bool isUncounted(const clang::CXXRecordDecl *Class); + +/// \returns true if \p T is either a raw pointer or reference to an uncounted +/// class, false if not. +bool isUncountedPtr(const clang::Type *T); + +/// \returns true if \p F creates ref-countable object from uncounted parameter, +/// false if not. +bool isCtorOfRefCounted(const clang::FunctionDecl *F); + +/// \returns true if \p M is getter of a ref-counted class, false if not. +bool isGetterOfRefCounted(const clang::CXXMethodDecl *Method); + +/// \returns true if \p F is a conversion between ref-countable or ref-counted +/// pointer types. +bool isPtrConversion(const FunctionDecl *F); + +} // namespace clang + +#endif Index: clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp =================================================================== --- /dev/null +++ clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp @@ -0,0 +1,172 @@ +//=======- PtrTypesSemantics.cpp ---------------------------------*- C++ -*-==// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "PtrTypesSemantics.h" +#include "ASTUtils.h" +#include "clang/AST/CXXInheritance.h" +#include "clang/AST/Decl.h" +#include "clang/AST/DeclCXX.h" +#include "clang/AST/ExprCXX.h" + +using llvm::Optional; +using namespace clang; + +namespace { + +bool hasPublicRefAndDeref(const CXXRecordDecl *R) { + assert(R); + + bool hasRef = false; + bool hasDeref = false; + for (const CXXMethodDecl *MD : R->methods()) { + const auto MethodName = safeGetName(MD); + + if (MethodName == "ref" && MD->getAccess() == AS_public) { + if (hasDeref) + return true; + hasRef = true; + } else if (MethodName == "deref" && MD->getAccess() == AS_public) { + if (hasRef) + return true; + hasDeref = true; + } + } + return false; +} + +} // namespace + +namespace clang { + +const CXXRecordDecl *isRefCountable(const CXXBaseSpecifier *Base) { + assert(Base); + + const Type *T = Base->getType().getTypePtrOrNull(); + if (!T) + return nullptr; + + const CXXRecordDecl *R = T->getAsCXXRecordDecl(); + if (!R) + return nullptr; + + return hasPublicRefAndDeref(R) ? R : nullptr; +}; + +bool isRefCountable(const CXXRecordDecl *R) { + assert(R); + + R = R->getDefinition(); + assert(R); + + if (hasPublicRefAndDeref(R)) + return true; + + CXXBasePaths Paths; + Paths.setOrigin(const_cast(R)); + + const auto isRefCountableBase = [](const CXXBaseSpecifier *Base, + CXXBasePath &) { + return clang::isRefCountable(Base); + }; + + return R->lookupInBases(isRefCountableBase, Paths, + /*LookupInDependent =*/true); +} + +bool isCtorOfRefCounted(const clang::FunctionDecl *F) { + assert(F); + const auto &FunctionName = safeGetName(F); + + return FunctionName == "Ref" || FunctionName == "makeRef" + + || FunctionName == "RefPtr" || FunctionName == "makeRefPtr" + + || FunctionName == "UniqueRef" || FunctionName == "makeUniqueRef" || + FunctionName == "makeUniqueRefWithoutFastMallocCheck" + + || FunctionName == "String" || FunctionName == "AtomString" || + FunctionName == "UniqueString" + // FIXME: Implement as attribute. + || FunctionName == "Identifier"; +} + +bool isUncounted(const CXXRecordDecl *Class) { + // Keep isRefCounted first as it's cheaper. + return !isRefCounted(Class) && isRefCountable(Class); +} + +bool isUncountedPtr(const Type *T) { + assert(T); + + if (T->isPointerType() || T->isReferenceType()) { + if (auto *CXXRD = T->getPointeeCXXRecordDecl()) { + return isUncounted(CXXRD); + } + } + return false; +} + +bool isGetterOfRefCounted(const CXXMethodDecl *M) { + assert(M); + + if (auto *calleeMethodDecl = dyn_cast(M)) { + const CXXRecordDecl *calleeMethodsClass = M->getParent(); + auto className = safeGetName(calleeMethodsClass); + auto methodName = safeGetName(M); + + if (((className == "Ref" || className == "RefPtr") && + methodName == "get") || + ((className == "String" || className == "AtomString" || + className == "AtomStringImpl" || className == "UniqueString" || + className == "UniqueStringImpl" || className == "Identifier") && + methodName == "impl")) + return true; + + // Ref -> T conversion + // FIXME: Currently allowing any Ref -> whatever cast. + if (className == "Ref" || className == "RefPtr") { + if (auto *maybeRefToRawOperator = dyn_cast(M)) { + if (auto *targetConversionType = + maybeRefToRawOperator->getConversionType().getTypePtrOrNull()) { + if (isUncountedPtr(targetConversionType)) { + return true; + } + } + } + } + } + return false; +} + +bool isRefCounted(const CXXRecordDecl *R) { + assert(R); + if (auto *TmplR = R->getTemplateInstantiationPattern()) { + // FIXME: String/AtomString/UniqueString + const auto &ClassName = safeGetName(TmplR); + return ClassName == "RefPtr" || ClassName == "Ref"; + } + return false; +} + +bool isPtrConversion(const FunctionDecl *F) { + assert(F); + if (isCtorOfRefCounted(F)) + return true; + + // FIXME: check # of params == 1 + const auto FunctionName = safeGetName(F); + if (FunctionName == "getPtr" || FunctionName == "WeakPtr" || + FunctionName == "makeWeakPtr" + + || FunctionName == "downcast" || FunctionName == "bitwise_cast") + return true; + + return false; +} + +} // namespace clang Index: clang/lib/StaticAnalyzer/Checkers/WebKit/RefCntblBaseVirtualDtorChecker.cpp =================================================================== --- /dev/null +++ clang/lib/StaticAnalyzer/Checkers/WebKit/RefCntblBaseVirtualDtorChecker.cpp @@ -0,0 +1,169 @@ +//=======- RefCntblBaseVirtualDtor.cpp ---------------------------*- C++ -*-==// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "DiagOutputUtils.h" +#include "PtrTypesSemantics.h" +#include "clang/AST/CXXInheritance.h" +#include "clang/AST/RecursiveASTVisitor.h" +#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" +#include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h" +#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" +#include "clang/StaticAnalyzer/Core/Checker.h" + +using namespace clang; +using namespace ento; + +namespace { +class RefCntblBaseVirtualDtorChecker + : public Checker> { +private: + BugType Bug; + mutable BugReporter *BR; + +public: + RefCntblBaseVirtualDtorChecker() + : Bug( + this, "Reference-countable base class doesn't have virtual destructor", + "WebKit coding guidelines" + ) + { } + + void checkASTDecl(const TranslationUnitDecl *TUD, AnalysisManager &MGR, + BugReporter &BRArg) const { + BR = &BRArg; + + // The calls to checkAST* from AnalysisConsumer don't + // visit template instantiations or lambda classes. We + // want to visit those, so we make our own RecursiveASTVisitor. + struct LocalVisitor : public RecursiveASTVisitor { + const RefCntblBaseVirtualDtorChecker *Checker; + explicit LocalVisitor(const RefCntblBaseVirtualDtorChecker *Checker) + : Checker(Checker) { + assert(Checker); + } + + bool shouldVisitTemplateInstantiations() const { return true; } + bool shouldVisitImplicitCode() const { return false; } + + bool VisitCXXRecordDecl(const CXXRecordDecl *RD) { + Checker->visitCXXRecordDecl(RD); + return true; + } + }; + + LocalVisitor visitor(this); + visitor.TraverseDecl(const_cast(TUD)); + } + + void visitCXXRecordDecl(const CXXRecordDecl *RD) const { + if (shouldSkipDecl(RD)) + return; + + CXXBasePaths Paths; + Paths.setOrigin(RD); + + const CXXBaseSpecifier *ProblematicBaseSpecifier = nullptr; + const CXXRecordDecl *ProblematicBaseClass = nullptr; + + const auto IsPublicBaseRefCntblWOVirtualDtor = + [RD, &ProblematicBaseSpecifier, + &ProblematicBaseClass](const CXXBaseSpecifier *Base, CXXBasePath &) { + const auto AccSpec = Base->getAccessSpecifier(); + if (AccSpec == AS_protected || AccSpec == AS_private || + (AccSpec == AS_none && RD->isClass())) + return false; + + llvm::Optional MaybeRefCntblBaseRD = + isRefCountable(Base); + if (!MaybeRefCntblBaseRD.hasValue()) + return false; + + const CXXRecordDecl *RefCntblBaseRD = MaybeRefCntblBaseRD.getValue(); + if (!RefCntblBaseRD) + return false; + + const auto *Dtor = RefCntblBaseRD->getDestructor(); + if (!Dtor || !Dtor->isVirtual()) { + ProblematicBaseSpecifier = Base; + ProblematicBaseClass = RefCntblBaseRD; + return true; + } + + return false; + }; + + if (RD->lookupInBases(IsPublicBaseRefCntblWOVirtualDtor, Paths, + /*LookupInDependent =*/true)) { + reportBug(RD, ProblematicBaseSpecifier, ProblematicBaseClass); + } + } + + bool shouldSkipDecl(const CXXRecordDecl *RD) const { + if (!RD->isThisDeclarationADefinition()) + return true; + + if (RD->isImplicit()) + return true; + + if (RD->isLambda()) + return true; + + // If the construct doesn't have a source file, then it's not something + // we want to diagnose. + const auto RDLocation = RD->getLocation(); + if (!RDLocation.isValid()) + return true; + + const auto Kind = RD->getTagKind(); + if (Kind != TTK_Struct && Kind != TTK_Class) + return true; + + // Ignore CXXRecords that come from system headers. + if (BR->getSourceManager().getFileCharacteristic(RDLocation) != + SrcMgr::C_User) + return true; + + return false; + } + + void reportBug(const CXXRecordDecl *DerivedClass, + const CXXBaseSpecifier *BaseSpec, + const CXXRecordDecl *ProblematicBaseClass) const { + assert(DerivedClass); + assert(BaseSpec); + assert(ProblematicBaseClass); + + SmallString<100> Buf; + llvm::raw_svector_ostream Os(Buf); + + Os << (ProblematicBaseClass->isClass() ? "Class" : "Struct") << " "; + printQuotedQualifiedName(Os, ProblematicBaseClass); + + Os << " is used as a base of " + << (DerivedClass->isClass() ? "class" : "struct") << " "; + printQuotedQualifiedName(Os, DerivedClass); + + Os << " but doesn't have virtual destructor"; + + PathDiagnosticLocation BSLoc(BaseSpec->getSourceRange().getBegin(), + BR->getSourceManager()); + auto Report = std::make_unique(Bug, Os.str(), BSLoc); + Report->addRange(BaseSpec->getSourceRange()); + BR->emitReport(std::move(Report)); + } +}; +} // namespace + +void ento::registerWebKitRefCntblBaseVirtualDtorChecker(CheckerManager &Mgr) { + Mgr.registerChecker(); +} + +bool ento::shouldRegisterWebKitRefCntblBaseVirtualDtorChecker( + const LangOptions &LO) { + return true; +} Index: clang/test/Analysis/Checkers/WebKit/mock-types.h =================================================================== --- /dev/null +++ clang/test/Analysis/Checkers/WebKit/mock-types.h @@ -0,0 +1,48 @@ +#ifndef mock_types_1103988513531 +#define mock_types_1103988513531 + +template struct Ref { + T t; + + Ref() : t{} {}; + Ref(T *) {} + T *get() { return nullptr; } + operator const T &() const { return t; } + operator T &() { return t; } +}; + +template struct RefPtr { + T *t; + + RefPtr() : t(new T) {} + RefPtr(T *t) : t(t) {} + T *get() { return t; } + T *operator->() { return t; } + T &operator*() { return *t; } + RefPtr &operator=(T *) { return *this; } +}; + +template bool operator==(const RefPtr &, const RefPtr &) { + return false; +} + +template bool operator==(const RefPtr &, T *) { return false; } + +template bool operator==(const RefPtr &, T &) { return false; } + +template bool operator!=(const RefPtr &, const RefPtr &) { + return false; +} + +template bool operator!=(const RefPtr &, T *) { return false; } + +template bool operator!=(const RefPtr &, T &) { return false; } + +struct RefCountable { + void ref() {} + void deref() {} +}; + +template T *downcast(T *t) { return t; } + +#endif Index: clang/test/Analysis/Checkers/WebKit/ref-cntbl-base-virtual-dtor-templates.cpp =================================================================== --- /dev/null +++ clang/test/Analysis/Checkers/WebKit/ref-cntbl-base-virtual-dtor-templates.cpp @@ -0,0 +1,30 @@ +// RUN: %clang_analyze_cc1 -analyzer-checker=webkit.WebKitRefCntblBaseVirtualDtor -verify %s + +struct RefCntblBase { + void ref() {} + void deref() {} +}; + +template +struct DerivedClassTmpl1 : T { }; +// expected-warning@-1{{Struct 'RefCntblBase' is used as a base of struct 'DerivedClassTmpl1' but doesn't have virtual destructor}} + +DerivedClassTmpl1 a; + + + +template +struct DerivedClassTmpl2 : T { }; +// expected-warning@-1{{Struct 'RefCntblBase' is used as a base of struct 'DerivedClassTmpl2' but doesn't have virtual destructor}} + +template int foo(T) { DerivedClassTmpl2 f; return 42; } +int b = foo(RefCntblBase{}); + + + +template +struct DerivedClassTmpl3 : T { }; +// expected-warning@-1{{Struct 'RefCntblBase' is used as a base of struct 'DerivedClassTmpl3' but doesn't have virtual destructor}} + +typedef DerivedClassTmpl3 Foo; +Foo c; Index: clang/test/Analysis/Checkers/WebKit/ref-cntbl-base-virtual-dtor.cpp =================================================================== --- /dev/null +++ clang/test/Analysis/Checkers/WebKit/ref-cntbl-base-virtual-dtor.cpp @@ -0,0 +1,53 @@ +// RUN: %clang_analyze_cc1 -analyzer-checker=webkit.WebKitRefCntblBaseVirtualDtor -verify %s + +struct RefCntblBase { + void ref() {} + void deref() {} +}; + +struct Derived : RefCntblBase { }; +// expected-warning@-1{{Struct 'RefCntblBase' is used as a base of struct 'Derived' but doesn't have virtual destructor}} + +struct DerivedWithVirtualDtor : RefCntblBase { +// expected-warning@-1{{Struct 'RefCntblBase' is used as a base of struct 'DerivedWithVirtualDtor' but doesn't have virtual destructor}} + virtual ~DerivedWithVirtualDtor() {} +}; + + + +template +struct DerivedClassTmpl : T { }; +typedef DerivedClassTmpl Foo; + + + +struct RandomBase {}; +struct RandomDerivedClass : RandomBase { }; + + + +struct FakeRefCntblBase1 { + private: + void ref() {} + void deref() {} +}; +struct Quiet1 : FakeRefCntblBase1 {}; + +struct FakeRefCntblBase2 { + protected: + void ref() {} + void deref() {} +}; +struct Quiet2 : FakeRefCntblBase2 {}; + +class FakeRefCntblBase3 { + void ref() {} + void deref() {} +}; +struct Quiet3 : FakeRefCntblBase3 {}; +struct Quiet4 : private RefCntblBase {}; +class Quiet5 : RefCntblBase {}; + +void foo () { + Derived d; +}