diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.h b/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.h index 9ed8e7cab6abb..ec1db1cc33580 100644 --- a/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.h +++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.h @@ -50,6 +50,9 @@ std::optional isUncounted(const clang::CXXRecordDecl* Class); /// class, false if not, std::nullopt if inconclusive. std::optional isUncountedPtr(const clang::Type* T); +/// \returns true if Name is a RefPtr, Ref, or its variant, false if not. +bool isRefType(const std::string &Name); + /// \returns true if \p F creates ref-countable object from uncounted parameter, /// false if not. bool isCtorOfRefCounted(const clang::FunctionDecl *F); diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedCallArgsChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedCallArgsChecker.cpp index 8b41a949fd673..741f336761589 100644 --- a/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedCallArgsChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedCallArgsChecker.cpp @@ -53,6 +53,12 @@ class UncountedCallArgsChecker bool shouldVisitTemplateInstantiations() const { return true; } bool shouldVisitImplicitCode() const { return false; } + bool TraverseDecl(Decl *D) { + if (isa(D) && isRefType(safeGetName(D))) + return true; + return RecursiveASTVisitor::TraverseDecl(D); + } + bool VisitCallExpr(const CallExpr *CE) { Checker->visitCallExpr(CE); return true; diff --git a/clang/test/Analysis/Checkers/WebKit/call-args.cpp b/clang/test/Analysis/Checkers/WebKit/call-args.cpp index f2e1f9bc5a246..2a4b6bb1f1063 100644 --- a/clang/test/Analysis/Checkers/WebKit/call-args.cpp +++ b/clang/test/Analysis/Checkers/WebKit/call-args.cpp @@ -32,7 +32,7 @@ namespace ref_counted { void consume_ref_counted(Ref) {} void foo() { - consume_refcntbl(provide_ref_counted().get()); + consume_refcntbl(provide_ref_counted().ptr()); // no warning } } diff --git a/clang/test/Analysis/Checkers/WebKit/mock-types.h b/clang/test/Analysis/Checkers/WebKit/mock-types.h index aab99197dfa49..c27ea9baaf3bf 100644 --- a/clang/test/Analysis/Checkers/WebKit/mock-types.h +++ b/clang/test/Analysis/Checkers/WebKit/mock-types.h @@ -1,24 +1,61 @@ #ifndef mock_types_1103988513531 #define mock_types_1103988513531 -template struct Ref { - T *t; +template +struct RawPtrTraits { + using StorageType = T*; - Ref() : t{} {}; - Ref(T &t) - : t(t) { - if (t) - t->ref(); + template + static T* exchange(StorageType& ptr, U&& newValue) + { + StorageType oldValue = static_cast(ptr); + ptr = static_cast(newValue); + return oldValue; } - ~Ref() { - if (t) - t->deref(); + + static void swap(StorageType& a, StorageType& b) + { + StorageType temp = static_cast(a); + a = static_cast(b); + b = static_cast(temp); } - T *get() { return t; } - T *ptr() { return t; } - T *operator->() { return t; } - operator const T &() const { return *t; } - operator T &() { return *t; } + static T* unwrap(const StorageType& ptr) { return ptr; } +}; + +template struct DefaultRefDerefTraits { + static T* refIfNotNull(T* ptr) + { + if (ptr) + ptr->ref(); + return ptr; + } + + static T& ref(T& ref) + { + ref.ref(); + return ref; + } + + static void derefIfNotNull(T* ptr) + { + if (ptr) + ptr->deref(); + } +}; + +template , typename RefDerefTraits = DefaultRefDerefTraits> struct Ref { + typename PtrTraits::StorageType t; + + Ref() : t{} {}; + Ref(T &t) : t(RefDerefTraits::refIfNotNull(t)) { } + Ref(const Ref& o) : t(RefDerefTraits::refIfNotNull(PtrTraits::unwrap(o.t))) { } + ~Ref() { RefDerefTraits::derefIfNotNull(PtrTraits::exchange(t, nullptr)); } + T &get() { return *PtrTraits::unwrap(t); } + T *ptr() { return PtrTraits::unwrap(t); } + T *operator->() { return PtrTraits::unwrap(t); } + operator const T &() const { return *PtrTraits::unwrap(t); } + operator T &() { return *PtrTraits::unwrap(t); } + T* leakRef() { PtrTraits::exchange(t, nullptr); } }; template struct RefPtr {