[clang] Fix WebKit static analyzers to support ref and deref methods to be defined on different classes. (PR #69985)
https://github.com/rniwa updated https://github.com/llvm/llvm-project/pull/69985 >From bf169f3d79f8a5b0658d9e060ddd6c3e773b979a Mon Sep 17 00:00:00 2001 From: Ryosuke Niwa Date: Mon, 23 Oct 2023 16:38:10 -0700 Subject: [PATCH] Fix WebKit static analyzers to support ref and deref methods to be defined on different classes. --- .../Checkers/WebKit/PtrTypesSemantics.cpp | 62 ++- .../Checkers/WebKit/PtrTypesSemantics.h | 8 +-- .../WebKit/RefCntblBaseVirtualDtorChecker.cpp | 47 -- ...virtual-dtor-ref-deref-on-diff-classes.cpp | 22 +++ ...nted-members-ref-deref-on-diff-classes.cpp | 23 +++ 5 files changed, 126 insertions(+), 36 deletions(-) create mode 100644 clang/test/Analysis/Checkers/WebKit/ref-cntbl-base-virtual-dtor-ref-deref-on-diff-classes.cpp create mode 100644 clang/test/Analysis/Checkers/WebKit/uncounted-members-ref-deref-on-diff-classes.cpp diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp b/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp index c1f180f31338cb3..d2b663410580008 100644 --- a/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp @@ -18,24 +18,15 @@ using namespace clang; namespace { -bool hasPublicRefAndDeref(const CXXRecordDecl *R) { +bool hasPublicMethodInBaseClass(const CXXRecordDecl *R, +const char *NameToMatch) { assert(R); assert(R->hasDefinition()); - 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; -} +if (MethodName == NameToMatch && MD->getAccess() == AS_public) + return true; } return false; } @@ -44,9 +35,8 @@ bool hasPublicRefAndDeref(const CXXRecordDecl *R) { namespace clang { -std::optional -isRefCountable(const CXXBaseSpecifier* Base) -{ +std::optional +hasPublicMethodInBase(const CXXBaseSpecifier *Base, const char *NameToMatch) { assert(Base); const Type *T = Base->getType().getTypePtrOrNull(); @@ -59,7 +49,7 @@ isRefCountable(const CXXBaseSpecifier* Base) if (!R->hasDefinition()) return std::nullopt; - return hasPublicRefAndDeref(R) ? R : nullptr; + return hasPublicMethodInBaseClass(R, NameToMatch) ? R : nullptr; } std::optional isRefCountable(const CXXRecordDecl* R) @@ -70,29 +60,45 @@ std::optional isRefCountable(const CXXRecordDecl* R) if (!R) return std::nullopt; - if (hasPublicRefAndDeref(R)) + bool hasRef = hasPublicMethodInBaseClass(R, "ref"); + bool hasDeref = hasPublicMethodInBaseClass(R, "deref"); + if (hasRef && hasDeref) return true; CXXBasePaths Paths; Paths.setOrigin(const_cast(R)); bool AnyInconclusiveBase = false; - const auto isRefCountableBase = - [&AnyInconclusiveBase](const CXXBaseSpecifier* Base, CXXBasePath&) { - std::optional IsRefCountable = clang::isRefCountable(Base); - if (!IsRefCountable) { - AnyInconclusiveBase = true; - return false; - } - return (*IsRefCountable) != nullptr; + const auto hasPublicRefInBase = + [&AnyInconclusiveBase](const CXXBaseSpecifier *Base, CXXBasePath &) { +auto hasRefInBase = clang::hasPublicMethodInBase(Base, "ref"); +if (!hasRefInBase) { + AnyInconclusiveBase = true; + return false; +} +return (*hasRefInBase) != nullptr; }; - bool BasesResult = R->lookupInBases(isRefCountableBase, Paths, + hasRef = hasRef || R->lookupInBases(hasPublicRefInBase, Paths, /*LookupInDependent =*/true); if (AnyInconclusiveBase) return std::nullopt; - return BasesResult; + const auto hasPublicDerefInBase = + [&AnyInconclusiveBase](const CXXBaseSpecifier *Base, CXXBasePath &) { +auto hasDerefInBase = clang::hasPublicMethodInBase(Base, "deref"); +if (!hasDerefInBase) { + AnyInconclusiveBase = true; + return false; +} +return (*hasDerefInBase) != nullptr; + }; + hasDeref = hasDeref || R->lookupInBases(hasPublicDerefInBase, Paths, + /*LookupInDependent =*/true); + if (AnyInconclusiveBase) +return std::nullopt; + + return hasRef && hasDeref; } bool isCtorOfRefCounted(const clang::FunctionDecl *F) { diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.h b/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.h index 91e3ccf2ec3047e..45b21cc0918443b 100644 --- a/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.h +++ b/clang/lib/StaticAnalyzer/Checkers/WebK
[clang] Fix WebKit static analyzers to support ref and deref methods to be defined on different classes. (PR #69985)
@@ -77,14 +77,53 @@ class RefCntblBaseVirtualDtorChecker (AccSpec == AS_none && RD->isClass())) return false; - std::optional RefCntblBaseRD = isRefCountable(Base); - if (!RefCntblBaseRD || !(*RefCntblBaseRD)) + auto hasRefInBase = clang::hasPublicMethodInBase(Base, "ref"); + auto hasDerefInBase = clang::hasPublicMethodInBase(Base, "deref"); + + bool hasRef = hasRefInBase && *hasRefInBase != nullptr; + bool hasDeref = hasDerefInBase && *hasDerefInBase != nullptr; + + const Type *T = Base->getType().getTypePtrOrNull(); + if (!T) +return false; + + const CXXRecordDecl *C = T->getAsCXXRecordDecl(); rniwa wrote: Fixed. https://github.com/llvm/llvm-project/pull/69985 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] Fix WebKit static analyzers to support ref and deref methods to be defined on different classes. (PR #69985)
rniwa wrote: Yes, it would be great if you can merge the change now that I've addressed your nits. https://github.com/llvm/llvm-project/pull/69985 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] Ignore assignment to Ref / RefPtr in alpha.webkit.UncountedCallArgsChecker. (PR #80810)
https://github.com/rniwa updated https://github.com/llvm/llvm-project/pull/80810 >From e179bbef69084caac3948977a7091332c69130f5 Mon Sep 17 00:00:00 2001 From: Ryosuke Niwa Date: Tue, 6 Feb 2024 01:13:34 -0800 Subject: [PATCH] Ignore assignment to Ref / RefPtr in alpha.webkit.UncountedCallArgsChecker. --- .../WebKit/UncountedCallArgsChecker.cpp | 10 +++ .../Checkers/WebKit/assignment-to-refptr.cpp | 71 +++ 2 files changed, 81 insertions(+) create mode 100644 clang/test/Analysis/Checkers/WebKit/assignment-to-refptr.cpp diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedCallArgsChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedCallArgsChecker.cpp index 407b6ba7a76428..419140bf12b66c 100644 --- a/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedCallArgsChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedCallArgsChecker.cpp @@ -126,6 +126,16 @@ class UncountedCallArgsChecker // of object on LHS. if (auto *MemberOp = dyn_cast(CE)) { // Note: assignemnt to built-in type isn't derived from CallExpr. + if (MemberOp->getOperator() == + OO_Equal) { // Ignore assignment to Ref/RefPtr. +auto *callee = MemberOp->getDirectCallee(); +if (auto *calleeDecl = dyn_cast(callee)) { + if (const CXXRecordDecl *classDecl = calleeDecl->getParent()) { +if (isRefCounted(classDecl)) + return true; + } +} + } if (MemberOp->isAssignmentOp()) return false; } diff --git a/clang/test/Analysis/Checkers/WebKit/assignment-to-refptr.cpp b/clang/test/Analysis/Checkers/WebKit/assignment-to-refptr.cpp new file mode 100644 index 00..c8ad634a5493f5 --- /dev/null +++ b/clang/test/Analysis/Checkers/WebKit/assignment-to-refptr.cpp @@ -0,0 +1,71 @@ +// RUN: %clang_analyze_cc1 -analyzer-checker=alpha.webkit.UncountedCallArgsChecker -verify %s +// expected-no-diagnostics + +template +class RefPtr { +public: +inline constexpr RefPtr() : m_ptr(nullptr) { } +inline RefPtr(T* ptr) + : m_ptr(ptr) +{ +if (m_ptr) +m_ptr->ref(); +} + +inline RefPtr(const RefPtr& o) +: m_ptr(o.m_ptr) +{ +if (m_ptr) +m_ptr->ref(); +} + +inline ~RefPtr() +{ +if (m_ptr) +m_ptr->deref(); +} + +inline T* operator->() const { return m_ptr; } +explicit operator bool() const { return m_ptr; } + +RefPtr& operator=(const RefPtr&); +RefPtr& operator=(T*); + +private: +T* m_ptr; +}; + +template +inline RefPtr& RefPtr::operator=(const RefPtr& o) +{ +if (m_ptr) +m_ptr->deref(); +m_ptr = o.m_ptr; +if (m_ptr) +m_ptr->ref(); +return *this; +} + +template +inline RefPtr& RefPtr::operator=(T* optr) +{ +if (m_ptr) +m_ptr->deref(); +m_ptr = optr; +if (m_ptr) +m_ptr->ref(); +return *this; +} + +class Node { +public: +Node* nextSibling() const; + +void ref() const; +void deref() const; +}; + +static void removeDetachedChildren(Node* firstChild) +{ +for (RefPtr child = firstChild; child; child = child->nextSibling()); +} ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] Add the support for calling Ref::ptr accessor. (PR #80919)
https://github.com/rniwa created https://github.com/llvm/llvm-project/pull/80919 This accessor returns a pointer from Ref type and is therefore safe. >From 63a64cf22e5e470db3426688a2517c2fef64fd46 Mon Sep 17 00:00:00 2001 From: Ryosuke Niwa Date: Tue, 6 Feb 2024 17:56:01 -0800 Subject: [PATCH] Add the support for calling Ref::ptr accessor. This accessor returns a pointer from Ref type and is therefore safe. --- .../Checkers/WebKit/PtrTypesSemantics.cpp | 1 + .../Checkers/WebKit/ref-ptr-accessor.cpp | 86 +++ 2 files changed, 87 insertions(+) create mode 100644 clang/test/Analysis/Checkers/WebKit/ref-ptr-accessor.cpp diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp b/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp index c1f180f31338cb..4bc3f7a8c9c3ce 100644 --- a/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp @@ -148,6 +148,7 @@ std::optional isGetterOfRefCounted(const CXXMethodDecl* M) if (((className == "Ref" || className == "RefPtr") && methodName == "get") || +(className == "Ref" && methodName == "ptr") || ((className == "String" || className == "AtomString" || className == "AtomStringImpl" || className == "UniqueString" || className == "UniqueStringImpl" || className == "Identifier") && diff --git a/clang/test/Analysis/Checkers/WebKit/ref-ptr-accessor.cpp b/clang/test/Analysis/Checkers/WebKit/ref-ptr-accessor.cpp new file mode 100644 index 00..d3185a9f2d58a4 --- /dev/null +++ b/clang/test/Analysis/Checkers/WebKit/ref-ptr-accessor.cpp @@ -0,0 +1,86 @@ +// RUN: %clang_analyze_cc1 -analyzer-checker=alpha.webkit.UncountedCallArgsChecker -verify %s +// expected-no-diagnostics + +template +class Ref { +public: +~Ref() +{ +if (auto* ptr = m_ptr) +ptr->deref(); +m_ptr = nullptr; +} + +Ref(T& object) +: m_ptr(&object) +{ +object.ref(); +} + +Ref(const Ref& other) +: m_ptr(other.ptr()) +{ +m_ptr->ref(); +} + +template Ref(const Ref& other) +: m_ptr(other.ptr()) +{ +m_ptr->ref(); +} + +Ref(Ref&& other) +: m_ptr(&other.leakRef()) +{ +} + +template +Ref(Ref&& other) +: m_ptr(&other.leakRef()) +{ +} + +T* operator->() const { return m_ptr; } +T* ptr() const { return m_ptr; } +T& get() const { return *m_ptr; } +operator T&() const { return *m_ptr; } +bool operator!() const { return !*m_ptr; } + +T& leakRef() +{ +T& result = *m_ptr; +m_ptr = nullptr; +return result; +} + +private: +template friend inline Ref adoptRef(U&); + +enum AdoptTag { Adopt }; +Ref(T& object, AdoptTag) +: m_ptr(&object) +{ +} + +T* m_ptr; +}; + +class Node { +public: +static Ref create(); +virtual ~Node(); + +void ref() const; +void deref() const; + +protected: +explicit Node(); +}; + +void someFunction(Node*); + +void testFunction() +{ +Ref node = Node::create(); +someFunction(node.ptr()); +} ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] Add a new attribute value for suppressing WebKit's unsafe member variable warning (PR #70124)
https://github.com/rniwa closed https://github.com/llvm/llvm-project/pull/70124 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] Add the support for calling Ref::ptr accessor. (PR #80919)
https://github.com/rniwa updated https://github.com/llvm/llvm-project/pull/80919 >From b117f4c8247a14a02ddb2cc89493a54a6dd3815e Mon Sep 17 00:00:00 2001 From: Ryosuke Niwa Date: Tue, 6 Feb 2024 17:56:01 -0800 Subject: [PATCH] [alpha.webkit.UncountedCallArgsChecker] Add the support for calling Ref::ptr accessor. This accessor returns a pointer from Ref type and is therefore safe. --- .../Checkers/WebKit/PtrTypesSemantics.cpp | 1 + .../Checkers/WebKit/ref-ptr-accessor.cpp | 86 +++ 2 files changed, 87 insertions(+) create mode 100644 clang/test/Analysis/Checkers/WebKit/ref-ptr-accessor.cpp diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp b/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp index c1f180f31338c..4bc3f7a8c9c3c 100644 --- a/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp @@ -148,6 +148,7 @@ std::optional isGetterOfRefCounted(const CXXMethodDecl* M) if (((className == "Ref" || className == "RefPtr") && methodName == "get") || +(className == "Ref" && methodName == "ptr") || ((className == "String" || className == "AtomString" || className == "AtomStringImpl" || className == "UniqueString" || className == "UniqueStringImpl" || className == "Identifier") && diff --git a/clang/test/Analysis/Checkers/WebKit/ref-ptr-accessor.cpp b/clang/test/Analysis/Checkers/WebKit/ref-ptr-accessor.cpp new file mode 100644 index 0..d3185a9f2d58a --- /dev/null +++ b/clang/test/Analysis/Checkers/WebKit/ref-ptr-accessor.cpp @@ -0,0 +1,86 @@ +// RUN: %clang_analyze_cc1 -analyzer-checker=alpha.webkit.UncountedCallArgsChecker -verify %s +// expected-no-diagnostics + +template +class Ref { +public: +~Ref() +{ +if (auto* ptr = m_ptr) +ptr->deref(); +m_ptr = nullptr; +} + +Ref(T& object) +: m_ptr(&object) +{ +object.ref(); +} + +Ref(const Ref& other) +: m_ptr(other.ptr()) +{ +m_ptr->ref(); +} + +template Ref(const Ref& other) +: m_ptr(other.ptr()) +{ +m_ptr->ref(); +} + +Ref(Ref&& other) +: m_ptr(&other.leakRef()) +{ +} + +template +Ref(Ref&& other) +: m_ptr(&other.leakRef()) +{ +} + +T* operator->() const { return m_ptr; } +T* ptr() const { return m_ptr; } +T& get() const { return *m_ptr; } +operator T&() const { return *m_ptr; } +bool operator!() const { return !*m_ptr; } + +T& leakRef() +{ +T& result = *m_ptr; +m_ptr = nullptr; +return result; +} + +private: +template friend inline Ref adoptRef(U&); + +enum AdoptTag { Adopt }; +Ref(T& object, AdoptTag) +: m_ptr(&object) +{ +} + +T* m_ptr; +}; + +class Node { +public: +static Ref create(); +virtual ~Node(); + +void ref() const; +void deref() const; + +protected: +explicit Node(); +}; + +void someFunction(Node*); + +void testFunction() +{ +Ref node = Node::create(); +someFunction(node.ptr()); +} ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] Fix an implicit cast to a base ref counted class generates a false positive. (PR #80934)
https://github.com/rniwa created https://github.com/llvm/llvm-project/pull/80934 The bug was caused by isRefCountable erroneously returning false for a class with both ref() and deref() functions defined because we were not resetting the base paths results between looking for "ref()" and "deref()" >From 377f096eda881b9a7c9b4d413081f11a3c4af62e Mon Sep 17 00:00:00 2001 From: Ryosuke Niwa Date: Tue, 6 Feb 2024 20:10:33 -0800 Subject: [PATCH] Fix an implicit cast to a base ref counted class generates a false positive. The bug was caused by isRefCountable erroneously returning false for a class with both ref() and deref() functions defined because we were not resetting the base paths results between looking for "ref()" and "deref()" --- .../Checkers/WebKit/PtrTypesSemantics.cpp | 1 + ...to-base-class-with-deref-in-superclass.cpp | 51 +++ 2 files changed, 52 insertions(+) create mode 100644 clang/test/Analysis/Checkers/WebKit/implicit-cast-to-base-class-with-deref-in-superclass.cpp diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp b/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp index d2b66341058000..0fd8afedc0b0f5 100644 --- a/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp @@ -84,6 +84,7 @@ std::optional isRefCountable(const CXXRecordDecl* R) if (AnyInconclusiveBase) return std::nullopt; + Paths.clear(); const auto hasPublicDerefInBase = [&AnyInconclusiveBase](const CXXBaseSpecifier *Base, CXXBasePath &) { auto hasDerefInBase = clang::hasPublicMethodInBase(Base, "deref"); diff --git a/clang/test/Analysis/Checkers/WebKit/implicit-cast-to-base-class-with-deref-in-superclass.cpp b/clang/test/Analysis/Checkers/WebKit/implicit-cast-to-base-class-with-deref-in-superclass.cpp new file mode 100644 index 00..49826c98a4610d --- /dev/null +++ b/clang/test/Analysis/Checkers/WebKit/implicit-cast-to-base-class-with-deref-in-superclass.cpp @@ -0,0 +1,51 @@ +// RUN: %clang_analyze_cc1 -analyzer-checker=alpha.webkit.UncountedCallArgsChecker -verify %s +// expected-no-diagnostics + +template +class Ref { +public: +~Ref() +{ +if (auto* ptr = m_ptr) +ptr->deref(); +m_ptr = nullptr; +} + +Ref(T& object) +: m_ptr(&object) +{ +object.ref(); +} + +operator T&() const { return *m_ptr; } +bool operator!() const { return !*m_ptr; } + +private: +T* m_ptr; +}; + +class Base { +public: +virtual ~Base(); +void ref() const; +void deref() const; +}; + +class Event : public Base { +protected: +explicit Event(); +}; + +class SubEvent : public Event { +public: +static Ref create(); +private: +SubEvent() = default; +}; + +void someFunction(Base&); + +static void test() +{ +someFunction(SubEvent::create()); +} ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] Fix an implicit cast to a base ref counted class generates a false positive. (PR #80934)
https://github.com/rniwa updated https://github.com/llvm/llvm-project/pull/80934 >From 9154815c48578df9ee384a9707dd79ee64259cea Mon Sep 17 00:00:00 2001 From: Ryosuke Niwa Date: Tue, 6 Feb 2024 20:10:33 -0800 Subject: [PATCH] [alpha.webkit.UncountedCallArgsChecker] Fix an implicit cast to a base ref counted class generates a false positive. The bug was caused by isRefCountable erroneously returning false for a class with both ref() and deref() functions defined because we were not resetting the base paths results between looking for "ref()" and "deref()" --- .../Checkers/WebKit/PtrTypesSemantics.cpp | 1 + ...to-base-class-with-deref-in-superclass.cpp | 51 +++ 2 files changed, 52 insertions(+) create mode 100644 clang/test/Analysis/Checkers/WebKit/implicit-cast-to-base-class-with-deref-in-superclass.cpp diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp b/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp index d2b66341058000..0fd8afedc0b0f5 100644 --- a/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp @@ -84,6 +84,7 @@ std::optional isRefCountable(const CXXRecordDecl* R) if (AnyInconclusiveBase) return std::nullopt; + Paths.clear(); const auto hasPublicDerefInBase = [&AnyInconclusiveBase](const CXXBaseSpecifier *Base, CXXBasePath &) { auto hasDerefInBase = clang::hasPublicMethodInBase(Base, "deref"); diff --git a/clang/test/Analysis/Checkers/WebKit/implicit-cast-to-base-class-with-deref-in-superclass.cpp b/clang/test/Analysis/Checkers/WebKit/implicit-cast-to-base-class-with-deref-in-superclass.cpp new file mode 100644 index 00..49826c98a4610d --- /dev/null +++ b/clang/test/Analysis/Checkers/WebKit/implicit-cast-to-base-class-with-deref-in-superclass.cpp @@ -0,0 +1,51 @@ +// RUN: %clang_analyze_cc1 -analyzer-checker=alpha.webkit.UncountedCallArgsChecker -verify %s +// expected-no-diagnostics + +template +class Ref { +public: +~Ref() +{ +if (auto* ptr = m_ptr) +ptr->deref(); +m_ptr = nullptr; +} + +Ref(T& object) +: m_ptr(&object) +{ +object.ref(); +} + +operator T&() const { return *m_ptr; } +bool operator!() const { return !*m_ptr; } + +private: +T* m_ptr; +}; + +class Base { +public: +virtual ~Base(); +void ref() const; +void deref() const; +}; + +class Event : public Base { +protected: +explicit Event(); +}; + +class SubEvent : public Event { +public: +static Ref create(); +private: +SubEvent() = default; +}; + +void someFunction(Base&); + +static void test() +{ +someFunction(SubEvent::create()); +} ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] Allow default arguments to be evaluated like other arguments. (PR #80956)
https://github.com/rniwa created https://github.com/llvm/llvm-project/pull/80956 This PR aligns the evaluation of default arguments with other kinds of arguments by extracting the expressions within them as argument values to be evaluated. >From 9d3f7377901539abeef949c1b33a99b1278900d8 Mon Sep 17 00:00:00 2001 From: Ryosuke Niwa Date: Wed, 7 Feb 2024 00:57:00 -0800 Subject: [PATCH] Allow default arguments to be evaluated like other arguments. This PR aligns the evaluation of default arguments with other kinds of arguments by extracting the expressions within them as argument values to be evaluated. --- .../WebKit/UncountedCallArgsChecker.cpp | 3 ++ .../ref-countable-default-arg-nullptr.cpp | 45 +++ 2 files changed, 48 insertions(+) create mode 100644 clang/test/Analysis/Checkers/WebKit/ref-countable-default-arg-nullptr.cpp diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedCallArgsChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedCallArgsChecker.cpp index 31ccae8b097b89..7cb0e4680d9e9e 100644 --- a/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedCallArgsChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedCallArgsChecker.cpp @@ -91,6 +91,9 @@ class UncountedCallArgsChecker const auto *Arg = CE->getArg(ArgIdx); +if (auto *defaultArg = dyn_cast(Arg)) + Arg = defaultArg->getExpr(); + std::pair ArgOrigin = tryToFindPtrOrigin(Arg, true); diff --git a/clang/test/Analysis/Checkers/WebKit/ref-countable-default-arg-nullptr.cpp b/clang/test/Analysis/Checkers/WebKit/ref-countable-default-arg-nullptr.cpp new file mode 100644 index 00..cd38b335dcf85e --- /dev/null +++ b/clang/test/Analysis/Checkers/WebKit/ref-countable-default-arg-nullptr.cpp @@ -0,0 +1,45 @@ +// RUN: %clang_analyze_cc1 -analyzer-checker=alpha.webkit.UncountedCallArgsChecker -verify %s + +template +class RefPtr { +public: + RefPtr(T* ptr) +: m_ptr(ptr) + { +if (m_ptr) + m_ptr->ref(); + } + + ~RefPtr() + { +if (m_ptr) + m_ptr->deref(); + } + + T* get() { return m_ptr; } + +private: + T* m_ptr; +}; + +class Obj { +public: + static Obj* get(); + static RefPtr create(); + void ref() const; + void deref() const; +}; + +void someFunction(Obj*, Obj* = nullptr); +void otherFunction(Obj*, Obj* = Obj::get()); +// expected-warning@-1{{Call argument is uncounted and unsafe [alpha.webkit.UncountedCallArgsChecker]}} +void anotherFunction(Obj*, Obj* = Obj::create().get()); + +void otherFunction() { + someFunction(nullptr); + someFunction(Obj::get()); + // expected-warning@-1{{Call argument is uncounted and unsafe [alpha.webkit.UncountedCallArgsChecker]}} + someFunction(Obj::create().get()); + otherFunction(nullptr); + anotherFunction(nullptr); +} ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] Allow default arguments to be evaluated like other arguments. (PR #80956)
https://github.com/rniwa updated https://github.com/llvm/llvm-project/pull/80956 >From bc390afd3a2e9de6e2a883205ce3862e45d26e06 Mon Sep 17 00:00:00 2001 From: Ryosuke Niwa Date: Wed, 7 Feb 2024 00:57:00 -0800 Subject: [PATCH] [alpha.webkit.UncountedCallArgsChecker] Allow default arguments to be evaluated like other arguments. This PR aligns the evaluation of default arguments with other kinds of arguments by extracting the expressions within them as argument values to be evaluated. --- .../WebKit/UncountedCallArgsChecker.cpp | 3 ++ .../ref-countable-default-arg-nullptr.cpp | 45 +++ 2 files changed, 48 insertions(+) create mode 100644 clang/test/Analysis/Checkers/WebKit/ref-countable-default-arg-nullptr.cpp diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedCallArgsChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedCallArgsChecker.cpp index 31ccae8b097b89..7cb0e4680d9e9e 100644 --- a/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedCallArgsChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedCallArgsChecker.cpp @@ -91,6 +91,9 @@ class UncountedCallArgsChecker const auto *Arg = CE->getArg(ArgIdx); +if (auto *defaultArg = dyn_cast(Arg)) + Arg = defaultArg->getExpr(); + std::pair ArgOrigin = tryToFindPtrOrigin(Arg, true); diff --git a/clang/test/Analysis/Checkers/WebKit/ref-countable-default-arg-nullptr.cpp b/clang/test/Analysis/Checkers/WebKit/ref-countable-default-arg-nullptr.cpp new file mode 100644 index 00..cd38b335dcf85e --- /dev/null +++ b/clang/test/Analysis/Checkers/WebKit/ref-countable-default-arg-nullptr.cpp @@ -0,0 +1,45 @@ +// RUN: %clang_analyze_cc1 -analyzer-checker=alpha.webkit.UncountedCallArgsChecker -verify %s + +template +class RefPtr { +public: + RefPtr(T* ptr) +: m_ptr(ptr) + { +if (m_ptr) + m_ptr->ref(); + } + + ~RefPtr() + { +if (m_ptr) + m_ptr->deref(); + } + + T* get() { return m_ptr; } + +private: + T* m_ptr; +}; + +class Obj { +public: + static Obj* get(); + static RefPtr create(); + void ref() const; + void deref() const; +}; + +void someFunction(Obj*, Obj* = nullptr); +void otherFunction(Obj*, Obj* = Obj::get()); +// expected-warning@-1{{Call argument is uncounted and unsafe [alpha.webkit.UncountedCallArgsChecker]}} +void anotherFunction(Obj*, Obj* = Obj::create().get()); + +void otherFunction() { + someFunction(nullptr); + someFunction(Obj::get()); + // expected-warning@-1{{Call argument is uncounted and unsafe [alpha.webkit.UncountedCallArgsChecker]}} + someFunction(Obj::create().get()); + otherFunction(nullptr); + anotherFunction(nullptr); +} ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] Add the support for calling Ref::ptr accessor. (PR #80919)
@@ -0,0 +1,86 @@ +// RUN: %clang_analyze_cc1 -analyzer-checker=alpha.webkit.UncountedCallArgsChecker -verify %s +// expected-no-diagnostics + +template +class Ref { rniwa wrote: Sure. https://github.com/llvm/llvm-project/pull/80919 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] Add the support for calling Ref::ptr accessor. (PR #80919)
https://github.com/rniwa updated https://github.com/llvm/llvm-project/pull/80919 >From 26f23953843b7c79664c7e721c3cbf130be405c0 Mon Sep 17 00:00:00 2001 From: Ryosuke Niwa Date: Tue, 6 Feb 2024 17:56:01 -0800 Subject: [PATCH] [alpha.webkit.UncountedCallArgsChecker] Add the support for calling Ref::ptr accessor. This accessor returns a pointer from Ref type and is therefore safe. --- .../Checkers/WebKit/PtrTypesSemantics.cpp | 1 + .../Checkers/WebKit/ref-ptr-accessor.cpp | 24 +++ clang/test/Analysis/Checkers/WebKit/ref.h | 63 +++ 3 files changed, 88 insertions(+) create mode 100644 clang/test/Analysis/Checkers/WebKit/ref-ptr-accessor.cpp create mode 100644 clang/test/Analysis/Checkers/WebKit/ref.h diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp b/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp index c1f180f31338cb..4bc3f7a8c9c3ce 100644 --- a/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp @@ -148,6 +148,7 @@ std::optional isGetterOfRefCounted(const CXXMethodDecl* M) if (((className == "Ref" || className == "RefPtr") && methodName == "get") || +(className == "Ref" && methodName == "ptr") || ((className == "String" || className == "AtomString" || className == "AtomStringImpl" || className == "UniqueString" || className == "UniqueStringImpl" || className == "Identifier") && diff --git a/clang/test/Analysis/Checkers/WebKit/ref-ptr-accessor.cpp b/clang/test/Analysis/Checkers/WebKit/ref-ptr-accessor.cpp new file mode 100644 index 00..1cd9bfb38c3b97 --- /dev/null +++ b/clang/test/Analysis/Checkers/WebKit/ref-ptr-accessor.cpp @@ -0,0 +1,24 @@ +// RUN: %clang_analyze_cc1 -analyzer-checker=alpha.webkit.UncountedCallArgsChecker -verify %s +// expected-no-diagnostics + +#include "ref.h" + +class Node { +public: +static Ref create(); +virtual ~Node(); + +void ref() const; +void deref() const; + +protected: +explicit Node(); +}; + +void someFunction(Node*); + +void testFunction() +{ +Ref node = Node::create(); +someFunction(node.ptr()); +} diff --git a/clang/test/Analysis/Checkers/WebKit/ref.h b/clang/test/Analysis/Checkers/WebKit/ref.h new file mode 100644 index 00..051a0447391bef --- /dev/null +++ b/clang/test/Analysis/Checkers/WebKit/ref.h @@ -0,0 +1,63 @@ +template +class Ref { +public: +~Ref() +{ +if (auto* ptr = m_ptr) +ptr->deref(); +m_ptr = nullptr; +} + +Ref(T& object) +: m_ptr(&object) +{ +object.ref(); +} + +Ref(const Ref& other) +: m_ptr(other.ptr()) +{ +m_ptr->ref(); +} + +template Ref(const Ref& other) +: m_ptr(other.ptr()) +{ +m_ptr->ref(); +} + +Ref(Ref&& other) +: m_ptr(&other.leakRef()) +{ +} + +template +Ref(Ref&& other) +: m_ptr(&other.leakRef()) +{ +} + +T* operator->() const { return m_ptr; } +T* ptr() const { return m_ptr; } +T& get() const { return *m_ptr; } +operator T&() const { return *m_ptr; } +bool operator!() const { return !*m_ptr; } + +T& leakRef() +{ +T& result = *m_ptr; +m_ptr = nullptr; +return result; +} + +private: +template friend inline Ref adoptRef(U&); + +enum AdoptTag { Adopt }; +Ref(T& object, AdoptTag) +: m_ptr(&object) +{ +} + +T* m_ptr; +}; ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] Add the support for calling Ref::ptr accessor. (PR #80919)
https://github.com/rniwa updated https://github.com/llvm/llvm-project/pull/80919 >From 6476d4cbca68815a96cea56519e3f5c75b4f9738 Mon Sep 17 00:00:00 2001 From: Ryosuke Niwa Date: Tue, 6 Feb 2024 17:56:01 -0800 Subject: [PATCH] [alpha.webkit.UncountedCallArgsChecker] Add the support for calling Ref::ptr accessor. This accessor returns a pointer from Ref type and is therefore safe. --- .../Checkers/WebKit/PtrTypesSemantics.cpp| 1 + clang/test/Analysis/Checkers/WebKit/mock-types.h | 10 ++ .../Analysis/Checkers/WebKit/ref-ptr-accessor.cpp| 12 3 files changed, 19 insertions(+), 4 deletions(-) create mode 100644 clang/test/Analysis/Checkers/WebKit/ref-ptr-accessor.cpp diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp b/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp index c1f180f31338cb..4bc3f7a8c9c3ce 100644 --- a/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp @@ -148,6 +148,7 @@ std::optional isGetterOfRefCounted(const CXXMethodDecl* M) if (((className == "Ref" || className == "RefPtr") && methodName == "get") || +(className == "Ref" && methodName == "ptr") || ((className == "String" || className == "AtomString" || className == "AtomStringImpl" || className == "UniqueString" || className == "UniqueStringImpl" || className == "Identifier") && diff --git a/clang/test/Analysis/Checkers/WebKit/mock-types.h b/clang/test/Analysis/Checkers/WebKit/mock-types.h index 5f570b8bee8cb8..f10d530e1109ef 100644 --- a/clang/test/Analysis/Checkers/WebKit/mock-types.h +++ b/clang/test/Analysis/Checkers/WebKit/mock-types.h @@ -2,13 +2,14 @@ #define mock_types_1103988513531 template struct Ref { - T t; + T *t; Ref() : t{} {}; Ref(T *) {} - T *get() { return nullptr; } - operator const T &() const { return t; } - operator T &() { return t; } + T *get() { return t; } + T *ptr() { return t; } + operator const T &() const { return *t; } + operator T &() { return *t; } }; template struct RefPtr { @@ -39,6 +40,7 @@ template bool operator!=(const RefPtr &, T *) { return false; } template bool operator!=(const RefPtr &, T &) { return false; } struct RefCountable { + static Ref create(); void ref() {} void deref() {} }; diff --git a/clang/test/Analysis/Checkers/WebKit/ref-ptr-accessor.cpp b/clang/test/Analysis/Checkers/WebKit/ref-ptr-accessor.cpp new file mode 100644 index 00..560dcfd4bdb094 --- /dev/null +++ b/clang/test/Analysis/Checkers/WebKit/ref-ptr-accessor.cpp @@ -0,0 +1,12 @@ +// RUN: %clang_analyze_cc1 -analyzer-checker=alpha.webkit.UncountedCallArgsChecker -verify %s +// expected-no-diagnostics + +#include "mock-types.h" + +void someFunction(RefCountable*); + +void testFunction() +{ +Ref item = RefCountable::create(); +someFunction(item.ptr()); +} ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] Ignore assignment to Ref / RefPtr in alpha.webkit.UncountedCallArgsChecker. (PR #80810)
https://github.com/rniwa updated https://github.com/llvm/llvm-project/pull/80810 >From e179bbef69084caac3948977a7091332c69130f5 Mon Sep 17 00:00:00 2001 From: Ryosuke Niwa Date: Tue, 6 Feb 2024 01:13:34 -0800 Subject: [PATCH 1/2] Ignore assignment to Ref / RefPtr in alpha.webkit.UncountedCallArgsChecker. --- .../WebKit/UncountedCallArgsChecker.cpp | 10 +++ .../Checkers/WebKit/assignment-to-refptr.cpp | 71 +++ 2 files changed, 81 insertions(+) create mode 100644 clang/test/Analysis/Checkers/WebKit/assignment-to-refptr.cpp diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedCallArgsChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedCallArgsChecker.cpp index 407b6ba7a76428..419140bf12b66c 100644 --- a/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedCallArgsChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedCallArgsChecker.cpp @@ -126,6 +126,16 @@ class UncountedCallArgsChecker // of object on LHS. if (auto *MemberOp = dyn_cast(CE)) { // Note: assignemnt to built-in type isn't derived from CallExpr. + if (MemberOp->getOperator() == + OO_Equal) { // Ignore assignment to Ref/RefPtr. +auto *callee = MemberOp->getDirectCallee(); +if (auto *calleeDecl = dyn_cast(callee)) { + if (const CXXRecordDecl *classDecl = calleeDecl->getParent()) { +if (isRefCounted(classDecl)) + return true; + } +} + } if (MemberOp->isAssignmentOp()) return false; } diff --git a/clang/test/Analysis/Checkers/WebKit/assignment-to-refptr.cpp b/clang/test/Analysis/Checkers/WebKit/assignment-to-refptr.cpp new file mode 100644 index 00..c8ad634a5493f5 --- /dev/null +++ b/clang/test/Analysis/Checkers/WebKit/assignment-to-refptr.cpp @@ -0,0 +1,71 @@ +// RUN: %clang_analyze_cc1 -analyzer-checker=alpha.webkit.UncountedCallArgsChecker -verify %s +// expected-no-diagnostics + +template +class RefPtr { +public: +inline constexpr RefPtr() : m_ptr(nullptr) { } +inline RefPtr(T* ptr) + : m_ptr(ptr) +{ +if (m_ptr) +m_ptr->ref(); +} + +inline RefPtr(const RefPtr& o) +: m_ptr(o.m_ptr) +{ +if (m_ptr) +m_ptr->ref(); +} + +inline ~RefPtr() +{ +if (m_ptr) +m_ptr->deref(); +} + +inline T* operator->() const { return m_ptr; } +explicit operator bool() const { return m_ptr; } + +RefPtr& operator=(const RefPtr&); +RefPtr& operator=(T*); + +private: +T* m_ptr; +}; + +template +inline RefPtr& RefPtr::operator=(const RefPtr& o) +{ +if (m_ptr) +m_ptr->deref(); +m_ptr = o.m_ptr; +if (m_ptr) +m_ptr->ref(); +return *this; +} + +template +inline RefPtr& RefPtr::operator=(T* optr) +{ +if (m_ptr) +m_ptr->deref(); +m_ptr = optr; +if (m_ptr) +m_ptr->ref(); +return *this; +} + +class Node { +public: +Node* nextSibling() const; + +void ref() const; +void deref() const; +}; + +static void removeDetachedChildren(Node* firstChild) +{ +for (RefPtr child = firstChild; child; child = child->nextSibling()); +} >From 6eded7d9da598f15647cb25fc6ac023b27d16132 Mon Sep 17 00:00:00 2001 From: Ryosuke Niwa Date: Fri, 9 Feb 2024 15:17:23 -0800 Subject: [PATCH 2/2] Use mock-types.h instead of embedding RefPtr definiton in the test. --- .../Checkers/WebKit/assignment-to-refptr.cpp | 56 +-- .../Analysis/Checkers/WebKit/mock-types.h | 1 + 2 files changed, 2 insertions(+), 55 deletions(-) diff --git a/clang/test/Analysis/Checkers/WebKit/assignment-to-refptr.cpp b/clang/test/Analysis/Checkers/WebKit/assignment-to-refptr.cpp index c8ad634a5493f5..8b2b4671ed96b1 100644 --- a/clang/test/Analysis/Checkers/WebKit/assignment-to-refptr.cpp +++ b/clang/test/Analysis/Checkers/WebKit/assignment-to-refptr.cpp @@ -1,61 +1,7 @@ // RUN: %clang_analyze_cc1 -analyzer-checker=alpha.webkit.UncountedCallArgsChecker -verify %s // expected-no-diagnostics -template -class RefPtr { -public: -inline constexpr RefPtr() : m_ptr(nullptr) { } -inline RefPtr(T* ptr) - : m_ptr(ptr) -{ -if (m_ptr) -m_ptr->ref(); -} - -inline RefPtr(const RefPtr& o) -: m_ptr(o.m_ptr) -{ -if (m_ptr) -m_ptr->ref(); -} - -inline ~RefPtr() -{ -if (m_ptr) -m_ptr->deref(); -} - -inline T* operator->() const { return m_ptr; } -explicit operator bool() const { return m_ptr; } - -RefPtr& operator=(const RefPtr&); -RefPtr& operator=(T*); - -private: -T* m_ptr; -}; - -template -inline RefPtr& RefPtr::operator=(const RefPtr& o) -{ -if (m_ptr) -m_ptr->deref(); -m_ptr = o.m_ptr; -if (m_ptr) -m_ptr->ref(); -return *this; -} - -template -inline RefPtr& RefPtr::operator=(T* optr) -{ -if (m_ptr) -m_ptr->deref(); -m_ptr = optr; -
[clang] Fix an implicit cast to a base ref counted class generates a false positive. (PR #80934)
https://github.com/rniwa updated https://github.com/llvm/llvm-project/pull/80934 >From 9154815c48578df9ee384a9707dd79ee64259cea Mon Sep 17 00:00:00 2001 From: Ryosuke Niwa Date: Tue, 6 Feb 2024 20:10:33 -0800 Subject: [PATCH 1/2] [alpha.webkit.UncountedCallArgsChecker] Fix an implicit cast to a base ref counted class generates a false positive. The bug was caused by isRefCountable erroneously returning false for a class with both ref() and deref() functions defined because we were not resetting the base paths results between looking for "ref()" and "deref()" --- .../Checkers/WebKit/PtrTypesSemantics.cpp | 1 + ...to-base-class-with-deref-in-superclass.cpp | 51 +++ 2 files changed, 52 insertions(+) create mode 100644 clang/test/Analysis/Checkers/WebKit/implicit-cast-to-base-class-with-deref-in-superclass.cpp diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp b/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp index d2b66341058000..0fd8afedc0b0f5 100644 --- a/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp @@ -84,6 +84,7 @@ std::optional isRefCountable(const CXXRecordDecl* R) if (AnyInconclusiveBase) return std::nullopt; + Paths.clear(); const auto hasPublicDerefInBase = [&AnyInconclusiveBase](const CXXBaseSpecifier *Base, CXXBasePath &) { auto hasDerefInBase = clang::hasPublicMethodInBase(Base, "deref"); diff --git a/clang/test/Analysis/Checkers/WebKit/implicit-cast-to-base-class-with-deref-in-superclass.cpp b/clang/test/Analysis/Checkers/WebKit/implicit-cast-to-base-class-with-deref-in-superclass.cpp new file mode 100644 index 00..49826c98a4610d --- /dev/null +++ b/clang/test/Analysis/Checkers/WebKit/implicit-cast-to-base-class-with-deref-in-superclass.cpp @@ -0,0 +1,51 @@ +// RUN: %clang_analyze_cc1 -analyzer-checker=alpha.webkit.UncountedCallArgsChecker -verify %s +// expected-no-diagnostics + +template +class Ref { +public: +~Ref() +{ +if (auto* ptr = m_ptr) +ptr->deref(); +m_ptr = nullptr; +} + +Ref(T& object) +: m_ptr(&object) +{ +object.ref(); +} + +operator T&() const { return *m_ptr; } +bool operator!() const { return !*m_ptr; } + +private: +T* m_ptr; +}; + +class Base { +public: +virtual ~Base(); +void ref() const; +void deref() const; +}; + +class Event : public Base { +protected: +explicit Event(); +}; + +class SubEvent : public Event { +public: +static Ref create(); +private: +SubEvent() = default; +}; + +void someFunction(Base&); + +static void test() +{ +someFunction(SubEvent::create()); +} >From c4e5d9c747b021201c5cdb7d3073f6ec358418e5 Mon Sep 17 00:00:00 2001 From: Ryosuke Niwa Date: Fri, 9 Feb 2024 15:27:18 -0800 Subject: [PATCH 2/2] Use mock-types.h instead of embedding the defintion of Ref in the test. --- ...to-base-class-with-deref-in-superclass.cpp | 23 +-- 1 file changed, 1 insertion(+), 22 deletions(-) diff --git a/clang/test/Analysis/Checkers/WebKit/implicit-cast-to-base-class-with-deref-in-superclass.cpp b/clang/test/Analysis/Checkers/WebKit/implicit-cast-to-base-class-with-deref-in-superclass.cpp index 49826c98a4610d..176238f31bd2e4 100644 --- a/clang/test/Analysis/Checkers/WebKit/implicit-cast-to-base-class-with-deref-in-superclass.cpp +++ b/clang/test/Analysis/Checkers/WebKit/implicit-cast-to-base-class-with-deref-in-superclass.cpp @@ -1,28 +1,7 @@ // RUN: %clang_analyze_cc1 -analyzer-checker=alpha.webkit.UncountedCallArgsChecker -verify %s // expected-no-diagnostics -template -class Ref { -public: -~Ref() -{ -if (auto* ptr = m_ptr) -ptr->deref(); -m_ptr = nullptr; -} - -Ref(T& object) -: m_ptr(&object) -{ -object.ref(); -} - -operator T&() const { return *m_ptr; } -bool operator!() const { return !*m_ptr; } - -private: -T* m_ptr; -}; +#include "mock-types.h" class Base { public: ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] Allow default arguments to be evaluated like other arguments. (PR #80956)
https://github.com/rniwa updated https://github.com/llvm/llvm-project/pull/80956 >From bc390afd3a2e9de6e2a883205ce3862e45d26e06 Mon Sep 17 00:00:00 2001 From: Ryosuke Niwa Date: Wed, 7 Feb 2024 00:57:00 -0800 Subject: [PATCH 1/2] [alpha.webkit.UncountedCallArgsChecker] Allow default arguments to be evaluated like other arguments. This PR aligns the evaluation of default arguments with other kinds of arguments by extracting the expressions within them as argument values to be evaluated. --- .../WebKit/UncountedCallArgsChecker.cpp | 3 ++ .../ref-countable-default-arg-nullptr.cpp | 45 +++ 2 files changed, 48 insertions(+) create mode 100644 clang/test/Analysis/Checkers/WebKit/ref-countable-default-arg-nullptr.cpp diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedCallArgsChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedCallArgsChecker.cpp index 31ccae8b097b89..7cb0e4680d9e9e 100644 --- a/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedCallArgsChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedCallArgsChecker.cpp @@ -91,6 +91,9 @@ class UncountedCallArgsChecker const auto *Arg = CE->getArg(ArgIdx); +if (auto *defaultArg = dyn_cast(Arg)) + Arg = defaultArg->getExpr(); + std::pair ArgOrigin = tryToFindPtrOrigin(Arg, true); diff --git a/clang/test/Analysis/Checkers/WebKit/ref-countable-default-arg-nullptr.cpp b/clang/test/Analysis/Checkers/WebKit/ref-countable-default-arg-nullptr.cpp new file mode 100644 index 00..cd38b335dcf85e --- /dev/null +++ b/clang/test/Analysis/Checkers/WebKit/ref-countable-default-arg-nullptr.cpp @@ -0,0 +1,45 @@ +// RUN: %clang_analyze_cc1 -analyzer-checker=alpha.webkit.UncountedCallArgsChecker -verify %s + +template +class RefPtr { +public: + RefPtr(T* ptr) +: m_ptr(ptr) + { +if (m_ptr) + m_ptr->ref(); + } + + ~RefPtr() + { +if (m_ptr) + m_ptr->deref(); + } + + T* get() { return m_ptr; } + +private: + T* m_ptr; +}; + +class Obj { +public: + static Obj* get(); + static RefPtr create(); + void ref() const; + void deref() const; +}; + +void someFunction(Obj*, Obj* = nullptr); +void otherFunction(Obj*, Obj* = Obj::get()); +// expected-warning@-1{{Call argument is uncounted and unsafe [alpha.webkit.UncountedCallArgsChecker]}} +void anotherFunction(Obj*, Obj* = Obj::create().get()); + +void otherFunction() { + someFunction(nullptr); + someFunction(Obj::get()); + // expected-warning@-1{{Call argument is uncounted and unsafe [alpha.webkit.UncountedCallArgsChecker]}} + someFunction(Obj::create().get()); + otherFunction(nullptr); + anotherFunction(nullptr); +} >From 4ca232820a5789155cdef2aa9768d715c3a874b8 Mon Sep 17 00:00:00 2001 From: Ryosuke Niwa Date: Fri, 9 Feb 2024 15:55:50 -0800 Subject: [PATCH 2/2] Use "mock-types.h" instead of embedding RefPtr in the test. --- .../ref-countable-default-arg-nullptr.cpp | 22 +-- 1 file changed, 1 insertion(+), 21 deletions(-) diff --git a/clang/test/Analysis/Checkers/WebKit/ref-countable-default-arg-nullptr.cpp b/clang/test/Analysis/Checkers/WebKit/ref-countable-default-arg-nullptr.cpp index cd38b335dcf85e..a1860a5434c864 100644 --- a/clang/test/Analysis/Checkers/WebKit/ref-countable-default-arg-nullptr.cpp +++ b/clang/test/Analysis/Checkers/WebKit/ref-countable-default-arg-nullptr.cpp @@ -1,26 +1,6 @@ // RUN: %clang_analyze_cc1 -analyzer-checker=alpha.webkit.UncountedCallArgsChecker -verify %s -template -class RefPtr { -public: - RefPtr(T* ptr) -: m_ptr(ptr) - { -if (m_ptr) - m_ptr->ref(); - } - - ~RefPtr() - { -if (m_ptr) - m_ptr->deref(); - } - - T* get() { return m_ptr; } - -private: - T* m_ptr; -}; +#include "mock-types.h" class Obj { public: ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [alpha.webkit.UncountedCallArgsChecker] Check the safety of the object argument in a member function call. (PR #81400)
https://github.com/rniwa created https://github.com/llvm/llvm-project/pull/81400 This PR makes alpha.webkit.UncountedCallArgsChecker eplicitly check the safety of the object argument in a member function call. It also removes the exemption of local variables from this checker so that each local variable's safety is checked if it's used in a function call instead of relying on the local variable checker to find those since local variable checker currently has exemption for "for" and "if" statements. >From b7121ce4f2ef69b4a410f2399fbd9d9525156b93 Mon Sep 17 00:00:00 2001 From: Ryosuke Niwa Date: Sun, 11 Feb 2024 00:07:30 -0800 Subject: [PATCH] [alpha.webkit.UncountedCallArgsChecker] Check the safety of the object argument in a member function call. This PR makes alpha.webkit.UncountedCallArgsChecker eplicitly check the safety of the object argument in a member function call. It also removes the exemption of local variables from this checker so that each local variable's safety is checked if it's used in a function call instead of relying on the local variable checker to find those since local variable checker currently has exemption for "for" and "if" statements. --- .../Checkers/WebKit/ASTUtils.cpp | 2 +- .../WebKit/UncountedCallArgsChecker.cpp | 68 +-- .../WebKit/call-args-inside-if-statement.cpp | 20 ++ 3 files changed, 69 insertions(+), 21 deletions(-) create mode 100644 clang/test/Analysis/Checkers/WebKit/call-args-inside-if-statement.cpp diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/ASTUtils.cpp b/clang/lib/StaticAnalyzer/Checkers/WebKit/ASTUtils.cpp index 4526fac64735bf..da0d52e361c946 100644 --- a/clang/lib/StaticAnalyzer/Checkers/WebKit/ASTUtils.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/ASTUtils.cpp @@ -85,7 +85,7 @@ 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()) + if (isa(D)) return true; } } diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedCallArgsChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedCallArgsChecker.cpp index 31ccae8b097b89..fa6aeb4741d0b7 100644 --- a/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedCallArgsChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedCallArgsChecker.cpp @@ -70,6 +70,14 @@ class UncountedCallArgsChecker // or std::function call operator). unsigned ArgIdx = isa(CE) && isa_and_nonnull(F); + if (auto* MemberCallExpr = dyn_cast(CE)) { +auto *E = MemberCallExpr->getImplicitObjectArgument(); +auto *ArgType = MemberCallExpr->getObjectType().getTypePtrOrNull(); +std::optional IsUncounted = isUncounted(ArgType->getAsCXXRecordDecl()); +if (IsUncounted && *IsUncounted && !isPtrOriginSafe(E)) + reportBugOnThis(E); + } + for (auto P = F->param_begin(); // FIXME: Also check variadic function parameters. // FIXME: Also check default function arguments. Probably a different @@ -90,32 +98,36 @@ class UncountedCallArgsChecker continue; const auto *Arg = CE->getArg(ArgIdx); - -std::pair ArgOrigin = -tryToFindPtrOrigin(Arg, true); - -// Temporary ref-counted object created as part of the call argument -// would outlive the call. -if (ArgOrigin.second) - continue; - -if (isa(ArgOrigin.first)) { - // foo(nullptr) - continue; -} -if (isa(ArgOrigin.first)) { - // FIXME: Check the value. - // foo(NULL) - continue; -} - -if (isASafeCallArg(ArgOrigin.first)) + +if (isPtrOriginSafe(Arg)) continue; reportBug(Arg, *P); } } } + + bool isPtrOriginSafe(const Expr *Arg) const { +std::pair ArgOrigin = +tryToFindPtrOrigin(Arg, true); + +// Temporary ref-counted object created as part of the call argument +// would outlive the call. +if (ArgOrigin.second) + return true; + +if (isa(ArgOrigin.first)) { + // foo(nullptr) + return true; +} +if (isa(ArgOrigin.first)) { + // FIXME: Check the value. + // foo(NULL) + return true; +} + +return isASafeCallArg(ArgOrigin.first); + } bool shouldSkipCall(const CallExpr *CE) const { if (CE->getNumArgs() == 0) @@ -183,6 +195,22 @@ class UncountedCallArgsChecker Report->addRange(CallArg->getSourceRange()); BR->emitReport(std::move(Report)); } + + void reportBugOnThis(const Expr *CallArg) const { +assert(CallArg); + +SmallString<100> Buf; +llvm::raw_svector_ostream Os(Buf); + +Os << "Call argument for 'this' parameter is uncounted and unsafe."; + +const SourceLocation SrcLocToReport = CallArg->getSourceRange().getBegin(); + +PathDiagnosticLocation
[clang] [alpha.webkit.UncountedCallArgsChecker] Check the safety of the object argument in a member function call. (PR #81400)
https://github.com/rniwa updated https://github.com/llvm/llvm-project/pull/81400 >From 16d5c240639cdf25b824f5a1a60c256d33fe3565 Mon Sep 17 00:00:00 2001 From: Ryosuke Niwa Date: Sun, 11 Feb 2024 00:07:30 -0800 Subject: [PATCH] [alpha.webkit.UncountedCallArgsChecker] Check the safety of the object argument in a member function call. This PR makes alpha.webkit.UncountedCallArgsChecker eplicitly check the safety of the object argument in a member function call. It also removes the exemption of local variables from this checker so that each local variable's safety is checked if it's used in a function call instead of relying on the local variable checker to find those since local variable checker currently has exemption for "for" and "if" statements. --- .../Checkers/WebKit/ASTUtils.cpp | 2 +- .../WebKit/UncountedCallArgsChecker.cpp | 66 +-- .../WebKit/call-args-inside-if-statement.cpp | 20 ++ 3 files changed, 68 insertions(+), 20 deletions(-) create mode 100644 clang/test/Analysis/Checkers/WebKit/call-args-inside-if-statement.cpp diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/ASTUtils.cpp b/clang/lib/StaticAnalyzer/Checkers/WebKit/ASTUtils.cpp index 4526fac64735bf..da0d52e361c946 100644 --- a/clang/lib/StaticAnalyzer/Checkers/WebKit/ASTUtils.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/ASTUtils.cpp @@ -85,7 +85,7 @@ 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()) + if (isa(D)) return true; } } diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedCallArgsChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedCallArgsChecker.cpp index 31ccae8b097b89..170ff7f531f9e9 100644 --- a/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedCallArgsChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedCallArgsChecker.cpp @@ -70,6 +70,14 @@ class UncountedCallArgsChecker // or std::function call operator). unsigned ArgIdx = isa(CE) && isa_and_nonnull(F); + if (auto *MemberCallExpr = dyn_cast(CE)) { +auto *E = MemberCallExpr->getImplicitObjectArgument(); +auto *ArgType = MemberCallExpr->getObjectType().getTypePtrOrNull(); +std::optional IsUncounted = isUncounted(ArgType->getAsCXXRecordDecl()); +if (IsUncounted && *IsUncounted && !isPtrOriginSafe(E)) + reportBugOnThis(E); + } + for (auto P = F->param_begin(); // FIXME: Also check variadic function parameters. // FIXME: Also check default function arguments. Probably a different @@ -91,25 +99,7 @@ class UncountedCallArgsChecker const auto *Arg = CE->getArg(ArgIdx); -std::pair ArgOrigin = -tryToFindPtrOrigin(Arg, true); - -// Temporary ref-counted object created as part of the call argument -// would outlive the call. -if (ArgOrigin.second) - continue; - -if (isa(ArgOrigin.first)) { - // foo(nullptr) - continue; -} -if (isa(ArgOrigin.first)) { - // FIXME: Check the value. - // foo(NULL) - continue; -} - -if (isASafeCallArg(ArgOrigin.first)) +if (isPtrOriginSafe(Arg)) continue; reportBug(Arg, *P); @@ -117,6 +107,28 @@ class UncountedCallArgsChecker } } + bool isPtrOriginSafe(const Expr *Arg) const { +std::pair ArgOrigin = +tryToFindPtrOrigin(Arg, true); + +// Temporary ref-counted object created as part of the call argument +// would outlive the call. +if (ArgOrigin.second) + return true; + +if (isa(ArgOrigin.first)) { + // foo(nullptr) + return true; +} +if (isa(ArgOrigin.first)) { + // FIXME: Check the value. + // foo(NULL) + return true; +} + +return isASafeCallArg(ArgOrigin.first); + } + bool shouldSkipCall(const CallExpr *CE) const { if (CE->getNumArgs() == 0) return false; @@ -183,6 +195,22 @@ class UncountedCallArgsChecker Report->addRange(CallArg->getSourceRange()); BR->emitReport(std::move(Report)); } + + void reportBugOnThis(const Expr *CallArg) const { +assert(CallArg); + +SmallString<100> Buf; +llvm::raw_svector_ostream Os(Buf); + +Os << "Call argument for 'this' parameter is uncounted and unsafe."; + +const SourceLocation SrcLocToReport = CallArg->getSourceRange().getBegin(); + +PathDiagnosticLocation BSLoc(SrcLocToReport, BR->getSourceManager()); +auto Report = std::make_unique(Bug, Os.str(), BSLoc); +Report->addRange(CallArg->getSourceRange()); +BR->emitReport(std::move(Report)); + } }; } // namespace diff --git a/clang/test/Analysis/Checkers/WebKit/call-args-inside-if-statement.cpp b/clang/test/Analysis/Checkers/WebKit/call-args-inside-if-statement.cpp new file mode 10064
[clang] [alpha.webkit.UncountedCallArgsChecker] Check the safety of the object argument in a member function call. (PR #81400)
https://github.com/rniwa updated https://github.com/llvm/llvm-project/pull/81400 >From c8ac9ad6ecd95a3dbd023458a572b08a4664de03 Mon Sep 17 00:00:00 2001 From: Ryosuke Niwa Date: Sun, 11 Feb 2024 00:07:30 -0800 Subject: [PATCH] [alpha.webkit.UncountedCallArgsChecker] Check the safety of the object argument in a member function call. This PR makes alpha.webkit.UncountedCallArgsChecker eplicitly check the safety of the object argument in a member function call. It also removes the exemption of local variables from this checker so that each local variable's safety is checked if it's used in a function call instead of relying on the local variable checker to find those since local variable checker currently has exemption for "for" and "if" statements. --- .../Checkers/WebKit/ASTUtils.cpp | 2 +- .../WebKit/UncountedCallArgsChecker.cpp | 67 +-- .../WebKit/call-args-inside-if-statement.cpp | 20 ++ 3 files changed, 69 insertions(+), 20 deletions(-) create mode 100644 clang/test/Analysis/Checkers/WebKit/call-args-inside-if-statement.cpp diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/ASTUtils.cpp b/clang/lib/StaticAnalyzer/Checkers/WebKit/ASTUtils.cpp index 4526fac64735bf..da0d52e361c946 100644 --- a/clang/lib/StaticAnalyzer/Checkers/WebKit/ASTUtils.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/ASTUtils.cpp @@ -85,7 +85,7 @@ 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()) + if (isa(D)) return true; } } diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedCallArgsChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedCallArgsChecker.cpp index 31ccae8b097b89..cda96b70ea8735 100644 --- a/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedCallArgsChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedCallArgsChecker.cpp @@ -70,6 +70,15 @@ class UncountedCallArgsChecker // or std::function call operator). unsigned ArgIdx = isa(CE) && isa_and_nonnull(F); + if (auto *MemberCallExpr = dyn_cast(CE)) { +auto *E = MemberCallExpr->getImplicitObjectArgument(); +auto *ArgType = MemberCallExpr->getObjectType().getTypePtrOrNull(); +std::optional IsUncounted = +isUncounted(ArgType->getAsCXXRecordDecl()); +if (IsUncounted && *IsUncounted && !isPtrOriginSafe(E)) + reportBugOnThis(E); + } + for (auto P = F->param_begin(); // FIXME: Also check variadic function parameters. // FIXME: Also check default function arguments. Probably a different @@ -91,25 +100,7 @@ class UncountedCallArgsChecker const auto *Arg = CE->getArg(ArgIdx); -std::pair ArgOrigin = -tryToFindPtrOrigin(Arg, true); - -// Temporary ref-counted object created as part of the call argument -// would outlive the call. -if (ArgOrigin.second) - continue; - -if (isa(ArgOrigin.first)) { - // foo(nullptr) - continue; -} -if (isa(ArgOrigin.first)) { - // FIXME: Check the value. - // foo(NULL) - continue; -} - -if (isASafeCallArg(ArgOrigin.first)) +if (isPtrOriginSafe(Arg)) continue; reportBug(Arg, *P); @@ -117,6 +108,28 @@ class UncountedCallArgsChecker } } + bool isPtrOriginSafe(const Expr *Arg) const { +std::pair ArgOrigin = +tryToFindPtrOrigin(Arg, true); + +// Temporary ref-counted object created as part of the call argument +// would outlive the call. +if (ArgOrigin.second) + return true; + +if (isa(ArgOrigin.first)) { + // foo(nullptr) + return true; +} +if (isa(ArgOrigin.first)) { + // FIXME: Check the value. + // foo(NULL) + return true; +} + +return isASafeCallArg(ArgOrigin.first); + } + bool shouldSkipCall(const CallExpr *CE) const { if (CE->getNumArgs() == 0) return false; @@ -183,6 +196,22 @@ class UncountedCallArgsChecker Report->addRange(CallArg->getSourceRange()); BR->emitReport(std::move(Report)); } + + void reportBugOnThis(const Expr *CallArg) const { +assert(CallArg); + +SmallString<100> Buf; +llvm::raw_svector_ostream Os(Buf); + +Os << "Call argument for 'this' parameter is uncounted and unsafe."; + +const SourceLocation SrcLocToReport = CallArg->getSourceRange().getBegin(); + +PathDiagnosticLocation BSLoc(SrcLocToReport, BR->getSourceManager()); +auto Report = std::make_unique(Bug, Os.str(), BSLoc); +Report->addRange(CallArg->getSourceRange()); +BR->emitReport(std::move(Report)); + } }; } // namespace diff --git a/clang/test/Analysis/Checkers/WebKit/call-args-inside-if-statement.cpp b/clang/test/Analysis/Checkers/WebKit/call-args-inside-if-statement.cpp new fi
[clang] [alpha.webkit.UncountedCallArgsChecker] Check the safety of the object argument in a member function call. (PR #81400)
https://github.com/rniwa updated https://github.com/llvm/llvm-project/pull/81400 >From 04e18254efc4f671e0bbd9625c7e994fe47c1636 Mon Sep 17 00:00:00 2001 From: Ryosuke Niwa Date: Sun, 11 Feb 2024 00:07:30 -0800 Subject: [PATCH] [alpha.webkit.UncountedCallArgsChecker] Check the safety of the object argument in a member function call. This PR makes alpha.webkit.UncountedCallArgsChecker eplicitly check the safety of the object argument in a member function call. --- .../WebKit/UncountedCallArgsChecker.cpp | 67 +-- .../Checkers/WebKit/uncounted-obj-arg.cpp | 18 + 2 files changed, 66 insertions(+), 19 deletions(-) create mode 100644 clang/test/Analysis/Checkers/WebKit/uncounted-obj-arg.cpp diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedCallArgsChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedCallArgsChecker.cpp index 31ccae8b097b89..cda96b70ea8735 100644 --- a/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedCallArgsChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedCallArgsChecker.cpp @@ -70,6 +70,15 @@ class UncountedCallArgsChecker // or std::function call operator). unsigned ArgIdx = isa(CE) && isa_and_nonnull(F); + if (auto *MemberCallExpr = dyn_cast(CE)) { +auto *E = MemberCallExpr->getImplicitObjectArgument(); +auto *ArgType = MemberCallExpr->getObjectType().getTypePtrOrNull(); +std::optional IsUncounted = +isUncounted(ArgType->getAsCXXRecordDecl()); +if (IsUncounted && *IsUncounted && !isPtrOriginSafe(E)) + reportBugOnThis(E); + } + for (auto P = F->param_begin(); // FIXME: Also check variadic function parameters. // FIXME: Also check default function arguments. Probably a different @@ -91,25 +100,7 @@ class UncountedCallArgsChecker const auto *Arg = CE->getArg(ArgIdx); -std::pair ArgOrigin = -tryToFindPtrOrigin(Arg, true); - -// Temporary ref-counted object created as part of the call argument -// would outlive the call. -if (ArgOrigin.second) - continue; - -if (isa(ArgOrigin.first)) { - // foo(nullptr) - continue; -} -if (isa(ArgOrigin.first)) { - // FIXME: Check the value. - // foo(NULL) - continue; -} - -if (isASafeCallArg(ArgOrigin.first)) +if (isPtrOriginSafe(Arg)) continue; reportBug(Arg, *P); @@ -117,6 +108,28 @@ class UncountedCallArgsChecker } } + bool isPtrOriginSafe(const Expr *Arg) const { +std::pair ArgOrigin = +tryToFindPtrOrigin(Arg, true); + +// Temporary ref-counted object created as part of the call argument +// would outlive the call. +if (ArgOrigin.second) + return true; + +if (isa(ArgOrigin.first)) { + // foo(nullptr) + return true; +} +if (isa(ArgOrigin.first)) { + // FIXME: Check the value. + // foo(NULL) + return true; +} + +return isASafeCallArg(ArgOrigin.first); + } + bool shouldSkipCall(const CallExpr *CE) const { if (CE->getNumArgs() == 0) return false; @@ -183,6 +196,22 @@ class UncountedCallArgsChecker Report->addRange(CallArg->getSourceRange()); BR->emitReport(std::move(Report)); } + + void reportBugOnThis(const Expr *CallArg) const { +assert(CallArg); + +SmallString<100> Buf; +llvm::raw_svector_ostream Os(Buf); + +Os << "Call argument for 'this' parameter is uncounted and unsafe."; + +const SourceLocation SrcLocToReport = CallArg->getSourceRange().getBegin(); + +PathDiagnosticLocation BSLoc(SrcLocToReport, BR->getSourceManager()); +auto Report = std::make_unique(Bug, Os.str(), BSLoc); +Report->addRange(CallArg->getSourceRange()); +BR->emitReport(std::move(Report)); + } }; } // namespace diff --git a/clang/test/Analysis/Checkers/WebKit/uncounted-obj-arg.cpp b/clang/test/Analysis/Checkers/WebKit/uncounted-obj-arg.cpp new file mode 100644 index 00..e5e39e3faac714 --- /dev/null +++ b/clang/test/Analysis/Checkers/WebKit/uncounted-obj-arg.cpp @@ -0,0 +1,18 @@ +// RUN: %clang_analyze_cc1 -analyzer-checker=alpha.webkit.UncountedCallArgsChecker -verify %s + +#include "mock-types.h" + +class RefCounted { +public: + void ref() const; + void deref() const; + void someFunction(); +}; + +RefCounted* refCountedObj(); + +void test() +{ + refCountedObj()->someFunction(); + // expected-warning@-1{{Call argument for 'this' parameter is uncounted and unsafe}} +} ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [alpha.webkit.UncountedCallArgsChecker] Add a few more safe functions to call. (PR #81527)
https://github.com/rniwa created https://github.com/llvm/llvm-project/pull/81527 Added checkedDowncast, uncheckedDowncast, & toString as safe functions to call. >From c67d64d7ca5885b03b6e0738d5e237ccbd3ed38a Mon Sep 17 00:00:00 2001 From: Ryosuke Niwa Date: Mon, 12 Feb 2024 12:31:37 -0800 Subject: [PATCH] [alpha.webkit.UncountedCallArgsChecker] Add a few more safe functions to call. Added checkedDowncast, uncheckedDowncast, & toString as safe functions to call. --- .../Checkers/WebKit/PtrTypesSemantics.cpp | 5 +++-- .../WebKit/UncountedCallArgsChecker.cpp | 12 ++- ...ncast.cpp => call-args-safe-functions.cpp} | 21 +++ 3 files changed, 31 insertions(+), 7 deletions(-) rename clang/test/Analysis/Checkers/WebKit/{call-args-dynamic-downcast.cpp => call-args-safe-functions.cpp} (55%) diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp b/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp index d2b66341058000..e2c19ef43be132 100644 --- a/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp @@ -192,8 +192,9 @@ bool isPtrConversion(const FunctionDecl *F) { // FIXME: check # of params == 1 const auto FunctionName = safeGetName(F); if (FunctionName == "getPtr" || FunctionName == "WeakPtr" || - FunctionName == "dynamicDowncast" - || FunctionName == "downcast" || FunctionName == "bitwise_cast") + FunctionName == "dynamicDowncast" || FunctionName == "downcast" || + FunctionName == "checkedDowncast" || FunctionName == "uncheckedDowncast" || + FunctionName == "bitwise_cast") return true; return false; diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedCallArgsChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedCallArgsChecker.cpp index 31ccae8b097b89..5d5fce2867f99b 100644 --- a/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedCallArgsChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedCallArgsChecker.cpp @@ -148,13 +148,15 @@ class UncountedCallArgsChecker auto name = safeGetName(Callee); if (name == "adoptRef" || name == "getPtr" || name == "WeakPtr" || -name == "dynamicDowncast" || name == "downcast" || name == "bitwise_cast" || -name == "is" || name == "equal" || name == "hash" || -name == "isType" +name == "dynamicDowncast" || name == "downcast" || +name == "checkedDowncast" || name == "uncheckedDowncast" || +name == "bitwise_cast" || name == "is" || name == "equal" || +name == "hash" || name == "isType" || // FIXME: Most/all of these should be implemented via attributes. -|| name == "equalIgnoringASCIICase" || +name == "equalIgnoringASCIICase" || name == "equalIgnoringASCIICaseCommon" || -name == "equalIgnoringNullity") +name == "equalIgnoringNullity" || +name == "toString") return true; return false; diff --git a/clang/test/Analysis/Checkers/WebKit/call-args-dynamic-downcast.cpp b/clang/test/Analysis/Checkers/WebKit/call-args-safe-functions.cpp similarity index 55% rename from clang/test/Analysis/Checkers/WebKit/call-args-dynamic-downcast.cpp rename to clang/test/Analysis/Checkers/WebKit/call-args-safe-functions.cpp index 28156623d9a0fd..a87446564870cd 100644 --- a/clang/test/Analysis/Checkers/WebKit/call-args-dynamic-downcast.cpp +++ b/clang/test/Analysis/Checkers/WebKit/call-args-safe-functions.cpp @@ -23,13 +23,34 @@ class OtherObject { Derived* obj(); }; +class String { +}; + template inline Target* dynamicDowncast(Source* source) { return static_cast(source); } +template +inline Target* checkedDowncast(Source* source) +{ +return static_cast(source); +} + +template +inline Target* uncheckedDowncast(Source* source) +{ +return static_cast(source); +} + +template +String toString(const Types&... values); + void foo(OtherObject* other) { dynamicDowncast(other->obj()); +checkedDowncast(other->obj()); +uncheckedDowncast(other->obj()); +toString(other->obj()); } ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [alpha.webkit.UncountedCallArgsChecker] Add a few more safe functions to call. (PR #81527)
https://github.com/rniwa updated https://github.com/llvm/llvm-project/pull/81527 >From 7596e70e7ed63d881d4754f3f18b9e01f38f176f Mon Sep 17 00:00:00 2001 From: Ryosuke Niwa Date: Mon, 12 Feb 2024 12:31:37 -0800 Subject: [PATCH] [alpha.webkit.UncountedCallArgsChecker] Add a few more safe functions to call. Added checkedDowncast, uncheckedDowncast, & toString as safe functions to call. --- .../Checkers/WebKit/PtrTypesSemantics.cpp | 5 +++-- .../WebKit/UncountedCallArgsChecker.cpp | 11 +- ...ncast.cpp => call-args-safe-functions.cpp} | 21 +++ 3 files changed, 30 insertions(+), 7 deletions(-) rename clang/test/Analysis/Checkers/WebKit/{call-args-dynamic-downcast.cpp => call-args-safe-functions.cpp} (55%) diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp b/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp index d2b66341058000..16fc6a68a0800a 100644 --- a/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp @@ -192,8 +192,9 @@ bool isPtrConversion(const FunctionDecl *F) { // FIXME: check # of params == 1 const auto FunctionName = safeGetName(F); if (FunctionName == "getPtr" || FunctionName == "WeakPtr" || - FunctionName == "dynamicDowncast" - || FunctionName == "downcast" || FunctionName == "bitwise_cast") + FunctionName == "dynamicDowncast" || FunctionName == "downcast" || + FunctionName == "checkedDowncast" || + FunctionName == "uncheckedDowncast" || FunctionName == "bitwise_cast"s return true; return false; diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedCallArgsChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedCallArgsChecker.cpp index 31ccae8b097b89..cc4585a0b0eeff 100644 --- a/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedCallArgsChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedCallArgsChecker.cpp @@ -148,13 +148,14 @@ class UncountedCallArgsChecker auto name = safeGetName(Callee); if (name == "adoptRef" || name == "getPtr" || name == "WeakPtr" || -name == "dynamicDowncast" || name == "downcast" || name == "bitwise_cast" || -name == "is" || name == "equal" || name == "hash" || -name == "isType" +name == "dynamicDowncast" || name == "downcast" || +name == "checkedDowncast" || name == "uncheckedDowncast" || +name == "bitwise_cast" || name == "is" || name == "equal" || +name == "hash" || name == "isType" || // FIXME: Most/all of these should be implemented via attributes. -|| name == "equalIgnoringASCIICase" || +name == "equalIgnoringASCIICase" || name == "equalIgnoringASCIICaseCommon" || -name == "equalIgnoringNullity") +name == "equalIgnoringNullity" || name == "toString") return true; return false; diff --git a/clang/test/Analysis/Checkers/WebKit/call-args-dynamic-downcast.cpp b/clang/test/Analysis/Checkers/WebKit/call-args-safe-functions.cpp similarity index 55% rename from clang/test/Analysis/Checkers/WebKit/call-args-dynamic-downcast.cpp rename to clang/test/Analysis/Checkers/WebKit/call-args-safe-functions.cpp index 28156623d9a0fd..a87446564870cd 100644 --- a/clang/test/Analysis/Checkers/WebKit/call-args-dynamic-downcast.cpp +++ b/clang/test/Analysis/Checkers/WebKit/call-args-safe-functions.cpp @@ -23,13 +23,34 @@ class OtherObject { Derived* obj(); }; +class String { +}; + template inline Target* dynamicDowncast(Source* source) { return static_cast(source); } +template +inline Target* checkedDowncast(Source* source) +{ +return static_cast(source); +} + +template +inline Target* uncheckedDowncast(Source* source) +{ +return static_cast(source); +} + +template +String toString(const Types&... values); + void foo(OtherObject* other) { dynamicDowncast(other->obj()); +checkedDowncast(other->obj()); +uncheckedDowncast(other->obj()); +toString(other->obj()); } ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [alpha.webkit.UncountedCallArgsChecker] Add a few more safe functions to call. (PR #81527)
https://github.com/rniwa closed https://github.com/llvm/llvm-project/pull/81527 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [alpha.webkit.UncountedCallArgsChecker] Add a few more safe functions to call. (PR #81532)
https://github.com/rniwa created https://github.com/llvm/llvm-project/pull/81532 Added checkedDowncast, uncheckedDowncast, & toString as safe functions to call. >From 7596e70e7ed63d881d4754f3f18b9e01f38f176f Mon Sep 17 00:00:00 2001 From: Ryosuke Niwa Date: Mon, 12 Feb 2024 12:31:37 -0800 Subject: [PATCH] [alpha.webkit.UncountedCallArgsChecker] Add a few more safe functions to call. Added checkedDowncast, uncheckedDowncast, & toString as safe functions to call. --- .../Checkers/WebKit/PtrTypesSemantics.cpp | 5 +++-- .../WebKit/UncountedCallArgsChecker.cpp | 11 +- ...ncast.cpp => call-args-safe-functions.cpp} | 21 +++ 3 files changed, 30 insertions(+), 7 deletions(-) rename clang/test/Analysis/Checkers/WebKit/{call-args-dynamic-downcast.cpp => call-args-safe-functions.cpp} (55%) diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp b/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp index d2b66341058000..16fc6a68a0800a 100644 --- a/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp @@ -192,8 +192,9 @@ bool isPtrConversion(const FunctionDecl *F) { // FIXME: check # of params == 1 const auto FunctionName = safeGetName(F); if (FunctionName == "getPtr" || FunctionName == "WeakPtr" || - FunctionName == "dynamicDowncast" - || FunctionName == "downcast" || FunctionName == "bitwise_cast") + FunctionName == "dynamicDowncast" || FunctionName == "downcast" || + FunctionName == "checkedDowncast" || + FunctionName == "uncheckedDowncast" || FunctionName == "bitwise_cast"s return true; return false; diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedCallArgsChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedCallArgsChecker.cpp index 31ccae8b097b89..cc4585a0b0eeff 100644 --- a/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedCallArgsChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedCallArgsChecker.cpp @@ -148,13 +148,14 @@ class UncountedCallArgsChecker auto name = safeGetName(Callee); if (name == "adoptRef" || name == "getPtr" || name == "WeakPtr" || -name == "dynamicDowncast" || name == "downcast" || name == "bitwise_cast" || -name == "is" || name == "equal" || name == "hash" || -name == "isType" +name == "dynamicDowncast" || name == "downcast" || +name == "checkedDowncast" || name == "uncheckedDowncast" || +name == "bitwise_cast" || name == "is" || name == "equal" || +name == "hash" || name == "isType" || // FIXME: Most/all of these should be implemented via attributes. -|| name == "equalIgnoringASCIICase" || +name == "equalIgnoringASCIICase" || name == "equalIgnoringASCIICaseCommon" || -name == "equalIgnoringNullity") +name == "equalIgnoringNullity" || name == "toString") return true; return false; diff --git a/clang/test/Analysis/Checkers/WebKit/call-args-dynamic-downcast.cpp b/clang/test/Analysis/Checkers/WebKit/call-args-safe-functions.cpp similarity index 55% rename from clang/test/Analysis/Checkers/WebKit/call-args-dynamic-downcast.cpp rename to clang/test/Analysis/Checkers/WebKit/call-args-safe-functions.cpp index 28156623d9a0fd..a87446564870cd 100644 --- a/clang/test/Analysis/Checkers/WebKit/call-args-dynamic-downcast.cpp +++ b/clang/test/Analysis/Checkers/WebKit/call-args-safe-functions.cpp @@ -23,13 +23,34 @@ class OtherObject { Derived* obj(); }; +class String { +}; + template inline Target* dynamicDowncast(Source* source) { return static_cast(source); } +template +inline Target* checkedDowncast(Source* source) +{ +return static_cast(source); +} + +template +inline Target* uncheckedDowncast(Source* source) +{ +return static_cast(source); +} + +template +String toString(const Types&... values); + void foo(OtherObject* other) { dynamicDowncast(other->obj()); +checkedDowncast(other->obj()); +uncheckedDowncast(other->obj()); +toString(other->obj()); } ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [alpha.webkit.UncountedCallArgsChecker] Add a few more safe functions to call. (PR #81532)
https://github.com/rniwa updated https://github.com/llvm/llvm-project/pull/81532 >From 9a25673cf0c585b1d13e54b1b836edaae1a1ca2c Mon Sep 17 00:00:00 2001 From: Ryosuke Niwa Date: Mon, 12 Feb 2024 12:31:37 -0800 Subject: [PATCH] [alpha.webkit.UncountedCallArgsChecker] Add a few more safe functions to call. Added checkedDowncast, uncheckedDowncast, & toString as safe functions to call. --- .../Checkers/WebKit/PtrTypesSemantics.cpp | 5 +++-- .../WebKit/UncountedCallArgsChecker.cpp | 11 +- ...ncast.cpp => call-args-safe-functions.cpp} | 21 +++ 3 files changed, 30 insertions(+), 7 deletions(-) rename clang/test/Analysis/Checkers/WebKit/{call-args-dynamic-downcast.cpp => call-args-safe-functions.cpp} (55%) diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp b/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp index d2b66341058000..d9f4e1a598e788 100644 --- a/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp @@ -192,8 +192,9 @@ bool isPtrConversion(const FunctionDecl *F) { // FIXME: check # of params == 1 const auto FunctionName = safeGetName(F); if (FunctionName == "getPtr" || FunctionName == "WeakPtr" || - FunctionName == "dynamicDowncast" - || FunctionName == "downcast" || FunctionName == "bitwise_cast") + FunctionName == "dynamicDowncast" || FunctionName == "downcast" || + FunctionName == "checkedDowncast" || + FunctionName == "uncheckedDowncast" || FunctionName == "bitwise_cast" return true; return false; diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedCallArgsChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedCallArgsChecker.cpp index 31ccae8b097b89..cc4585a0b0eeff 100644 --- a/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedCallArgsChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedCallArgsChecker.cpp @@ -148,13 +148,14 @@ class UncountedCallArgsChecker auto name = safeGetName(Callee); if (name == "adoptRef" || name == "getPtr" || name == "WeakPtr" || -name == "dynamicDowncast" || name == "downcast" || name == "bitwise_cast" || -name == "is" || name == "equal" || name == "hash" || -name == "isType" +name == "dynamicDowncast" || name == "downcast" || +name == "checkedDowncast" || name == "uncheckedDowncast" || +name == "bitwise_cast" || name == "is" || name == "equal" || +name == "hash" || name == "isType" || // FIXME: Most/all of these should be implemented via attributes. -|| name == "equalIgnoringASCIICase" || +name == "equalIgnoringASCIICase" || name == "equalIgnoringASCIICaseCommon" || -name == "equalIgnoringNullity") +name == "equalIgnoringNullity" || name == "toString") return true; return false; diff --git a/clang/test/Analysis/Checkers/WebKit/call-args-dynamic-downcast.cpp b/clang/test/Analysis/Checkers/WebKit/call-args-safe-functions.cpp similarity index 55% rename from clang/test/Analysis/Checkers/WebKit/call-args-dynamic-downcast.cpp rename to clang/test/Analysis/Checkers/WebKit/call-args-safe-functions.cpp index 28156623d9a0fd..a87446564870cd 100644 --- a/clang/test/Analysis/Checkers/WebKit/call-args-dynamic-downcast.cpp +++ b/clang/test/Analysis/Checkers/WebKit/call-args-safe-functions.cpp @@ -23,13 +23,34 @@ class OtherObject { Derived* obj(); }; +class String { +}; + template inline Target* dynamicDowncast(Source* source) { return static_cast(source); } +template +inline Target* checkedDowncast(Source* source) +{ +return static_cast(source); +} + +template +inline Target* uncheckedDowncast(Source* source) +{ +return static_cast(source); +} + +template +String toString(const Types&... values); + void foo(OtherObject* other) { dynamicDowncast(other->obj()); +checkedDowncast(other->obj()); +uncheckedDowncast(other->obj()); +toString(other->obj()); } ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [alpha.webkit.UncountedCallArgsChecker] Add a few more safe functions to call. (PR #81532)
https://github.com/rniwa updated https://github.com/llvm/llvm-project/pull/81532 >From 52b6e959d69a96704ec2a403131627049a782352 Mon Sep 17 00:00:00 2001 From: Ryosuke Niwa Date: Mon, 12 Feb 2024 12:31:37 -0800 Subject: [PATCH] [alpha.webkit.UncountedCallArgsChecker] Add a few more safe functions to call. Added checkedDowncast, uncheckedDowncast, & toString as safe functions to call. --- .../Checkers/WebKit/PtrTypesSemantics.cpp | 5 +++-- .../WebKit/UncountedCallArgsChecker.cpp | 11 +- ...ncast.cpp => call-args-safe-functions.cpp} | 21 +++ 3 files changed, 30 insertions(+), 7 deletions(-) rename clang/test/Analysis/Checkers/WebKit/{call-args-dynamic-downcast.cpp => call-args-safe-functions.cpp} (55%) diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp b/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp index d2b66341058000..c4a78da25438be 100644 --- a/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp @@ -192,8 +192,9 @@ bool isPtrConversion(const FunctionDecl *F) { // FIXME: check # of params == 1 const auto FunctionName = safeGetName(F); if (FunctionName == "getPtr" || FunctionName == "WeakPtr" || - FunctionName == "dynamicDowncast" - || FunctionName == "downcast" || FunctionName == "bitwise_cast") + FunctionName == "dynamicDowncast" || FunctionName == "downcast" || + FunctionName == "checkedDowncast" || + FunctionName == "uncheckedDowncast" || FunctionName == "bitwise_cast") return true; return false; diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedCallArgsChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedCallArgsChecker.cpp index 31ccae8b097b89..cc4585a0b0eeff 100644 --- a/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedCallArgsChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedCallArgsChecker.cpp @@ -148,13 +148,14 @@ class UncountedCallArgsChecker auto name = safeGetName(Callee); if (name == "adoptRef" || name == "getPtr" || name == "WeakPtr" || -name == "dynamicDowncast" || name == "downcast" || name == "bitwise_cast" || -name == "is" || name == "equal" || name == "hash" || -name == "isType" +name == "dynamicDowncast" || name == "downcast" || +name == "checkedDowncast" || name == "uncheckedDowncast" || +name == "bitwise_cast" || name == "is" || name == "equal" || +name == "hash" || name == "isType" || // FIXME: Most/all of these should be implemented via attributes. -|| name == "equalIgnoringASCIICase" || +name == "equalIgnoringASCIICase" || name == "equalIgnoringASCIICaseCommon" || -name == "equalIgnoringNullity") +name == "equalIgnoringNullity" || name == "toString") return true; return false; diff --git a/clang/test/Analysis/Checkers/WebKit/call-args-dynamic-downcast.cpp b/clang/test/Analysis/Checkers/WebKit/call-args-safe-functions.cpp similarity index 55% rename from clang/test/Analysis/Checkers/WebKit/call-args-dynamic-downcast.cpp rename to clang/test/Analysis/Checkers/WebKit/call-args-safe-functions.cpp index 28156623d9a0fd..a87446564870cd 100644 --- a/clang/test/Analysis/Checkers/WebKit/call-args-dynamic-downcast.cpp +++ b/clang/test/Analysis/Checkers/WebKit/call-args-safe-functions.cpp @@ -23,13 +23,34 @@ class OtherObject { Derived* obj(); }; +class String { +}; + template inline Target* dynamicDowncast(Source* source) { return static_cast(source); } +template +inline Target* checkedDowncast(Source* source) +{ +return static_cast(source); +} + +template +inline Target* uncheckedDowncast(Source* source) +{ +return static_cast(source); +} + +template +String toString(const Types&... values); + void foo(OtherObject* other) { dynamicDowncast(other->obj()); +checkedDowncast(other->obj()); +uncheckedDowncast(other->obj()); +toString(other->obj()); } ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] Detect a return value of Ref & RefPtr (PR #81580)
https://github.com/rniwa created https://github.com/llvm/llvm-project/pull/81580 This PR makes the checker not emit warning when a function is called with a return value of another function when the return value is of type Ref or RefPtr. >From 52b6e959d69a96704ec2a403131627049a782352 Mon Sep 17 00:00:00 2001 From: Ryosuke Niwa Date: Mon, 12 Feb 2024 12:31:37 -0800 Subject: [PATCH 1/2] [alpha.webkit.UncountedCallArgsChecker] Add a few more safe functions to call. Added checkedDowncast, uncheckedDowncast, & toString as safe functions to call. --- .../Checkers/WebKit/PtrTypesSemantics.cpp | 5 +++-- .../WebKit/UncountedCallArgsChecker.cpp | 11 +- ...ncast.cpp => call-args-safe-functions.cpp} | 21 +++ 3 files changed, 30 insertions(+), 7 deletions(-) rename clang/test/Analysis/Checkers/WebKit/{call-args-dynamic-downcast.cpp => call-args-safe-functions.cpp} (55%) diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp b/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp index d2b66341058000..c4a78da25438be 100644 --- a/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp @@ -192,8 +192,9 @@ bool isPtrConversion(const FunctionDecl *F) { // FIXME: check # of params == 1 const auto FunctionName = safeGetName(F); if (FunctionName == "getPtr" || FunctionName == "WeakPtr" || - FunctionName == "dynamicDowncast" - || FunctionName == "downcast" || FunctionName == "bitwise_cast") + FunctionName == "dynamicDowncast" || FunctionName == "downcast" || + FunctionName == "checkedDowncast" || + FunctionName == "uncheckedDowncast" || FunctionName == "bitwise_cast") return true; return false; diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedCallArgsChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedCallArgsChecker.cpp index 31ccae8b097b89..cc4585a0b0eeff 100644 --- a/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedCallArgsChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedCallArgsChecker.cpp @@ -148,13 +148,14 @@ class UncountedCallArgsChecker auto name = safeGetName(Callee); if (name == "adoptRef" || name == "getPtr" || name == "WeakPtr" || -name == "dynamicDowncast" || name == "downcast" || name == "bitwise_cast" || -name == "is" || name == "equal" || name == "hash" || -name == "isType" +name == "dynamicDowncast" || name == "downcast" || +name == "checkedDowncast" || name == "uncheckedDowncast" || +name == "bitwise_cast" || name == "is" || name == "equal" || +name == "hash" || name == "isType" || // FIXME: Most/all of these should be implemented via attributes. -|| name == "equalIgnoringASCIICase" || +name == "equalIgnoringASCIICase" || name == "equalIgnoringASCIICaseCommon" || -name == "equalIgnoringNullity") +name == "equalIgnoringNullity" || name == "toString") return true; return false; diff --git a/clang/test/Analysis/Checkers/WebKit/call-args-dynamic-downcast.cpp b/clang/test/Analysis/Checkers/WebKit/call-args-safe-functions.cpp similarity index 55% rename from clang/test/Analysis/Checkers/WebKit/call-args-dynamic-downcast.cpp rename to clang/test/Analysis/Checkers/WebKit/call-args-safe-functions.cpp index 28156623d9a0fd..a87446564870cd 100644 --- a/clang/test/Analysis/Checkers/WebKit/call-args-dynamic-downcast.cpp +++ b/clang/test/Analysis/Checkers/WebKit/call-args-safe-functions.cpp @@ -23,13 +23,34 @@ class OtherObject { Derived* obj(); }; +class String { +}; + template inline Target* dynamicDowncast(Source* source) { return static_cast(source); } +template +inline Target* checkedDowncast(Source* source) +{ +return static_cast(source); +} + +template +inline Target* uncheckedDowncast(Source* source) +{ +return static_cast(source); +} + +template +String toString(const Types&... values); + void foo(OtherObject* other) { dynamicDowncast(other->obj()); +checkedDowncast(other->obj()); +uncheckedDowncast(other->obj()); +toString(other->obj()); } >From a781c46e5db132e31816efd7bd475d6af157f256 Mon Sep 17 00:00:00 2001 From: Ryosuke Niwa Date: Tue, 13 Feb 2024 00:35:36 -0800 Subject: [PATCH 2/2] [alpha.webkit.UncountedCallArgsChecker] Detect a return value of Ref & RefPtr This PR makes the checker not emit warning when a function is called with a return value of another function when the return value is of type Ref or RefPtr. --- .../Checkers/WebKit/ASTUtils.cpp | 6 + .../Checkers/WebKit/PtrTypesSemantics.cpp | 20 .../Checkers/WebKit/PtrTypesSemantics.h | 3 +++ .../call-args-protected-return-value.cpp | 23 +++ .../Analysis/Checkers/WebKit/mock-types.h | 1 + 5 files changed, 53 insertions(+) create mode 100644 clang/te
[clang] Detect a return value of Ref & RefPtr (PR #81580)
https://github.com/rniwa updated https://github.com/llvm/llvm-project/pull/81580 >From 51fb3aa575c4509d4b4199d16d10e05281f911ac Mon Sep 17 00:00:00 2001 From: Ryosuke Niwa Date: Tue, 13 Feb 2024 00:35:36 -0800 Subject: [PATCH] [alpha.webkit.UncountedCallArgsChecker] Detect a return value of Ref & RefPtr This PR makes the checker not emit warning when a function is called with a return value of another function when the return value is of type Ref or RefPtr. --- .../Checkers/WebKit/ASTUtils.cpp | 6 + .../Checkers/WebKit/PtrTypesSemantics.cpp | 20 .../Checkers/WebKit/PtrTypesSemantics.h | 3 +++ .../call-args-protected-return-value.cpp | 23 +++ .../Analysis/Checkers/WebKit/mock-types.h | 1 + 5 files changed, 53 insertions(+) create mode 100644 clang/test/Analysis/Checkers/WebKit/call-args-protected-return-value.cpp diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/ASTUtils.cpp b/clang/lib/StaticAnalyzer/Checkers/WebKit/ASTUtils.cpp index 4526fac64735bf..b76c0551c77bb0 100644 --- a/clang/lib/StaticAnalyzer/Checkers/WebKit/ASTUtils.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/ASTUtils.cpp @@ -19,6 +19,10 @@ namespace clang { std::pair tryToFindPtrOrigin(const Expr *E, bool StopAtFirstRefCountedObj) { while (E) { +if (auto *tempExpr = dyn_cast(E)) { + E = tempExpr->getSubExpr(); + continue; +} if (auto *cast = dyn_cast(E)) { if (StopAtFirstRefCountedObj) { if (auto *ConversionFunc = @@ -62,6 +66,8 @@ tryToFindPtrOrigin(const Expr *E, bool StopAtFirstRefCountedObj) { E = call->getArg(0); continue; } +if (isReturnValueRefCounted(callee)) + return {E, true}; if (isPtrConversion(callee)) { E = call->getArg(0); diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp b/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp index d2b66341058000..377c2b9376c1b9 100644 --- a/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp @@ -118,6 +118,26 @@ bool isCtorOfRefCounted(const clang::FunctionDecl *F) { || FunctionName == "Identifier"; } +bool isReturnValueRefCounted(const clang::FunctionDecl *F) { + assert(F); + auto *type = F->getReturnType().getTypePtrOrNull(); + while (type) { +if (auto *elaboratedT = dyn_cast(type)) { + type = elaboratedT->desugar().getTypePtrOrNull(); + continue; +} +if (auto *specialT = dyn_cast(type)) { + if (auto* decl = specialT->getTemplateName().getAsTemplateDecl()) { +auto name = decl->getNameAsString(); +return name == "Ref" || name == "RefPtr"; + } + return false; +} +return false; + } + return false; +} + std::optional isUncounted(const CXXRecordDecl* Class) { // Keep isRefCounted first as it's cheaper. diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.h b/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.h index 45b21cc0918443..c2c5b74442ba43 100644 --- a/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.h +++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.h @@ -50,6 +50,9 @@ std::optional isUncountedPtr(const clang::Type* T); /// false if not. bool isCtorOfRefCounted(const clang::FunctionDecl *F); +/// \returns true if \p F returns a ref-counted object, false if not. +bool isReturnValueRefCounted(const clang::FunctionDecl *F); + /// \returns true if \p M is getter of a ref-counted class, false if not. std::optional isGetterOfRefCounted(const clang::CXXMethodDecl* Method); diff --git a/clang/test/Analysis/Checkers/WebKit/call-args-protected-return-value.cpp b/clang/test/Analysis/Checkers/WebKit/call-args-protected-return-value.cpp new file mode 100644 index 00..1c4b3df211b1e3 --- /dev/null +++ b/clang/test/Analysis/Checkers/WebKit/call-args-protected-return-value.cpp @@ -0,0 +1,23 @@ +// RUN: %clang_analyze_cc1 -analyzer-checker=alpha.webkit.UncountedCallArgsChecker -verify %s +// expected-no-diagnostics + +#include "mock-types.h" + +class RefCounted { +public: + void ref(); + void deref(); +}; + +class Object { +public: + void someFunction(RefCounted&); +}; + +RefPtr object(); +RefPtr protectedTargetObject(); + +void testFunction() { + if (RefPtr obj = object()) +obj->someFunction(*protectedTargetObject()); +} diff --git a/clang/test/Analysis/Checkers/WebKit/mock-types.h b/clang/test/Analysis/Checkers/WebKit/mock-types.h index 5f570b8bee8cb8..814e0944145992 100644 --- a/clang/test/Analysis/Checkers/WebKit/mock-types.h +++ b/clang/test/Analysis/Checkers/WebKit/mock-types.h @@ -20,6 +20,7 @@ template struct RefPtr { T *operator->() { return t; } T &operator*() { return *t; } RefPtr &operator=(T *) { return *this; } + operator bool() { return t; } }; template bool operator==(const RefPt
[clang] Detect a return value of Ref & RefPtr (PR #81580)
https://github.com/rniwa updated https://github.com/llvm/llvm-project/pull/81580 >From 478c7d2c0bb902b829d522fc483e68b8e36489ea Mon Sep 17 00:00:00 2001 From: Ryosuke Niwa Date: Tue, 13 Feb 2024 00:35:36 -0800 Subject: [PATCH] [alpha.webkit.UncountedCallArgsChecker] Detect a return value of `Ref` & `RefPtr` This PR makes the checker not emit warning when a function is called with a return value of another function when the return value is of type `Ref` or `RefPtr`. --- .../Checkers/WebKit/ASTUtils.cpp | 6 + .../Checkers/WebKit/PtrTypesSemantics.cpp | 20 .../Checkers/WebKit/PtrTypesSemantics.h | 3 +++ .../call-args-protected-return-value.cpp | 23 +++ .../Analysis/Checkers/WebKit/mock-types.h | 1 + 5 files changed, 53 insertions(+) create mode 100644 clang/test/Analysis/Checkers/WebKit/call-args-protected-return-value.cpp diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/ASTUtils.cpp b/clang/lib/StaticAnalyzer/Checkers/WebKit/ASTUtils.cpp index 4526fac64735bf..b76c0551c77bb0 100644 --- a/clang/lib/StaticAnalyzer/Checkers/WebKit/ASTUtils.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/ASTUtils.cpp @@ -19,6 +19,10 @@ namespace clang { std::pair tryToFindPtrOrigin(const Expr *E, bool StopAtFirstRefCountedObj) { while (E) { +if (auto *tempExpr = dyn_cast(E)) { + E = tempExpr->getSubExpr(); + continue; +} if (auto *cast = dyn_cast(E)) { if (StopAtFirstRefCountedObj) { if (auto *ConversionFunc = @@ -62,6 +66,8 @@ tryToFindPtrOrigin(const Expr *E, bool StopAtFirstRefCountedObj) { E = call->getArg(0); continue; } +if (isReturnValueRefCounted(callee)) + return {E, true}; if (isPtrConversion(callee)) { E = call->getArg(0); diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp b/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp index d2b66341058000..377c2b9376c1b9 100644 --- a/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp @@ -118,6 +118,26 @@ bool isCtorOfRefCounted(const clang::FunctionDecl *F) { || FunctionName == "Identifier"; } +bool isReturnValueRefCounted(const clang::FunctionDecl *F) { + assert(F); + auto *type = F->getReturnType().getTypePtrOrNull(); + while (type) { +if (auto *elaboratedT = dyn_cast(type)) { + type = elaboratedT->desugar().getTypePtrOrNull(); + continue; +} +if (auto *specialT = dyn_cast(type)) { + if (auto* decl = specialT->getTemplateName().getAsTemplateDecl()) { +auto name = decl->getNameAsString(); +return name == "Ref" || name == "RefPtr"; + } + return false; +} +return false; + } + return false; +} + std::optional isUncounted(const CXXRecordDecl* Class) { // Keep isRefCounted first as it's cheaper. diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.h b/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.h index 45b21cc0918443..c2c5b74442ba43 100644 --- a/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.h +++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.h @@ -50,6 +50,9 @@ std::optional isUncountedPtr(const clang::Type* T); /// false if not. bool isCtorOfRefCounted(const clang::FunctionDecl *F); +/// \returns true if \p F returns a ref-counted object, false if not. +bool isReturnValueRefCounted(const clang::FunctionDecl *F); + /// \returns true if \p M is getter of a ref-counted class, false if not. std::optional isGetterOfRefCounted(const clang::CXXMethodDecl* Method); diff --git a/clang/test/Analysis/Checkers/WebKit/call-args-protected-return-value.cpp b/clang/test/Analysis/Checkers/WebKit/call-args-protected-return-value.cpp new file mode 100644 index 00..1c4b3df211b1e3 --- /dev/null +++ b/clang/test/Analysis/Checkers/WebKit/call-args-protected-return-value.cpp @@ -0,0 +1,23 @@ +// RUN: %clang_analyze_cc1 -analyzer-checker=alpha.webkit.UncountedCallArgsChecker -verify %s +// expected-no-diagnostics + +#include "mock-types.h" + +class RefCounted { +public: + void ref(); + void deref(); +}; + +class Object { +public: + void someFunction(RefCounted&); +}; + +RefPtr object(); +RefPtr protectedTargetObject(); + +void testFunction() { + if (RefPtr obj = object()) +obj->someFunction(*protectedTargetObject()); +} diff --git a/clang/test/Analysis/Checkers/WebKit/mock-types.h b/clang/test/Analysis/Checkers/WebKit/mock-types.h index 5f570b8bee8cb8..814e0944145992 100644 --- a/clang/test/Analysis/Checkers/WebKit/mock-types.h +++ b/clang/test/Analysis/Checkers/WebKit/mock-types.h @@ -20,6 +20,7 @@ template struct RefPtr { T *operator->() { return t; } T &operator*() { return *t; } RefPtr &operator=(T *) { return *this; } + operator bool() { return t; } }; template bool operator==(con
[clang] Detect a return value of Ref & RefPtr (PR #81580)
https://github.com/rniwa updated https://github.com/llvm/llvm-project/pull/81580 >From 24fc75756094d36e72c69805c9d476d8144ed869 Mon Sep 17 00:00:00 2001 From: Ryosuke Niwa Date: Tue, 13 Feb 2024 00:35:36 -0800 Subject: [PATCH] [alpha.webkit.UncountedCallArgsChecker] Detect a return value of `Ref` & `RefPtr` This PR makes the checker not emit warning when a function is called with a return value of another function when the return value is of type `Ref` or `RefPtr`. --- .../Checkers/WebKit/ASTUtils.cpp | 6 + .../Checkers/WebKit/PtrTypesSemantics.cpp | 20 .../Checkers/WebKit/PtrTypesSemantics.h | 3 +++ .../call-args-protected-return-value.cpp | 23 +++ .../Analysis/Checkers/WebKit/mock-types.h | 1 + 5 files changed, 53 insertions(+) create mode 100644 clang/test/Analysis/Checkers/WebKit/call-args-protected-return-value.cpp diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/ASTUtils.cpp b/clang/lib/StaticAnalyzer/Checkers/WebKit/ASTUtils.cpp index 4526fac64735bf..b76c0551c77bb0 100644 --- a/clang/lib/StaticAnalyzer/Checkers/WebKit/ASTUtils.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/ASTUtils.cpp @@ -19,6 +19,10 @@ namespace clang { std::pair tryToFindPtrOrigin(const Expr *E, bool StopAtFirstRefCountedObj) { while (E) { +if (auto *tempExpr = dyn_cast(E)) { + E = tempExpr->getSubExpr(); + continue; +} if (auto *cast = dyn_cast(E)) { if (StopAtFirstRefCountedObj) { if (auto *ConversionFunc = @@ -62,6 +66,8 @@ tryToFindPtrOrigin(const Expr *E, bool StopAtFirstRefCountedObj) { E = call->getArg(0); continue; } +if (isReturnValueRefCounted(callee)) + return {E, true}; if (isPtrConversion(callee)) { E = call->getArg(0); diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp b/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp index d2b66341058000..70c94dd0e960ab 100644 --- a/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp @@ -118,6 +118,26 @@ bool isCtorOfRefCounted(const clang::FunctionDecl *F) { || FunctionName == "Identifier"; } +bool isReturnValueRefCounted(const clang::FunctionDecl *F) { + assert(F); + auto *type = F->getReturnType().getTypePtrOrNull(); + while (type) { +if (auto *elaboratedT = dyn_cast(type)) { + type = elaboratedT->desugar().getTypePtrOrNull(); + continue; +} +if (auto *specialT = dyn_cast(type)) { + if (auto *decl = specialT->getTemplateName().getAsTemplateDecl()) { +auto name = decl->getNameAsString(); +return name == "Ref" || name == "RefPtr"; + } + return false; +} +return false; + } + return false; +} + std::optional isUncounted(const CXXRecordDecl* Class) { // Keep isRefCounted first as it's cheaper. diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.h b/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.h index 45b21cc0918443..c2c5b74442ba43 100644 --- a/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.h +++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.h @@ -50,6 +50,9 @@ std::optional isUncountedPtr(const clang::Type* T); /// false if not. bool isCtorOfRefCounted(const clang::FunctionDecl *F); +/// \returns true if \p F returns a ref-counted object, false if not. +bool isReturnValueRefCounted(const clang::FunctionDecl *F); + /// \returns true if \p M is getter of a ref-counted class, false if not. std::optional isGetterOfRefCounted(const clang::CXXMethodDecl* Method); diff --git a/clang/test/Analysis/Checkers/WebKit/call-args-protected-return-value.cpp b/clang/test/Analysis/Checkers/WebKit/call-args-protected-return-value.cpp new file mode 100644 index 00..1c4b3df211b1e3 --- /dev/null +++ b/clang/test/Analysis/Checkers/WebKit/call-args-protected-return-value.cpp @@ -0,0 +1,23 @@ +// RUN: %clang_analyze_cc1 -analyzer-checker=alpha.webkit.UncountedCallArgsChecker -verify %s +// expected-no-diagnostics + +#include "mock-types.h" + +class RefCounted { +public: + void ref(); + void deref(); +}; + +class Object { +public: + void someFunction(RefCounted&); +}; + +RefPtr object(); +RefPtr protectedTargetObject(); + +void testFunction() { + if (RefPtr obj = object()) +obj->someFunction(*protectedTargetObject()); +} diff --git a/clang/test/Analysis/Checkers/WebKit/mock-types.h b/clang/test/Analysis/Checkers/WebKit/mock-types.h index 5f570b8bee8cb8..814e0944145992 100644 --- a/clang/test/Analysis/Checkers/WebKit/mock-types.h +++ b/clang/test/Analysis/Checkers/WebKit/mock-types.h @@ -20,6 +20,7 @@ template struct RefPtr { T *operator->() { return t; } T &operator*() { return *t; } RefPtr &operator=(T *) { return *this; } + operator bool() { return t; } }; template bool operator==(const Ref
[clang] Detect a return value of Ref & RefPtr (PR #81580)
https://github.com/rniwa updated https://github.com/llvm/llvm-project/pull/81580 >From 24fc75756094d36e72c69805c9d476d8144ed869 Mon Sep 17 00:00:00 2001 From: Ryosuke Niwa Date: Tue, 13 Feb 2024 00:35:36 -0800 Subject: [PATCH 1/2] [alpha.webkit.UncountedCallArgsChecker] Detect a return value of `Ref` & `RefPtr` This PR makes the checker not emit warning when a function is called with a return value of another function when the return value is of type `Ref` or `RefPtr`. --- .../Checkers/WebKit/ASTUtils.cpp | 6 + .../Checkers/WebKit/PtrTypesSemantics.cpp | 20 .../Checkers/WebKit/PtrTypesSemantics.h | 3 +++ .../call-args-protected-return-value.cpp | 23 +++ .../Analysis/Checkers/WebKit/mock-types.h | 1 + 5 files changed, 53 insertions(+) create mode 100644 clang/test/Analysis/Checkers/WebKit/call-args-protected-return-value.cpp diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/ASTUtils.cpp b/clang/lib/StaticAnalyzer/Checkers/WebKit/ASTUtils.cpp index 4526fac64735bf..b76c0551c77bb0 100644 --- a/clang/lib/StaticAnalyzer/Checkers/WebKit/ASTUtils.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/ASTUtils.cpp @@ -19,6 +19,10 @@ namespace clang { std::pair tryToFindPtrOrigin(const Expr *E, bool StopAtFirstRefCountedObj) { while (E) { +if (auto *tempExpr = dyn_cast(E)) { + E = tempExpr->getSubExpr(); + continue; +} if (auto *cast = dyn_cast(E)) { if (StopAtFirstRefCountedObj) { if (auto *ConversionFunc = @@ -62,6 +66,8 @@ tryToFindPtrOrigin(const Expr *E, bool StopAtFirstRefCountedObj) { E = call->getArg(0); continue; } +if (isReturnValueRefCounted(callee)) + return {E, true}; if (isPtrConversion(callee)) { E = call->getArg(0); diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp b/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp index d2b66341058000..70c94dd0e960ab 100644 --- a/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp @@ -118,6 +118,26 @@ bool isCtorOfRefCounted(const clang::FunctionDecl *F) { || FunctionName == "Identifier"; } +bool isReturnValueRefCounted(const clang::FunctionDecl *F) { + assert(F); + auto *type = F->getReturnType().getTypePtrOrNull(); + while (type) { +if (auto *elaboratedT = dyn_cast(type)) { + type = elaboratedT->desugar().getTypePtrOrNull(); + continue; +} +if (auto *specialT = dyn_cast(type)) { + if (auto *decl = specialT->getTemplateName().getAsTemplateDecl()) { +auto name = decl->getNameAsString(); +return name == "Ref" || name == "RefPtr"; + } + return false; +} +return false; + } + return false; +} + std::optional isUncounted(const CXXRecordDecl* Class) { // Keep isRefCounted first as it's cheaper. diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.h b/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.h index 45b21cc0918443..c2c5b74442ba43 100644 --- a/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.h +++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.h @@ -50,6 +50,9 @@ std::optional isUncountedPtr(const clang::Type* T); /// false if not. bool isCtorOfRefCounted(const clang::FunctionDecl *F); +/// \returns true if \p F returns a ref-counted object, false if not. +bool isReturnValueRefCounted(const clang::FunctionDecl *F); + /// \returns true if \p M is getter of a ref-counted class, false if not. std::optional isGetterOfRefCounted(const clang::CXXMethodDecl* Method); diff --git a/clang/test/Analysis/Checkers/WebKit/call-args-protected-return-value.cpp b/clang/test/Analysis/Checkers/WebKit/call-args-protected-return-value.cpp new file mode 100644 index 00..1c4b3df211b1e3 --- /dev/null +++ b/clang/test/Analysis/Checkers/WebKit/call-args-protected-return-value.cpp @@ -0,0 +1,23 @@ +// RUN: %clang_analyze_cc1 -analyzer-checker=alpha.webkit.UncountedCallArgsChecker -verify %s +// expected-no-diagnostics + +#include "mock-types.h" + +class RefCounted { +public: + void ref(); + void deref(); +}; + +class Object { +public: + void someFunction(RefCounted&); +}; + +RefPtr object(); +RefPtr protectedTargetObject(); + +void testFunction() { + if (RefPtr obj = object()) +obj->someFunction(*protectedTargetObject()); +} diff --git a/clang/test/Analysis/Checkers/WebKit/mock-types.h b/clang/test/Analysis/Checkers/WebKit/mock-types.h index 5f570b8bee8cb8..814e0944145992 100644 --- a/clang/test/Analysis/Checkers/WebKit/mock-types.h +++ b/clang/test/Analysis/Checkers/WebKit/mock-types.h @@ -20,6 +20,7 @@ template struct RefPtr { T *operator->() { return t; } T &operator*() { return *t; } RefPtr &operator=(T *) { return *this; } + operator bool() { return t; } }; template bool operator==(const
[clang] Detect a return value of Ref & RefPtr (PR #81580)
@@ -118,6 +118,26 @@ bool isCtorOfRefCounted(const clang::FunctionDecl *F) { || FunctionName == "Identifier"; } +bool isReturnValueRefCounted(const clang::FunctionDecl *F) { + assert(F); + auto *type = F->getReturnType().getTypePtrOrNull(); + while (type) { +if (auto *elaboratedT = dyn_cast(type)) { + type = elaboratedT->desugar().getTypePtrOrNull(); + continue; +} +if (auto *specialT = dyn_cast(type)) { rniwa wrote: Sounds good. Fixed. https://github.com/llvm/llvm-project/pull/81580 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [alpha.webkit.UncountedCallArgsChecker] Check the safety of the object argument in a member function call. (PR #81400)
@@ -70,6 +70,15 @@ class UncountedCallArgsChecker // or std::function call operator). unsigned ArgIdx = isa(CE) && isa_and_nonnull(F); + if (auto *MemberCallExpr = dyn_cast(CE)) { +auto *E = MemberCallExpr->getImplicitObjectArgument(); +auto *ArgType = MemberCallExpr->getObjectType().getTypePtrOrNull(); rniwa wrote: Ah, okay. Will do that. https://github.com/llvm/llvm-project/pull/81400 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [alpha.webkit.UncountedCallArgsChecker] Check the safety of the object argument in a member function call. (PR #81400)
https://github.com/rniwa updated https://github.com/llvm/llvm-project/pull/81400 >From 04e18254efc4f671e0bbd9625c7e994fe47c1636 Mon Sep 17 00:00:00 2001 From: Ryosuke Niwa Date: Sun, 11 Feb 2024 00:07:30 -0800 Subject: [PATCH 1/2] [alpha.webkit.UncountedCallArgsChecker] Check the safety of the object argument in a member function call. This PR makes alpha.webkit.UncountedCallArgsChecker eplicitly check the safety of the object argument in a member function call. --- .../WebKit/UncountedCallArgsChecker.cpp | 67 +-- .../Checkers/WebKit/uncounted-obj-arg.cpp | 18 + 2 files changed, 66 insertions(+), 19 deletions(-) create mode 100644 clang/test/Analysis/Checkers/WebKit/uncounted-obj-arg.cpp diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedCallArgsChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedCallArgsChecker.cpp index 31ccae8b097b89..cda96b70ea8735 100644 --- a/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedCallArgsChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedCallArgsChecker.cpp @@ -70,6 +70,15 @@ class UncountedCallArgsChecker // or std::function call operator). unsigned ArgIdx = isa(CE) && isa_and_nonnull(F); + if (auto *MemberCallExpr = dyn_cast(CE)) { +auto *E = MemberCallExpr->getImplicitObjectArgument(); +auto *ArgType = MemberCallExpr->getObjectType().getTypePtrOrNull(); +std::optional IsUncounted = +isUncounted(ArgType->getAsCXXRecordDecl()); +if (IsUncounted && *IsUncounted && !isPtrOriginSafe(E)) + reportBugOnThis(E); + } + for (auto P = F->param_begin(); // FIXME: Also check variadic function parameters. // FIXME: Also check default function arguments. Probably a different @@ -91,25 +100,7 @@ class UncountedCallArgsChecker const auto *Arg = CE->getArg(ArgIdx); -std::pair ArgOrigin = -tryToFindPtrOrigin(Arg, true); - -// Temporary ref-counted object created as part of the call argument -// would outlive the call. -if (ArgOrigin.second) - continue; - -if (isa(ArgOrigin.first)) { - // foo(nullptr) - continue; -} -if (isa(ArgOrigin.first)) { - // FIXME: Check the value. - // foo(NULL) - continue; -} - -if (isASafeCallArg(ArgOrigin.first)) +if (isPtrOriginSafe(Arg)) continue; reportBug(Arg, *P); @@ -117,6 +108,28 @@ class UncountedCallArgsChecker } } + bool isPtrOriginSafe(const Expr *Arg) const { +std::pair ArgOrigin = +tryToFindPtrOrigin(Arg, true); + +// Temporary ref-counted object created as part of the call argument +// would outlive the call. +if (ArgOrigin.second) + return true; + +if (isa(ArgOrigin.first)) { + // foo(nullptr) + return true; +} +if (isa(ArgOrigin.first)) { + // FIXME: Check the value. + // foo(NULL) + return true; +} + +return isASafeCallArg(ArgOrigin.first); + } + bool shouldSkipCall(const CallExpr *CE) const { if (CE->getNumArgs() == 0) return false; @@ -183,6 +196,22 @@ class UncountedCallArgsChecker Report->addRange(CallArg->getSourceRange()); BR->emitReport(std::move(Report)); } + + void reportBugOnThis(const Expr *CallArg) const { +assert(CallArg); + +SmallString<100> Buf; +llvm::raw_svector_ostream Os(Buf); + +Os << "Call argument for 'this' parameter is uncounted and unsafe."; + +const SourceLocation SrcLocToReport = CallArg->getSourceRange().getBegin(); + +PathDiagnosticLocation BSLoc(SrcLocToReport, BR->getSourceManager()); +auto Report = std::make_unique(Bug, Os.str(), BSLoc); +Report->addRange(CallArg->getSourceRange()); +BR->emitReport(std::move(Report)); + } }; } // namespace diff --git a/clang/test/Analysis/Checkers/WebKit/uncounted-obj-arg.cpp b/clang/test/Analysis/Checkers/WebKit/uncounted-obj-arg.cpp new file mode 100644 index 00..e5e39e3faac714 --- /dev/null +++ b/clang/test/Analysis/Checkers/WebKit/uncounted-obj-arg.cpp @@ -0,0 +1,18 @@ +// RUN: %clang_analyze_cc1 -analyzer-checker=alpha.webkit.UncountedCallArgsChecker -verify %s + +#include "mock-types.h" + +class RefCounted { +public: + void ref() const; + void deref() const; + void someFunction(); +}; + +RefCounted* refCountedObj(); + +void test() +{ + refCountedObj()->someFunction(); + // expected-warning@-1{{Call argument for 'this' parameter is uncounted and unsafe}} +} >From 2d780389a46e5c053053c1432d2ec3af2a7653e9 Mon Sep 17 00:00:00 2001 From: Ryosuke Niwa Date: Tue, 13 Feb 2024 19:36:09 -0800 Subject: [PATCH 2/2] Address the review comment. Namely avoid calling getTypePtrOrNull and don't use raw_svector_ostream. --- .../Checkers/WebKit/UncountedCallArgsChecker.cpp | 9 ++--- 1 file changed, 2 insertions(+), 7 deletions(-) d
[clang] [alpha.webkit.UncountedCallArgsChecker] Check the safety of the object argument in a member function call. (PR #81400)
@@ -183,6 +196,22 @@ class UncountedCallArgsChecker Report->addRange(CallArg->getSourceRange()); BR->emitReport(std::move(Report)); } + + void reportBugOnThis(const Expr *CallArg) const { +assert(CallArg); + +SmallString<100> Buf; +llvm::raw_svector_ostream Os(Buf); + +Os << "Call argument for 'this' parameter is uncounted and unsafe."; rniwa wrote: Good point. Fixed. https://github.com/llvm/llvm-project/pull/81400 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [alpha.webkit.UncountedCallArgsChecker] Check the safety of the object argument in a member function call. (PR #81400)
https://github.com/rniwa updated https://github.com/llvm/llvm-project/pull/81400 >From 6f94c583f5201fbd73156969fa410669d6e1be93 Mon Sep 17 00:00:00 2001 From: Ryosuke Niwa Date: Tue, 13 Feb 2024 20:05:18 -0800 Subject: [PATCH 1/2] [alpha.webkit.UncountedCallArgsChecker] Check the safety of the object argument in a member function call. This PR makes alpha.webkit.UncountedCallArgsChecker eplicitly check the safety of the object argument in a member function call. --- .../WebKit/UncountedCallArgsChecker.cpp | 67 +-- .../Checkers/WebKit/uncounted-obj-arg.cpp | 18 + 2 files changed, 66 insertions(+), 19 deletions(-) create mode 100644 clang/test/Analysis/Checkers/WebKit/uncounted-obj-arg.cpp diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedCallArgsChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedCallArgsChecker.cpp index f4e6191cf05a3c..d4bf3e2c2e75db 100644 --- a/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedCallArgsChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedCallArgsChecker.cpp @@ -70,6 +70,15 @@ class UncountedCallArgsChecker // or std::function call operator). unsigned ArgIdx = isa(CE) && isa_and_nonnull(F); + if (auto *MemberCallExpr = dyn_cast(CE)) { +auto *E = MemberCallExpr->getImplicitObjectArgument(); +auto *ArgType = MemberCallExpr->getObjectType().getTypePtrOrNull(); +std::optional IsUncounted = +isUncounted(ArgType->getAsCXXRecordDecl()); +if (IsUncounted && *IsUncounted && !isPtrOriginSafe(E)) + reportBugOnThis(E); + } + for (auto P = F->param_begin(); // FIXME: Also check variadic function parameters. // FIXME: Also check default function arguments. Probably a different @@ -94,25 +103,7 @@ class UncountedCallArgsChecker if (auto *defaultArg = dyn_cast(Arg)) Arg = defaultArg->getExpr(); -std::pair ArgOrigin = -tryToFindPtrOrigin(Arg, true); - -// Temporary ref-counted object created as part of the call argument -// would outlive the call. -if (ArgOrigin.second) - continue; - -if (isa(ArgOrigin.first)) { - // foo(nullptr) - continue; -} -if (isa(ArgOrigin.first)) { - // FIXME: Check the value. - // foo(NULL) - continue; -} - -if (isASafeCallArg(ArgOrigin.first)) +if (isPtrOriginSafe(Arg)) continue; reportBug(Arg, *P); @@ -120,6 +111,28 @@ class UncountedCallArgsChecker } } + bool isPtrOriginSafe(const Expr *Arg) const { +std::pair ArgOrigin = +tryToFindPtrOrigin(Arg, true); + +// Temporary ref-counted object created as part of the call argument +// would outlive the call. +if (ArgOrigin.second) + return true; + +if (isa(ArgOrigin.first)) { + // foo(nullptr) + return true; +} +if (isa(ArgOrigin.first)) { + // FIXME: Check the value. + // foo(NULL) + return true; +} + +return isASafeCallArg(ArgOrigin.first); + } + bool shouldSkipCall(const CallExpr *CE) const { if (CE->getNumArgs() == 0) return false; @@ -196,6 +209,22 @@ class UncountedCallArgsChecker Report->addRange(CallArg->getSourceRange()); BR->emitReport(std::move(Report)); } + + void reportBugOnThis(const Expr *CallArg) const { +assert(CallArg); + +SmallString<100> Buf; +llvm::raw_svector_ostream Os(Buf); + +Os << "Call argument for 'this' parameter is uncounted and unsafe."; + +const SourceLocation SrcLocToReport = CallArg->getSourceRange().getBegin(); + +PathDiagnosticLocation BSLoc(SrcLocToReport, BR->getSourceManager()); +auto Report = std::make_unique(Bug, Os.str(), BSLoc); +Report->addRange(CallArg->getSourceRange()); +BR->emitReport(std::move(Report)); + } }; } // namespace diff --git a/clang/test/Analysis/Checkers/WebKit/uncounted-obj-arg.cpp b/clang/test/Analysis/Checkers/WebKit/uncounted-obj-arg.cpp new file mode 100644 index 00..e5e39e3faac714 --- /dev/null +++ b/clang/test/Analysis/Checkers/WebKit/uncounted-obj-arg.cpp @@ -0,0 +1,18 @@ +// RUN: %clang_analyze_cc1 -analyzer-checker=alpha.webkit.UncountedCallArgsChecker -verify %s + +#include "mock-types.h" + +class RefCounted { +public: + void ref() const; + void deref() const; + void someFunction(); +}; + +RefCounted* refCountedObj(); + +void test() +{ + refCountedObj()->someFunction(); + // expected-warning@-1{{Call argument for 'this' parameter is uncounted and unsafe}} +} >From c15967f8f09198c01aec8b0161cc08484c78f66a Mon Sep 17 00:00:00 2001 From: Ryosuke Niwa Date: Tue, 13 Feb 2024 19:36:09 -0800 Subject: [PATCH 2/2] Address the review comment. Namely avoid calling getTypePtrOrNull and don't use raw_svector_ostream. --- .../Checkers/WebKit/UncountedCallArgsChecker.cpp | 9 ++--- 1 file chang
[clang] [alpha.webkit.UncountedCallArgsChecker] Check the safety of the object argument in a member function call. (PR #81400)
rniwa wrote: Rebased. https://github.com/llvm/llvm-project/pull/81400 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [alpha.webkit.UncountedCallArgsChecker] Check the safety of the object argument in a member function call. (PR #81400)
https://github.com/rniwa updated https://github.com/llvm/llvm-project/pull/81400 >From 6f94c583f5201fbd73156969fa410669d6e1be93 Mon Sep 17 00:00:00 2001 From: Ryosuke Niwa Date: Tue, 13 Feb 2024 20:05:18 -0800 Subject: [PATCH 1/3] [alpha.webkit.UncountedCallArgsChecker] Check the safety of the object argument in a member function call. This PR makes alpha.webkit.UncountedCallArgsChecker eplicitly check the safety of the object argument in a member function call. --- .../WebKit/UncountedCallArgsChecker.cpp | 67 +-- .../Checkers/WebKit/uncounted-obj-arg.cpp | 18 + 2 files changed, 66 insertions(+), 19 deletions(-) create mode 100644 clang/test/Analysis/Checkers/WebKit/uncounted-obj-arg.cpp diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedCallArgsChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedCallArgsChecker.cpp index f4e6191cf05a3c..d4bf3e2c2e75db 100644 --- a/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedCallArgsChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedCallArgsChecker.cpp @@ -70,6 +70,15 @@ class UncountedCallArgsChecker // or std::function call operator). unsigned ArgIdx = isa(CE) && isa_and_nonnull(F); + if (auto *MemberCallExpr = dyn_cast(CE)) { +auto *E = MemberCallExpr->getImplicitObjectArgument(); +auto *ArgType = MemberCallExpr->getObjectType().getTypePtrOrNull(); +std::optional IsUncounted = +isUncounted(ArgType->getAsCXXRecordDecl()); +if (IsUncounted && *IsUncounted && !isPtrOriginSafe(E)) + reportBugOnThis(E); + } + for (auto P = F->param_begin(); // FIXME: Also check variadic function parameters. // FIXME: Also check default function arguments. Probably a different @@ -94,25 +103,7 @@ class UncountedCallArgsChecker if (auto *defaultArg = dyn_cast(Arg)) Arg = defaultArg->getExpr(); -std::pair ArgOrigin = -tryToFindPtrOrigin(Arg, true); - -// Temporary ref-counted object created as part of the call argument -// would outlive the call. -if (ArgOrigin.second) - continue; - -if (isa(ArgOrigin.first)) { - // foo(nullptr) - continue; -} -if (isa(ArgOrigin.first)) { - // FIXME: Check the value. - // foo(NULL) - continue; -} - -if (isASafeCallArg(ArgOrigin.first)) +if (isPtrOriginSafe(Arg)) continue; reportBug(Arg, *P); @@ -120,6 +111,28 @@ class UncountedCallArgsChecker } } + bool isPtrOriginSafe(const Expr *Arg) const { +std::pair ArgOrigin = +tryToFindPtrOrigin(Arg, true); + +// Temporary ref-counted object created as part of the call argument +// would outlive the call. +if (ArgOrigin.second) + return true; + +if (isa(ArgOrigin.first)) { + // foo(nullptr) + return true; +} +if (isa(ArgOrigin.first)) { + // FIXME: Check the value. + // foo(NULL) + return true; +} + +return isASafeCallArg(ArgOrigin.first); + } + bool shouldSkipCall(const CallExpr *CE) const { if (CE->getNumArgs() == 0) return false; @@ -196,6 +209,22 @@ class UncountedCallArgsChecker Report->addRange(CallArg->getSourceRange()); BR->emitReport(std::move(Report)); } + + void reportBugOnThis(const Expr *CallArg) const { +assert(CallArg); + +SmallString<100> Buf; +llvm::raw_svector_ostream Os(Buf); + +Os << "Call argument for 'this' parameter is uncounted and unsafe."; + +const SourceLocation SrcLocToReport = CallArg->getSourceRange().getBegin(); + +PathDiagnosticLocation BSLoc(SrcLocToReport, BR->getSourceManager()); +auto Report = std::make_unique(Bug, Os.str(), BSLoc); +Report->addRange(CallArg->getSourceRange()); +BR->emitReport(std::move(Report)); + } }; } // namespace diff --git a/clang/test/Analysis/Checkers/WebKit/uncounted-obj-arg.cpp b/clang/test/Analysis/Checkers/WebKit/uncounted-obj-arg.cpp new file mode 100644 index 00..e5e39e3faac714 --- /dev/null +++ b/clang/test/Analysis/Checkers/WebKit/uncounted-obj-arg.cpp @@ -0,0 +1,18 @@ +// RUN: %clang_analyze_cc1 -analyzer-checker=alpha.webkit.UncountedCallArgsChecker -verify %s + +#include "mock-types.h" + +class RefCounted { +public: + void ref() const; + void deref() const; + void someFunction(); +}; + +RefCounted* refCountedObj(); + +void test() +{ + refCountedObj()->someFunction(); + // expected-warning@-1{{Call argument for 'this' parameter is uncounted and unsafe}} +} >From c15967f8f09198c01aec8b0161cc08484c78f66a Mon Sep 17 00:00:00 2001 From: Ryosuke Niwa Date: Tue, 13 Feb 2024 19:36:09 -0800 Subject: [PATCH 2/3] Address the review comment. Namely avoid calling getTypePtrOrNull and don't use raw_svector_ostream. --- .../Checkers/WebKit/UncountedCallArgsChecker.cpp | 9 ++--- 1 file chang
[clang] [alpha.webkit.UncountedLocalVarsChecker] Allow uncounted object references within trivial statements (PR #82229)
https://github.com/rniwa updated https://github.com/llvm/llvm-project/pull/82229 >From 234e301ab2721ddb2f4b43589785015a7d0aa304 Mon Sep 17 00:00:00 2001 From: Ryosuke Niwa Date: Mon, 19 Feb 2024 01:07:13 -0800 Subject: [PATCH 1/7] [alpha.webkit.UncountedLocalVarsChecker] Allow uncounted object references within trivial statements This PR makes alpha.webkit.UncountedLocalVarsChecker ignore raw references and pointers to a ref counted type which appears within "trival" statements. To do this, this PR extends TrivialFunctionAnalysis so that it can also analyze "triviality" of statements as well as that of functions Each Visit* function is now augmented with withCachedResult, which is responsible for looking up and updating the cache for each Visit* functions. As this PR dramatically improves the false positive rate of the checker, it also deletes the code to ignore raw pointers and references within if and for statements. --- .../Checkers/WebKit/PtrTypesSemantics.cpp | 222 -- .../Checkers/WebKit/PtrTypesSemantics.h | 21 +- .../WebKit/UncountedLocalVarsChecker.cpp | 69 +++--- .../Analysis/Checkers/WebKit/mock-types.h | 2 + .../Checkers/WebKit/uncounted-local-vars.cpp | 92 +++- 5 files changed, 285 insertions(+), 121 deletions(-) diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp b/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp index 01b191ab0eeaf4..6c9a8aedb38a4c 100644 --- a/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp @@ -245,18 +245,41 @@ class TrivialFunctionAnalysisVisitor // Returns false if at least one child is non-trivial. bool VisitChildren(const Stmt *S) { -for (const Stmt *Child : S->children()) { - if (Child && !Visit(Child)) +return withCachedResult(S, [&]() { + for (const Stmt *Child : S->children()) { +if (Child && !Visit(Child)) + return false; + } + return true; +}); + } + + bool VisitSubExpr(const Expr *Parent, const Expr *E) { +return withCachedResult(Parent, [&]() { + if (!Visit(E)) return false; -} + return true; +}); + } -return true; + template + bool withCachedResult(const StmtType *S, CheckFunction Function) { +// Insert false to the cache first to avoid infinite recursion. +auto [It, IsNew] = StatementCache.insert(std::make_pair(S, false)); +if (!IsNew) + return It->second; +bool Result = Function(); +It->second = Result; +return Result; } public: - using CacheTy = TrivialFunctionAnalysis::CacheTy; + using FunctionCacheTy = TrivialFunctionAnalysis::FunctionCacheTy; + using StatementCacheTy = TrivialFunctionAnalysis::StatementCacheTy; - TrivialFunctionAnalysisVisitor(CacheTy &Cache) : Cache(Cache) {} + TrivialFunctionAnalysisVisitor(FunctionCacheTy &FunctionCache, + StatementCacheTy &StatementCache) + : FunctionCache(FunctionCache), StatementCache(StatementCache) {} bool VisitStmt(const Stmt *S) { // All statements are non-trivial unless overriden later. @@ -272,13 +295,21 @@ class TrivialFunctionAnalysisVisitor bool VisitReturnStmt(const ReturnStmt *RS) { // A return statement is allowed as long as the return value is trivial. -if (auto *RV = RS->getRetValue()) - return Visit(RV); -return true; +return withCachedResult(RS, [&]() { + if (auto *RV = RS->getRetValue()) +return Visit(RV); + return true; +}); + } + + bool VisitCXXForRangeStmt(const CXXForRangeStmt *FS) { +return VisitChildren(FS); } bool VisitDeclStmt(const DeclStmt *DS) { return VisitChildren(DS); } bool VisitDoStmt(const DoStmt *DS) { return VisitChildren(DS); } + bool VisitForStmt(const ForStmt *FS) { return VisitChildren(FS); } + bool VisitWhileStmt(const WhileStmt *WS) { return VisitChildren(WS); } bool VisitIfStmt(const IfStmt *IS) { return VisitChildren(IS); } bool VisitSwitchStmt(const SwitchStmt *SS) { return VisitChildren(SS); } bool VisitCaseStmt(const CaseStmt *CS) { return VisitChildren(CS); } @@ -286,17 +317,26 @@ class TrivialFunctionAnalysisVisitor bool VisitUnaryOperator(const UnaryOperator *UO) { // Operator '*' and '!' are allowed as long as the operand is trivial. -if (UO->getOpcode() == UO_Deref || UO->getOpcode() == UO_AddrOf || -UO->getOpcode() == UO_LNot) - return Visit(UO->getSubExpr()); - -// Other operators are non-trivial. -return false; +return withCachedResult(UO, [&]() { + auto op = UO->getOpcode(); + if (op == UO_Deref || op == UO_AddrOf || op == UO_LNot) +return Visit(UO->getSubExpr()); + if (UO->isIncrementOp() || UO->isDecrementOp()) { +if (auto *RefExpr = dyn_cast(UO->getSubExpr())) { + if (auto *Decl = dyn_cast(RefExpr->getDecl())) +return Decl->i
[clang] [alpha.webkit.UncountedLocalVarsChecker] Allow uncounted object references within trivial statements (PR #82229)
@@ -474,4 +504,22 @@ bool TrivialFunctionAnalysis::isTrivialImpl( return Result; } +bool TrivialFunctionAnalysis::isTrivialImpl( +const Stmt *S, TrivialFunctionAnalysis::CacheTy &Cache) { + // If the statement isn't in the cache, conservatively assume that + // it's not trivial until analysis completes. Unlike a function case, + // we don't insert an entry into the cache until Visit returns + // since Visit* functions themselves make use of the cache. rniwa wrote: Sure, I need to make TrivialFunctionAnalysisVisitor reference the cache via a reference instead of via copy though. https://github.com/llvm/llvm-project/pull/82229 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [alpha.webkit.UncountedLocalVarsChecker] Allow uncounted object references within trivial statements (PR #82229)
@@ -189,18 +202,16 @@ class UncountedLocalVarsChecker dyn_cast_or_null(Ref->getFoundDecl())) { const auto *MaybeGuardianArgType = MaybeGuardian->getType().getTypePtr(); - if (!MaybeGuardianArgType) -return; - const CXXRecordDecl *const MaybeGuardianArgCXXRecord = - MaybeGuardianArgType->getAsCXXRecordDecl(); - if (!MaybeGuardianArgCXXRecord) -return; - - if (MaybeGuardian->isLocalVarDecl() && - (isRefCounted(MaybeGuardianArgCXXRecord) || - isRefcountedStringsHack(MaybeGuardian)) && - isGuardedScopeEmbeddedInGuardianScope(V, MaybeGuardian)) { -return; + if (MaybeGuardianArgType) { +const CXXRecordDecl *const MaybeGuardianArgCXXRecord = +MaybeGuardianArgType->getAsCXXRecordDecl(); +if (MaybeGuardianArgCXXRecord) { + if (MaybeGuardian->isLocalVarDecl() && + (isRefCounted(MaybeGuardianArgCXXRecord) || + isRefcountedStringsHack(MaybeGuardian)) && + isGuardedScopeEmbeddedInGuardianScope(V, MaybeGuardian)) +return; +} rniwa wrote: So before this PR, those early exits took place when `MaybeGuardianArgType` was `nullptr` or if `MaybeGuardianArgType` wasn't a `CXXRecordDecl`. This PR changes so that we only return when all these conditions hold true or when it's a `ParmVarDecl`. https://github.com/llvm/llvm-project/pull/82229 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [alpha.webkit.UncountedLocalVarsChecker] Allow uncounted object references within trivial statements (PR #82229)
@@ -189,18 +202,16 @@ class UncountedLocalVarsChecker dyn_cast_or_null(Ref->getFoundDecl())) { const auto *MaybeGuardianArgType = MaybeGuardian->getType().getTypePtr(); - if (!MaybeGuardianArgType) -return; - const CXXRecordDecl *const MaybeGuardianArgCXXRecord = - MaybeGuardianArgType->getAsCXXRecordDecl(); - if (!MaybeGuardianArgCXXRecord) -return; - - if (MaybeGuardian->isLocalVarDecl() && - (isRefCounted(MaybeGuardianArgCXXRecord) || - isRefcountedStringsHack(MaybeGuardian)) && - isGuardedScopeEmbeddedInGuardianScope(V, MaybeGuardian)) { -return; + if (MaybeGuardianArgType) { +const CXXRecordDecl *const MaybeGuardianArgCXXRecord = +MaybeGuardianArgType->getAsCXXRecordDecl(); +if (MaybeGuardianArgCXXRecord) { + if (MaybeGuardian->isLocalVarDecl() && + (isRefCounted(MaybeGuardianArgCXXRecord) || + isRefcountedStringsHack(MaybeGuardian)) && + isGuardedScopeEmbeddedInGuardianScope(V, MaybeGuardian)) +return; +} rniwa wrote: Specifically, without this change, we'd get the following error: ``` clang/test/Analysis/Checkers/WebKit/uncounted-local-vars.cpp Line 162 (directive at clang/test/Analysis/Checkers/WebKit/uncounted-local-vars.cpp:163): Local variable 'c' is uncounted and unsafe [alpha.webkit.UncountedLocalVarsChecker] ``` https://github.com/llvm/llvm-project/pull/82229 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [alpha.webkit.UncountedLocalVarsChecker] Allow uncounted object references within trivial statements (PR #82229)
rniwa wrote: Actually, we need to cache the results for `VisitDeclRefExpr` as well to avoid infinite recursion. https://github.com/llvm/llvm-project/pull/82229 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [alpha.webkit.UncountedLocalVarsChecker] Allow uncounted object references within trivial statements (PR #82229)
https://github.com/rniwa updated https://github.com/llvm/llvm-project/pull/82229 >From 234e301ab2721ddb2f4b43589785015a7d0aa304 Mon Sep 17 00:00:00 2001 From: Ryosuke Niwa Date: Mon, 19 Feb 2024 01:07:13 -0800 Subject: [PATCH 1/8] [alpha.webkit.UncountedLocalVarsChecker] Allow uncounted object references within trivial statements This PR makes alpha.webkit.UncountedLocalVarsChecker ignore raw references and pointers to a ref counted type which appears within "trival" statements. To do this, this PR extends TrivialFunctionAnalysis so that it can also analyze "triviality" of statements as well as that of functions Each Visit* function is now augmented with withCachedResult, which is responsible for looking up and updating the cache for each Visit* functions. As this PR dramatically improves the false positive rate of the checker, it also deletes the code to ignore raw pointers and references within if and for statements. --- .../Checkers/WebKit/PtrTypesSemantics.cpp | 222 -- .../Checkers/WebKit/PtrTypesSemantics.h | 21 +- .../WebKit/UncountedLocalVarsChecker.cpp | 69 +++--- .../Analysis/Checkers/WebKit/mock-types.h | 2 + .../Checkers/WebKit/uncounted-local-vars.cpp | 92 +++- 5 files changed, 285 insertions(+), 121 deletions(-) diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp b/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp index 01b191ab0eeaf4..6c9a8aedb38a4c 100644 --- a/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp @@ -245,18 +245,41 @@ class TrivialFunctionAnalysisVisitor // Returns false if at least one child is non-trivial. bool VisitChildren(const Stmt *S) { -for (const Stmt *Child : S->children()) { - if (Child && !Visit(Child)) +return withCachedResult(S, [&]() { + for (const Stmt *Child : S->children()) { +if (Child && !Visit(Child)) + return false; + } + return true; +}); + } + + bool VisitSubExpr(const Expr *Parent, const Expr *E) { +return withCachedResult(Parent, [&]() { + if (!Visit(E)) return false; -} + return true; +}); + } -return true; + template + bool withCachedResult(const StmtType *S, CheckFunction Function) { +// Insert false to the cache first to avoid infinite recursion. +auto [It, IsNew] = StatementCache.insert(std::make_pair(S, false)); +if (!IsNew) + return It->second; +bool Result = Function(); +It->second = Result; +return Result; } public: - using CacheTy = TrivialFunctionAnalysis::CacheTy; + using FunctionCacheTy = TrivialFunctionAnalysis::FunctionCacheTy; + using StatementCacheTy = TrivialFunctionAnalysis::StatementCacheTy; - TrivialFunctionAnalysisVisitor(CacheTy &Cache) : Cache(Cache) {} + TrivialFunctionAnalysisVisitor(FunctionCacheTy &FunctionCache, + StatementCacheTy &StatementCache) + : FunctionCache(FunctionCache), StatementCache(StatementCache) {} bool VisitStmt(const Stmt *S) { // All statements are non-trivial unless overriden later. @@ -272,13 +295,21 @@ class TrivialFunctionAnalysisVisitor bool VisitReturnStmt(const ReturnStmt *RS) { // A return statement is allowed as long as the return value is trivial. -if (auto *RV = RS->getRetValue()) - return Visit(RV); -return true; +return withCachedResult(RS, [&]() { + if (auto *RV = RS->getRetValue()) +return Visit(RV); + return true; +}); + } + + bool VisitCXXForRangeStmt(const CXXForRangeStmt *FS) { +return VisitChildren(FS); } bool VisitDeclStmt(const DeclStmt *DS) { return VisitChildren(DS); } bool VisitDoStmt(const DoStmt *DS) { return VisitChildren(DS); } + bool VisitForStmt(const ForStmt *FS) { return VisitChildren(FS); } + bool VisitWhileStmt(const WhileStmt *WS) { return VisitChildren(WS); } bool VisitIfStmt(const IfStmt *IS) { return VisitChildren(IS); } bool VisitSwitchStmt(const SwitchStmt *SS) { return VisitChildren(SS); } bool VisitCaseStmt(const CaseStmt *CS) { return VisitChildren(CS); } @@ -286,17 +317,26 @@ class TrivialFunctionAnalysisVisitor bool VisitUnaryOperator(const UnaryOperator *UO) { // Operator '*' and '!' are allowed as long as the operand is trivial. -if (UO->getOpcode() == UO_Deref || UO->getOpcode() == UO_AddrOf || -UO->getOpcode() == UO_LNot) - return Visit(UO->getSubExpr()); - -// Other operators are non-trivial. -return false; +return withCachedResult(UO, [&]() { + auto op = UO->getOpcode(); + if (op == UO_Deref || op == UO_AddrOf || op == UO_LNot) +return Visit(UO->getSubExpr()); + if (UO->isIncrementOp() || UO->isDecrementOp()) { +if (auto *RefExpr = dyn_cast(UO->getSubExpr())) { + if (auto *Decl = dyn_cast(RefExpr->getDecl())) +return Decl->i
[clang] [alpha.webkit.UncountedLocalVarsChecker] Allow uncounted object references within trivial statements (PR #82229)
@@ -305,19 +337,21 @@ class TrivialFunctionAnalysisVisitor } bool VisitDeclRefExpr(const DeclRefExpr *DRE) { -if (auto *decl = DRE->getDecl()) { - if (isa(decl)) -return true; - if (isa(decl)) -return true; - if (auto *VD = dyn_cast(decl)) { -if (VD->hasConstantInitialization() && VD->getEvaluatedValue()) +return WithCachedResult(DRE, [&]() { + if (auto *decl = DRE->getDecl()) { +if (isa(decl)) return true; -auto *Init = VD->getInit(); -return !Init || Visit(Init); +if (isa(decl)) + return true; +if (auto *VD = dyn_cast(decl)) { + if (VD->hasConstantInitialization() && VD->getEvaluatedValue()) +return true; + auto *Init = VD->getInit(); + return !Init || Visit(Init); rniwa wrote: Oh, that is a very good point. I guess we can just always `return true` for `DeclRefExpr`. https://github.com/llvm/llvm-project/pull/82229 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [alpha.webkit.UncountedLocalVarsChecker] Allow uncounted object references within trivial statements (PR #82229)
https://github.com/rniwa updated https://github.com/llvm/llvm-project/pull/82229 >From 234e301ab2721ddb2f4b43589785015a7d0aa304 Mon Sep 17 00:00:00 2001 From: Ryosuke Niwa Date: Mon, 19 Feb 2024 01:07:13 -0800 Subject: [PATCH 1/9] [alpha.webkit.UncountedLocalVarsChecker] Allow uncounted object references within trivial statements This PR makes alpha.webkit.UncountedLocalVarsChecker ignore raw references and pointers to a ref counted type which appears within "trival" statements. To do this, this PR extends TrivialFunctionAnalysis so that it can also analyze "triviality" of statements as well as that of functions Each Visit* function is now augmented with withCachedResult, which is responsible for looking up and updating the cache for each Visit* functions. As this PR dramatically improves the false positive rate of the checker, it also deletes the code to ignore raw pointers and references within if and for statements. --- .../Checkers/WebKit/PtrTypesSemantics.cpp | 222 -- .../Checkers/WebKit/PtrTypesSemantics.h | 21 +- .../WebKit/UncountedLocalVarsChecker.cpp | 69 +++--- .../Analysis/Checkers/WebKit/mock-types.h | 2 + .../Checkers/WebKit/uncounted-local-vars.cpp | 92 +++- 5 files changed, 285 insertions(+), 121 deletions(-) diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp b/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp index 01b191ab0eeaf4..6c9a8aedb38a4c 100644 --- a/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp @@ -245,18 +245,41 @@ class TrivialFunctionAnalysisVisitor // Returns false if at least one child is non-trivial. bool VisitChildren(const Stmt *S) { -for (const Stmt *Child : S->children()) { - if (Child && !Visit(Child)) +return withCachedResult(S, [&]() { + for (const Stmt *Child : S->children()) { +if (Child && !Visit(Child)) + return false; + } + return true; +}); + } + + bool VisitSubExpr(const Expr *Parent, const Expr *E) { +return withCachedResult(Parent, [&]() { + if (!Visit(E)) return false; -} + return true; +}); + } -return true; + template + bool withCachedResult(const StmtType *S, CheckFunction Function) { +// Insert false to the cache first to avoid infinite recursion. +auto [It, IsNew] = StatementCache.insert(std::make_pair(S, false)); +if (!IsNew) + return It->second; +bool Result = Function(); +It->second = Result; +return Result; } public: - using CacheTy = TrivialFunctionAnalysis::CacheTy; + using FunctionCacheTy = TrivialFunctionAnalysis::FunctionCacheTy; + using StatementCacheTy = TrivialFunctionAnalysis::StatementCacheTy; - TrivialFunctionAnalysisVisitor(CacheTy &Cache) : Cache(Cache) {} + TrivialFunctionAnalysisVisitor(FunctionCacheTy &FunctionCache, + StatementCacheTy &StatementCache) + : FunctionCache(FunctionCache), StatementCache(StatementCache) {} bool VisitStmt(const Stmt *S) { // All statements are non-trivial unless overriden later. @@ -272,13 +295,21 @@ class TrivialFunctionAnalysisVisitor bool VisitReturnStmt(const ReturnStmt *RS) { // A return statement is allowed as long as the return value is trivial. -if (auto *RV = RS->getRetValue()) - return Visit(RV); -return true; +return withCachedResult(RS, [&]() { + if (auto *RV = RS->getRetValue()) +return Visit(RV); + return true; +}); + } + + bool VisitCXXForRangeStmt(const CXXForRangeStmt *FS) { +return VisitChildren(FS); } bool VisitDeclStmt(const DeclStmt *DS) { return VisitChildren(DS); } bool VisitDoStmt(const DoStmt *DS) { return VisitChildren(DS); } + bool VisitForStmt(const ForStmt *FS) { return VisitChildren(FS); } + bool VisitWhileStmt(const WhileStmt *WS) { return VisitChildren(WS); } bool VisitIfStmt(const IfStmt *IS) { return VisitChildren(IS); } bool VisitSwitchStmt(const SwitchStmt *SS) { return VisitChildren(SS); } bool VisitCaseStmt(const CaseStmt *CS) { return VisitChildren(CS); } @@ -286,17 +317,26 @@ class TrivialFunctionAnalysisVisitor bool VisitUnaryOperator(const UnaryOperator *UO) { // Operator '*' and '!' are allowed as long as the operand is trivial. -if (UO->getOpcode() == UO_Deref || UO->getOpcode() == UO_AddrOf || -UO->getOpcode() == UO_LNot) - return Visit(UO->getSubExpr()); - -// Other operators are non-trivial. -return false; +return withCachedResult(UO, [&]() { + auto op = UO->getOpcode(); + if (op == UO_Deref || op == UO_AddrOf || op == UO_LNot) +return Visit(UO->getSubExpr()); + if (UO->isIncrementOp() || UO->isDecrementOp()) { +if (auto *RefExpr = dyn_cast(UO->getSubExpr())) { + if (auto *Decl = dyn_cast(RefExpr->getDecl())) +return Decl->i
[clang] [alpha.webkit.UncountedLocalVarsChecker] Allow uncounted object references within trivial statements (PR #82229)
https://github.com/rniwa closed https://github.com/llvm/llvm-project/pull/82229 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [alpha.webkit.UncountedCallArgsChecker] Don't assume local variables are safe & treat guarded local variable as safe function arguments (PR #82305)
https://github.com/rniwa closed https://github.com/llvm/llvm-project/pull/82305 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [alpha.webkit.UncountedCallArgsChecker] Don't assume local variables are safe & treat guarded local variable as safe function arguments (PR #82305)
rniwa wrote: So this PR turned out to be not really correct because we can't get rid of local variable checker when calling a non-trivial function. https://github.com/llvm/llvm-project/pull/82305 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [alpha.webkit.UncountedCallArgsChecker] Don't assume local variables are safe & treat guarded local variable as safe function arguments (PR #82305)
rniwa wrote: haha, sorry for the confusion. I don't have any outstanding PR at this point. Thanks for all the reviews :) https://github.com/llvm/llvm-project/pull/82305 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [alpha.webkit.UncountedCallArgsChecker] Allow a variable declaration in a trivial function. (PR #82291)
rniwa wrote: Thanks for the review! https://github.com/llvm/llvm-project/pull/82291 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [alpha.webkit.UncountedCallArgsChecker] Allow a variable declaration in a trivial function. (PR #82291)
https://github.com/rniwa updated https://github.com/llvm/llvm-project/pull/82291 >From 0a8cfb11a601e7a6ec59489b8ac15c40136002b2 Mon Sep 17 00:00:00 2001 From: Ryosuke Niwa Date: Sat, 17 Feb 2024 18:12:16 -0800 Subject: [PATCH] [alpha.webkit.UncountedCallArgsChecker] Allow a variable declaration in a trivial function. --- .../Checkers/WebKit/PtrTypesSemantics.cpp | 8 ++-- .../Checkers/WebKit/uncounted-obj-arg.cpp | 17 + 2 files changed, 23 insertions(+), 2 deletions(-) diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp b/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp index a7891d2da07c18..09c42434513177 100644 --- a/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp @@ -309,8 +309,12 @@ class TrivialFunctionAnalysisVisitor return true; if (isa(decl)) return true; - if (auto *VD = dyn_cast(decl)) -return VD->hasConstantInitialization() && VD->getEvaluatedValue(); + if (auto *VD = dyn_cast(decl)) { +if (VD->hasConstantInitialization() && VD->getEvaluatedValue()) + return true; +auto *Init = VD->getInit(); +return !Init || Visit(Init); + } } return false; } diff --git a/clang/test/Analysis/Checkers/WebKit/uncounted-obj-arg.cpp b/clang/test/Analysis/Checkers/WebKit/uncounted-obj-arg.cpp index ac16a31293f3de..80a9a263dab140 100644 --- a/clang/test/Analysis/Checkers/WebKit/uncounted-obj-arg.cpp +++ b/clang/test/Analysis/Checkers/WebKit/uncounted-obj-arg.cpp @@ -199,6 +199,8 @@ class RefCounted { bool trivial23() const { return OptionSet::fromRaw(v).contains(Flags::Flag1); } int trivial24() const { ASSERT(v); return v; } unsigned trivial25() const { return __c11_atomic_load((volatile _Atomic(unsigned) *)&v, __ATOMIC_RELAXED); } + bool trivial26() { bool hasValue = v; return !hasValue; } + bool trivial27(int v) { bool value; value = v ? 1 : 0; return value; } static RefCounted& singleton() { static RefCounted s_RefCounted; @@ -262,6 +264,15 @@ class RefCounted { return __c11_atomic_load((volatile _Atomic(unsigned) *)another(), __ATOMIC_RELAXED); } + void nonTrivial11() { +Number num(0.3); + } + + bool nonTrivial12() { +bool val = otherFunction(); +return val; + } + unsigned v { 0 }; Number* number { nullptr }; Enum enumValue { Enum::Value1 }; @@ -309,6 +320,8 @@ class UnrelatedClass { getFieldTrivial().trivial23(); // no-warning getFieldTrivial().trivial24(); // no-warning getFieldTrivial().trivial25(); // no-warning +getFieldTrivial().trivial26(); // no-warning +getFieldTrivial().trivial27(5); // no-warning RefCounted::singleton().trivial18(); // no-warning RefCounted::singleton().someFunction(); // no-warning @@ -334,6 +347,10 @@ class UnrelatedClass { // expected-warning@-1{{Call argument for 'this' parameter is uncounted and unsafe}} getFieldTrivial().nonTrivial10(); // expected-warning@-1{{Call argument for 'this' parameter is uncounted and unsafe}} +getFieldTrivial().nonTrivial11(); +// expected-warning@-1{{Call argument for 'this' parameter is uncounted and unsafe}} +getFieldTrivial().nonTrivial12(); +// expected-warning@-1{{Call argument for 'this' parameter is uncounted and unsafe}} } }; ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [alpha.webkit.UncountedCallArgsChecker] Allow a variable declaration in a trivial function. (PR #82291)
https://github.com/rniwa closed https://github.com/llvm/llvm-project/pull/82291 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [alpha.webkit.UncountedLocalVarsChecker] Allow uncounted object references within trivial statements (PR #82229)
https://github.com/rniwa updated https://github.com/llvm/llvm-project/pull/82229 >From 099640652b62c52160b12542a16eb66da90cfc93 Mon Sep 17 00:00:00 2001 From: Ryosuke Niwa Date: Mon, 19 Feb 2024 01:07:13 -0800 Subject: [PATCH 1/2] [alpha.webkit.UncountedLocalVarsChecker] Allow uncounted object references within trivial statements This PR makes alpha.webkit.UncountedLocalVarsChecker ignore raw references and pointers to a ref counted type which appears within "trival" statements. To do this, this PR extends TrivialFunctionAnalysis so that it can also analyze "triviality" of statements as well as that of functions Each Visit* function is now augmented with withCachedResult, which is responsible for looking up and updating the cache for each Visit* functions. As this PR dramatically improves the false positive rate of the checker, it also deletes the code to ignore raw pointers and references within if and for statements. --- .../Checkers/WebKit/PtrTypesSemantics.cpp | 221 -- .../Checkers/WebKit/PtrTypesSemantics.h | 21 +- .../WebKit/UncountedLocalVarsChecker.cpp | 69 +++--- .../Analysis/Checkers/WebKit/mock-types.h | 2 + .../Checkers/WebKit/uncounted-local-vars.cpp | 92 +++- 5 files changed, 286 insertions(+), 119 deletions(-) diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp b/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp index a7891d2da07c18..cb89ce59a60878 100644 --- a/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp @@ -244,18 +244,41 @@ class TrivialFunctionAnalysisVisitor // Returns false if at least one child is non-trivial. bool VisitChildren(const Stmt *S) { -for (const Stmt *Child : S->children()) { - if (Child && !Visit(Child)) +return withCachedResult(S, [&]() { + for (const Stmt *Child : S->children()) { +if (Child && !Visit(Child)) + return false; + } + return true; +}); + } + + bool VisitSubExpr(const Expr *Parent, const Expr *E) { +return withCachedResult(Parent, [&]() { + if (!Visit(E)) return false; -} + return true; +}); + } -return true; + template + bool withCachedResult(const StmtType *S, CheckFunction Function) { +// Insert false to the cache first to avoid infinite recursion. +auto [It, IsNew] = StatementCache.insert(std::make_pair(S, false)); +if (!IsNew) + return It->second; +bool Result = Function(); +It->second = Result; +return Result; } public: - using CacheTy = TrivialFunctionAnalysis::CacheTy; + using FunctionCacheTy = TrivialFunctionAnalysis::FunctionCacheTy; + using StatementCacheTy = TrivialFunctionAnalysis::StatementCacheTy; - TrivialFunctionAnalysisVisitor(CacheTy &Cache) : Cache(Cache) {} + TrivialFunctionAnalysisVisitor(FunctionCacheTy &FunctionCache, + StatementCacheTy &StatementCache) + : FunctionCache(FunctionCache), StatementCache(StatementCache) {} bool VisitStmt(const Stmt *S) { // All statements are non-trivial unless overriden later. @@ -271,13 +294,21 @@ class TrivialFunctionAnalysisVisitor bool VisitReturnStmt(const ReturnStmt *RS) { // A return statement is allowed as long as the return value is trivial. -if (auto *RV = RS->getRetValue()) - return Visit(RV); -return true; +return withCachedResult(RS, [&]() { + if (auto *RV = RS->getRetValue()) +return Visit(RV); + return true; +}); + } + + bool VisitCXXForRangeStmt(const CXXForRangeStmt *FS) { +return VisitChildren(FS); } bool VisitDeclStmt(const DeclStmt *DS) { return VisitChildren(DS); } bool VisitDoStmt(const DoStmt *DS) { return VisitChildren(DS); } + bool VisitForStmt(const ForStmt *FS) { return VisitChildren(FS); } + bool VisitWhileStmt(const WhileStmt *WS) { return VisitChildren(WS); } bool VisitIfStmt(const IfStmt *IS) { return VisitChildren(IS); } bool VisitSwitchStmt(const SwitchStmt *SS) { return VisitChildren(SS); } bool VisitCaseStmt(const CaseStmt *CS) { return VisitChildren(CS); } @@ -285,17 +316,26 @@ class TrivialFunctionAnalysisVisitor bool VisitUnaryOperator(const UnaryOperator *UO) { // Operator '*' and '!' are allowed as long as the operand is trivial. -if (UO->getOpcode() == UO_Deref || UO->getOpcode() == UO_AddrOf || -UO->getOpcode() == UO_LNot) - return Visit(UO->getSubExpr()); - -// Other operators are non-trivial. -return false; +return withCachedResult(UO, [&]() { + auto op = UO->getOpcode(); + if (op == UO_Deref || op == UO_AddrOf || op == UO_LNot) +return Visit(UO->getSubExpr()); + if (UO->isIncrementOp() || UO->isDecrementOp()) { +if (auto *RefExpr = dyn_cast(UO->getSubExpr())) { + if (auto *Decl = dyn_cast(RefExpr->getDecl())) +return Decl->i
[clang] [alpha.webkit.UncountedLocalVarsChecker] Allow uncounted object references within trivial statements (PR #82229)
https://github.com/rniwa updated https://github.com/llvm/llvm-project/pull/82229 >From 099640652b62c52160b12542a16eb66da90cfc93 Mon Sep 17 00:00:00 2001 From: Ryosuke Niwa Date: Mon, 19 Feb 2024 01:07:13 -0800 Subject: [PATCH 1/2] [alpha.webkit.UncountedLocalVarsChecker] Allow uncounted object references within trivial statements This PR makes alpha.webkit.UncountedLocalVarsChecker ignore raw references and pointers to a ref counted type which appears within "trival" statements. To do this, this PR extends TrivialFunctionAnalysis so that it can also analyze "triviality" of statements as well as that of functions Each Visit* function is now augmented with withCachedResult, which is responsible for looking up and updating the cache for each Visit* functions. As this PR dramatically improves the false positive rate of the checker, it also deletes the code to ignore raw pointers and references within if and for statements. --- .../Checkers/WebKit/PtrTypesSemantics.cpp | 221 -- .../Checkers/WebKit/PtrTypesSemantics.h | 21 +- .../WebKit/UncountedLocalVarsChecker.cpp | 69 +++--- .../Analysis/Checkers/WebKit/mock-types.h | 2 + .../Checkers/WebKit/uncounted-local-vars.cpp | 92 +++- 5 files changed, 286 insertions(+), 119 deletions(-) diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp b/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp index a7891d2da07c18..cb89ce59a60878 100644 --- a/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp @@ -244,18 +244,41 @@ class TrivialFunctionAnalysisVisitor // Returns false if at least one child is non-trivial. bool VisitChildren(const Stmt *S) { -for (const Stmt *Child : S->children()) { - if (Child && !Visit(Child)) +return withCachedResult(S, [&]() { + for (const Stmt *Child : S->children()) { +if (Child && !Visit(Child)) + return false; + } + return true; +}); + } + + bool VisitSubExpr(const Expr *Parent, const Expr *E) { +return withCachedResult(Parent, [&]() { + if (!Visit(E)) return false; -} + return true; +}); + } -return true; + template + bool withCachedResult(const StmtType *S, CheckFunction Function) { +// Insert false to the cache first to avoid infinite recursion. +auto [It, IsNew] = StatementCache.insert(std::make_pair(S, false)); +if (!IsNew) + return It->second; +bool Result = Function(); +It->second = Result; +return Result; } public: - using CacheTy = TrivialFunctionAnalysis::CacheTy; + using FunctionCacheTy = TrivialFunctionAnalysis::FunctionCacheTy; + using StatementCacheTy = TrivialFunctionAnalysis::StatementCacheTy; - TrivialFunctionAnalysisVisitor(CacheTy &Cache) : Cache(Cache) {} + TrivialFunctionAnalysisVisitor(FunctionCacheTy &FunctionCache, + StatementCacheTy &StatementCache) + : FunctionCache(FunctionCache), StatementCache(StatementCache) {} bool VisitStmt(const Stmt *S) { // All statements are non-trivial unless overriden later. @@ -271,13 +294,21 @@ class TrivialFunctionAnalysisVisitor bool VisitReturnStmt(const ReturnStmt *RS) { // A return statement is allowed as long as the return value is trivial. -if (auto *RV = RS->getRetValue()) - return Visit(RV); -return true; +return withCachedResult(RS, [&]() { + if (auto *RV = RS->getRetValue()) +return Visit(RV); + return true; +}); + } + + bool VisitCXXForRangeStmt(const CXXForRangeStmt *FS) { +return VisitChildren(FS); } bool VisitDeclStmt(const DeclStmt *DS) { return VisitChildren(DS); } bool VisitDoStmt(const DoStmt *DS) { return VisitChildren(DS); } + bool VisitForStmt(const ForStmt *FS) { return VisitChildren(FS); } + bool VisitWhileStmt(const WhileStmt *WS) { return VisitChildren(WS); } bool VisitIfStmt(const IfStmt *IS) { return VisitChildren(IS); } bool VisitSwitchStmt(const SwitchStmt *SS) { return VisitChildren(SS); } bool VisitCaseStmt(const CaseStmt *CS) { return VisitChildren(CS); } @@ -285,17 +316,26 @@ class TrivialFunctionAnalysisVisitor bool VisitUnaryOperator(const UnaryOperator *UO) { // Operator '*' and '!' are allowed as long as the operand is trivial. -if (UO->getOpcode() == UO_Deref || UO->getOpcode() == UO_AddrOf || -UO->getOpcode() == UO_LNot) - return Visit(UO->getSubExpr()); - -// Other operators are non-trivial. -return false; +return withCachedResult(UO, [&]() { + auto op = UO->getOpcode(); + if (op == UO_Deref || op == UO_AddrOf || op == UO_LNot) +return Visit(UO->getSubExpr()); + if (UO->isIncrementOp() || UO->isDecrementOp()) { +if (auto *RefExpr = dyn_cast(UO->getSubExpr())) { + if (auto *Decl = dyn_cast(RefExpr->getDecl())) +return Decl->i
[clang] [alpha.webkit.UncountedLocalVarsChecker] Allow uncounted object references within trivial statements (PR #82229)
@@ -70,15 +71,27 @@ bool isSingleton(const FunctionDecl *F); class TrivialFunctionAnalysis { public: /// \returns true if \p D is a "trivial" function. - bool isTrivial(const Decl *D) const { return isTrivialImpl(D, TheCache); } + bool isTrivial(const Decl *D) const { +return isTrivialImpl(D, TheFunctionCache, TheStatementCache); + } + + bool isTrivial(const Stmt *S) const { +return isTrivialImpl(S, TheFunctionCache, TheStatementCache); + } private: friend class TrivialFunctionAnalysisVisitor; - using CacheTy = llvm::DenseMap; - mutable CacheTy TheCache{}; + using FunctionCacheTy = llvm::DenseMap; + mutable FunctionCacheTy TheFunctionCache{}; + + using StatementCacheTy = llvm::DenseMap; + mutable StatementCacheTy TheStatementCache{}; rniwa wrote: Okay, done that. https://github.com/llvm/llvm-project/pull/82229 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [alpha.webkit.UncountedLocalVarsChecker] Allow uncounted object references within trivial statements (PR #82229)
https://github.com/rniwa updated https://github.com/llvm/llvm-project/pull/82229 >From fd171b82d03b29926984b5b835ad9c0ccf197536 Mon Sep 17 00:00:00 2001 From: Ryosuke Niwa Date: Mon, 19 Feb 2024 01:07:13 -0800 Subject: [PATCH 1/3] [alpha.webkit.UncountedLocalVarsChecker] Allow uncounted object references within trivial statements This PR makes alpha.webkit.UncountedLocalVarsChecker ignore raw references and pointers to a ref counted type which appears within "trival" statements. To do this, this PR extends TrivialFunctionAnalysis so that it can also analyze "triviality" of statements as well as that of functions Each Visit* function is now augmented with withCachedResult, which is responsible for looking up and updating the cache for each Visit* functions. As this PR dramatically improves the false positive rate of the checker, it also deletes the code to ignore raw pointers and references within if and for statements. --- .../Checkers/WebKit/PtrTypesSemantics.cpp | 221 -- .../Checkers/WebKit/PtrTypesSemantics.h | 21 +- .../WebKit/UncountedLocalVarsChecker.cpp | 69 +++--- .../Analysis/Checkers/WebKit/mock-types.h | 2 + .../Checkers/WebKit/uncounted-local-vars.cpp | 92 +++- 5 files changed, 286 insertions(+), 119 deletions(-) diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp b/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp index defd83ec8e179c1..904781e6ea72ae2 100644 --- a/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp @@ -245,18 +245,41 @@ class TrivialFunctionAnalysisVisitor // Returns false if at least one child is non-trivial. bool VisitChildren(const Stmt *S) { -for (const Stmt *Child : S->children()) { - if (Child && !Visit(Child)) +return withCachedResult(S, [&]() { + for (const Stmt *Child : S->children()) { +if (Child && !Visit(Child)) + return false; + } + return true; +}); + } + + bool VisitSubExpr(const Expr *Parent, const Expr *E) { +return withCachedResult(Parent, [&]() { + if (!Visit(E)) return false; -} + return true; +}); + } -return true; + template + bool withCachedResult(const StmtType *S, CheckFunction Function) { +// Insert false to the cache first to avoid infinite recursion. +auto [It, IsNew] = StatementCache.insert(std::make_pair(S, false)); +if (!IsNew) + return It->second; +bool Result = Function(); +It->second = Result; +return Result; } public: - using CacheTy = TrivialFunctionAnalysis::CacheTy; + using FunctionCacheTy = TrivialFunctionAnalysis::FunctionCacheTy; + using StatementCacheTy = TrivialFunctionAnalysis::StatementCacheTy; - TrivialFunctionAnalysisVisitor(CacheTy &Cache) : Cache(Cache) {} + TrivialFunctionAnalysisVisitor(FunctionCacheTy &FunctionCache, + StatementCacheTy &StatementCache) + : FunctionCache(FunctionCache), StatementCache(StatementCache) {} bool VisitStmt(const Stmt *S) { // All statements are non-trivial unless overriden later. @@ -272,13 +295,21 @@ class TrivialFunctionAnalysisVisitor bool VisitReturnStmt(const ReturnStmt *RS) { // A return statement is allowed as long as the return value is trivial. -if (auto *RV = RS->getRetValue()) - return Visit(RV); -return true; +return withCachedResult(RS, [&]() { + if (auto *RV = RS->getRetValue()) +return Visit(RV); + return true; +}); + } + + bool VisitCXXForRangeStmt(const CXXForRangeStmt *FS) { +return VisitChildren(FS); } bool VisitDeclStmt(const DeclStmt *DS) { return VisitChildren(DS); } bool VisitDoStmt(const DoStmt *DS) { return VisitChildren(DS); } + bool VisitForStmt(const ForStmt *FS) { return VisitChildren(FS); } + bool VisitWhileStmt(const WhileStmt *WS) { return VisitChildren(WS); } bool VisitIfStmt(const IfStmt *IS) { return VisitChildren(IS); } bool VisitSwitchStmt(const SwitchStmt *SS) { return VisitChildren(SS); } bool VisitCaseStmt(const CaseStmt *CS) { return VisitChildren(CS); } @@ -286,17 +317,26 @@ class TrivialFunctionAnalysisVisitor bool VisitUnaryOperator(const UnaryOperator *UO) { // Operator '*' and '!' are allowed as long as the operand is trivial. -if (UO->getOpcode() == UO_Deref || UO->getOpcode() == UO_AddrOf || -UO->getOpcode() == UO_LNot) - return Visit(UO->getSubExpr()); - -// Other operators are non-trivial. -return false; +return withCachedResult(UO, [&]() { + auto op = UO->getOpcode(); + if (op == UO_Deref || op == UO_AddrOf || op == UO_LNot) +return Visit(UO->getSubExpr()); + if (UO->isIncrementOp() || UO->isDecrementOp()) { +if (auto *RefExpr = dyn_cast(UO->getSubExpr())) { + if (auto *Decl = dyn_cast(RefExpr->getDecl())) +return Decl-
[clang] [alpha.webkit.UncountedLocalVarsChecker] Allow uncounted object references within trivial statements (PR #82229)
https://github.com/rniwa updated https://github.com/llvm/llvm-project/pull/82229 >From fd171b82d03b29926984b5b835ad9c0ccf197536 Mon Sep 17 00:00:00 2001 From: Ryosuke Niwa Date: Mon, 19 Feb 2024 01:07:13 -0800 Subject: [PATCH 1/4] [alpha.webkit.UncountedLocalVarsChecker] Allow uncounted object references within trivial statements This PR makes alpha.webkit.UncountedLocalVarsChecker ignore raw references and pointers to a ref counted type which appears within "trival" statements. To do this, this PR extends TrivialFunctionAnalysis so that it can also analyze "triviality" of statements as well as that of functions Each Visit* function is now augmented with withCachedResult, which is responsible for looking up and updating the cache for each Visit* functions. As this PR dramatically improves the false positive rate of the checker, it also deletes the code to ignore raw pointers and references within if and for statements. --- .../Checkers/WebKit/PtrTypesSemantics.cpp | 221 -- .../Checkers/WebKit/PtrTypesSemantics.h | 21 +- .../WebKit/UncountedLocalVarsChecker.cpp | 69 +++--- .../Analysis/Checkers/WebKit/mock-types.h | 2 + .../Checkers/WebKit/uncounted-local-vars.cpp | 92 +++- 5 files changed, 286 insertions(+), 119 deletions(-) diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp b/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp index defd83ec8e179c1..904781e6ea72ae2 100644 --- a/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp @@ -245,18 +245,41 @@ class TrivialFunctionAnalysisVisitor // Returns false if at least one child is non-trivial. bool VisitChildren(const Stmt *S) { -for (const Stmt *Child : S->children()) { - if (Child && !Visit(Child)) +return withCachedResult(S, [&]() { + for (const Stmt *Child : S->children()) { +if (Child && !Visit(Child)) + return false; + } + return true; +}); + } + + bool VisitSubExpr(const Expr *Parent, const Expr *E) { +return withCachedResult(Parent, [&]() { + if (!Visit(E)) return false; -} + return true; +}); + } -return true; + template + bool withCachedResult(const StmtType *S, CheckFunction Function) { +// Insert false to the cache first to avoid infinite recursion. +auto [It, IsNew] = StatementCache.insert(std::make_pair(S, false)); +if (!IsNew) + return It->second; +bool Result = Function(); +It->second = Result; +return Result; } public: - using CacheTy = TrivialFunctionAnalysis::CacheTy; + using FunctionCacheTy = TrivialFunctionAnalysis::FunctionCacheTy; + using StatementCacheTy = TrivialFunctionAnalysis::StatementCacheTy; - TrivialFunctionAnalysisVisitor(CacheTy &Cache) : Cache(Cache) {} + TrivialFunctionAnalysisVisitor(FunctionCacheTy &FunctionCache, + StatementCacheTy &StatementCache) + : FunctionCache(FunctionCache), StatementCache(StatementCache) {} bool VisitStmt(const Stmt *S) { // All statements are non-trivial unless overriden later. @@ -272,13 +295,21 @@ class TrivialFunctionAnalysisVisitor bool VisitReturnStmt(const ReturnStmt *RS) { // A return statement is allowed as long as the return value is trivial. -if (auto *RV = RS->getRetValue()) - return Visit(RV); -return true; +return withCachedResult(RS, [&]() { + if (auto *RV = RS->getRetValue()) +return Visit(RV); + return true; +}); + } + + bool VisitCXXForRangeStmt(const CXXForRangeStmt *FS) { +return VisitChildren(FS); } bool VisitDeclStmt(const DeclStmt *DS) { return VisitChildren(DS); } bool VisitDoStmt(const DoStmt *DS) { return VisitChildren(DS); } + bool VisitForStmt(const ForStmt *FS) { return VisitChildren(FS); } + bool VisitWhileStmt(const WhileStmt *WS) { return VisitChildren(WS); } bool VisitIfStmt(const IfStmt *IS) { return VisitChildren(IS); } bool VisitSwitchStmt(const SwitchStmt *SS) { return VisitChildren(SS); } bool VisitCaseStmt(const CaseStmt *CS) { return VisitChildren(CS); } @@ -286,17 +317,26 @@ class TrivialFunctionAnalysisVisitor bool VisitUnaryOperator(const UnaryOperator *UO) { // Operator '*' and '!' are allowed as long as the operand is trivial. -if (UO->getOpcode() == UO_Deref || UO->getOpcode() == UO_AddrOf || -UO->getOpcode() == UO_LNot) - return Visit(UO->getSubExpr()); - -// Other operators are non-trivial. -return false; +return withCachedResult(UO, [&]() { + auto op = UO->getOpcode(); + if (op == UO_Deref || op == UO_AddrOf || op == UO_LNot) +return Visit(UO->getSubExpr()); + if (UO->isIncrementOp() || UO->isDecrementOp()) { +if (auto *RefExpr = dyn_cast(UO->getSubExpr())) { + if (auto *Decl = dyn_cast(RefExpr->getDecl())) +return Decl-
[clang] [alpha.webkit.UncountedLocalVarsChecker] Allow uncounted object references within trivial statements (PR #82229)
@@ -171,6 +151,24 @@ class UncountedLocalVarsChecker std::optional IsUncountedPtr = isUncountedPtr(ArgType); if (IsUncountedPtr && *IsUncountedPtr) { + + ASTContext &ctx = V->getASTContext(); + for (DynTypedNodeList ancestors = ctx.getParents(*V); !ancestors.empty(); + ancestors = ctx.getParents(*ancestors.begin())) { rniwa wrote: Okay, I've rewritten to do that instead. Indeed, this looks cleaner. https://github.com/llvm/llvm-project/pull/82229 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [alpha.webkit.UncountedLocalVarsChecker] Allow uncounted object references within trivial statements (PR #82229)
https://github.com/rniwa updated https://github.com/llvm/llvm-project/pull/82229 >From fd171b82d03b29926984b5b835ad9c0ccf197536 Mon Sep 17 00:00:00 2001 From: Ryosuke Niwa Date: Mon, 19 Feb 2024 01:07:13 -0800 Subject: [PATCH 1/5] [alpha.webkit.UncountedLocalVarsChecker] Allow uncounted object references within trivial statements This PR makes alpha.webkit.UncountedLocalVarsChecker ignore raw references and pointers to a ref counted type which appears within "trival" statements. To do this, this PR extends TrivialFunctionAnalysis so that it can also analyze "triviality" of statements as well as that of functions Each Visit* function is now augmented with withCachedResult, which is responsible for looking up and updating the cache for each Visit* functions. As this PR dramatically improves the false positive rate of the checker, it also deletes the code to ignore raw pointers and references within if and for statements. --- .../Checkers/WebKit/PtrTypesSemantics.cpp | 221 -- .../Checkers/WebKit/PtrTypesSemantics.h | 21 +- .../WebKit/UncountedLocalVarsChecker.cpp | 69 +++--- .../Analysis/Checkers/WebKit/mock-types.h | 2 + .../Checkers/WebKit/uncounted-local-vars.cpp | 92 +++- 5 files changed, 286 insertions(+), 119 deletions(-) diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp b/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp index defd83ec8e179c1..904781e6ea72ae2 100644 --- a/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp @@ -245,18 +245,41 @@ class TrivialFunctionAnalysisVisitor // Returns false if at least one child is non-trivial. bool VisitChildren(const Stmt *S) { -for (const Stmt *Child : S->children()) { - if (Child && !Visit(Child)) +return withCachedResult(S, [&]() { + for (const Stmt *Child : S->children()) { +if (Child && !Visit(Child)) + return false; + } + return true; +}); + } + + bool VisitSubExpr(const Expr *Parent, const Expr *E) { +return withCachedResult(Parent, [&]() { + if (!Visit(E)) return false; -} + return true; +}); + } -return true; + template + bool withCachedResult(const StmtType *S, CheckFunction Function) { +// Insert false to the cache first to avoid infinite recursion. +auto [It, IsNew] = StatementCache.insert(std::make_pair(S, false)); +if (!IsNew) + return It->second; +bool Result = Function(); +It->second = Result; +return Result; } public: - using CacheTy = TrivialFunctionAnalysis::CacheTy; + using FunctionCacheTy = TrivialFunctionAnalysis::FunctionCacheTy; + using StatementCacheTy = TrivialFunctionAnalysis::StatementCacheTy; - TrivialFunctionAnalysisVisitor(CacheTy &Cache) : Cache(Cache) {} + TrivialFunctionAnalysisVisitor(FunctionCacheTy &FunctionCache, + StatementCacheTy &StatementCache) + : FunctionCache(FunctionCache), StatementCache(StatementCache) {} bool VisitStmt(const Stmt *S) { // All statements are non-trivial unless overriden later. @@ -272,13 +295,21 @@ class TrivialFunctionAnalysisVisitor bool VisitReturnStmt(const ReturnStmt *RS) { // A return statement is allowed as long as the return value is trivial. -if (auto *RV = RS->getRetValue()) - return Visit(RV); -return true; +return withCachedResult(RS, [&]() { + if (auto *RV = RS->getRetValue()) +return Visit(RV); + return true; +}); + } + + bool VisitCXXForRangeStmt(const CXXForRangeStmt *FS) { +return VisitChildren(FS); } bool VisitDeclStmt(const DeclStmt *DS) { return VisitChildren(DS); } bool VisitDoStmt(const DoStmt *DS) { return VisitChildren(DS); } + bool VisitForStmt(const ForStmt *FS) { return VisitChildren(FS); } + bool VisitWhileStmt(const WhileStmt *WS) { return VisitChildren(WS); } bool VisitIfStmt(const IfStmt *IS) { return VisitChildren(IS); } bool VisitSwitchStmt(const SwitchStmt *SS) { return VisitChildren(SS); } bool VisitCaseStmt(const CaseStmt *CS) { return VisitChildren(CS); } @@ -286,17 +317,26 @@ class TrivialFunctionAnalysisVisitor bool VisitUnaryOperator(const UnaryOperator *UO) { // Operator '*' and '!' are allowed as long as the operand is trivial. -if (UO->getOpcode() == UO_Deref || UO->getOpcode() == UO_AddrOf || -UO->getOpcode() == UO_LNot) - return Visit(UO->getSubExpr()); - -// Other operators are non-trivial. -return false; +return withCachedResult(UO, [&]() { + auto op = UO->getOpcode(); + if (op == UO_Deref || op == UO_AddrOf || op == UO_LNot) +return Visit(UO->getSubExpr()); + if (UO->isIncrementOp() || UO->isDecrementOp()) { +if (auto *RefExpr = dyn_cast(UO->getSubExpr())) { + if (auto *Decl = dyn_cast(RefExpr->getDecl())) +return Decl-
[clang] [alpha.webkit.UncountedLocalVarsChecker] Allow uncounted object references within trivial statements (PR #82229)
https://github.com/rniwa updated https://github.com/llvm/llvm-project/pull/82229 >From fd171b82d03b29926984b5b835ad9c0ccf197536 Mon Sep 17 00:00:00 2001 From: Ryosuke Niwa Date: Mon, 19 Feb 2024 01:07:13 -0800 Subject: [PATCH 1/5] [alpha.webkit.UncountedLocalVarsChecker] Allow uncounted object references within trivial statements This PR makes alpha.webkit.UncountedLocalVarsChecker ignore raw references and pointers to a ref counted type which appears within "trival" statements. To do this, this PR extends TrivialFunctionAnalysis so that it can also analyze "triviality" of statements as well as that of functions Each Visit* function is now augmented with withCachedResult, which is responsible for looking up and updating the cache for each Visit* functions. As this PR dramatically improves the false positive rate of the checker, it also deletes the code to ignore raw pointers and references within if and for statements. --- .../Checkers/WebKit/PtrTypesSemantics.cpp | 221 -- .../Checkers/WebKit/PtrTypesSemantics.h | 21 +- .../WebKit/UncountedLocalVarsChecker.cpp | 69 +++--- .../Analysis/Checkers/WebKit/mock-types.h | 2 + .../Checkers/WebKit/uncounted-local-vars.cpp | 92 +++- 5 files changed, 286 insertions(+), 119 deletions(-) diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp b/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp index defd83ec8e179c..904781e6ea72ae 100644 --- a/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp @@ -245,18 +245,41 @@ class TrivialFunctionAnalysisVisitor // Returns false if at least one child is non-trivial. bool VisitChildren(const Stmt *S) { -for (const Stmt *Child : S->children()) { - if (Child && !Visit(Child)) +return withCachedResult(S, [&]() { + for (const Stmt *Child : S->children()) { +if (Child && !Visit(Child)) + return false; + } + return true; +}); + } + + bool VisitSubExpr(const Expr *Parent, const Expr *E) { +return withCachedResult(Parent, [&]() { + if (!Visit(E)) return false; -} + return true; +}); + } -return true; + template + bool withCachedResult(const StmtType *S, CheckFunction Function) { +// Insert false to the cache first to avoid infinite recursion. +auto [It, IsNew] = StatementCache.insert(std::make_pair(S, false)); +if (!IsNew) + return It->second; +bool Result = Function(); +It->second = Result; +return Result; } public: - using CacheTy = TrivialFunctionAnalysis::CacheTy; + using FunctionCacheTy = TrivialFunctionAnalysis::FunctionCacheTy; + using StatementCacheTy = TrivialFunctionAnalysis::StatementCacheTy; - TrivialFunctionAnalysisVisitor(CacheTy &Cache) : Cache(Cache) {} + TrivialFunctionAnalysisVisitor(FunctionCacheTy &FunctionCache, + StatementCacheTy &StatementCache) + : FunctionCache(FunctionCache), StatementCache(StatementCache) {} bool VisitStmt(const Stmt *S) { // All statements are non-trivial unless overriden later. @@ -272,13 +295,21 @@ class TrivialFunctionAnalysisVisitor bool VisitReturnStmt(const ReturnStmt *RS) { // A return statement is allowed as long as the return value is trivial. -if (auto *RV = RS->getRetValue()) - return Visit(RV); -return true; +return withCachedResult(RS, [&]() { + if (auto *RV = RS->getRetValue()) +return Visit(RV); + return true; +}); + } + + bool VisitCXXForRangeStmt(const CXXForRangeStmt *FS) { +return VisitChildren(FS); } bool VisitDeclStmt(const DeclStmt *DS) { return VisitChildren(DS); } bool VisitDoStmt(const DoStmt *DS) { return VisitChildren(DS); } + bool VisitForStmt(const ForStmt *FS) { return VisitChildren(FS); } + bool VisitWhileStmt(const WhileStmt *WS) { return VisitChildren(WS); } bool VisitIfStmt(const IfStmt *IS) { return VisitChildren(IS); } bool VisitSwitchStmt(const SwitchStmt *SS) { return VisitChildren(SS); } bool VisitCaseStmt(const CaseStmt *CS) { return VisitChildren(CS); } @@ -286,17 +317,26 @@ class TrivialFunctionAnalysisVisitor bool VisitUnaryOperator(const UnaryOperator *UO) { // Operator '*' and '!' are allowed as long as the operand is trivial. -if (UO->getOpcode() == UO_Deref || UO->getOpcode() == UO_AddrOf || -UO->getOpcode() == UO_LNot) - return Visit(UO->getSubExpr()); - -// Other operators are non-trivial. -return false; +return withCachedResult(UO, [&]() { + auto op = UO->getOpcode(); + if (op == UO_Deref || op == UO_AddrOf || op == UO_LNot) +return Visit(UO->getSubExpr()); + if (UO->isIncrementOp() || UO->isDecrementOp()) { +if (auto *RefExpr = dyn_cast(UO->getSubExpr())) { + if (auto *Decl = dyn_cast(RefExpr->getDecl())) +return Decl->i
[clang] [alpha.webkit.UncountedLocalVarsChecker] Allow uncounted object references within trivial statements (PR #82229)
https://github.com/rniwa updated https://github.com/llvm/llvm-project/pull/82229 >From 234e301ab2721ddb2f4b43589785015a7d0aa304 Mon Sep 17 00:00:00 2001 From: Ryosuke Niwa Date: Mon, 19 Feb 2024 01:07:13 -0800 Subject: [PATCH 1/5] [alpha.webkit.UncountedLocalVarsChecker] Allow uncounted object references within trivial statements This PR makes alpha.webkit.UncountedLocalVarsChecker ignore raw references and pointers to a ref counted type which appears within "trival" statements. To do this, this PR extends TrivialFunctionAnalysis so that it can also analyze "triviality" of statements as well as that of functions Each Visit* function is now augmented with withCachedResult, which is responsible for looking up and updating the cache for each Visit* functions. As this PR dramatically improves the false positive rate of the checker, it also deletes the code to ignore raw pointers and references within if and for statements. --- .../Checkers/WebKit/PtrTypesSemantics.cpp | 222 -- .../Checkers/WebKit/PtrTypesSemantics.h | 21 +- .../WebKit/UncountedLocalVarsChecker.cpp | 69 +++--- .../Analysis/Checkers/WebKit/mock-types.h | 2 + .../Checkers/WebKit/uncounted-local-vars.cpp | 92 +++- 5 files changed, 285 insertions(+), 121 deletions(-) diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp b/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp index 01b191ab0eeaf4..6c9a8aedb38a4c 100644 --- a/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp @@ -245,18 +245,41 @@ class TrivialFunctionAnalysisVisitor // Returns false if at least one child is non-trivial. bool VisitChildren(const Stmt *S) { -for (const Stmt *Child : S->children()) { - if (Child && !Visit(Child)) +return withCachedResult(S, [&]() { + for (const Stmt *Child : S->children()) { +if (Child && !Visit(Child)) + return false; + } + return true; +}); + } + + bool VisitSubExpr(const Expr *Parent, const Expr *E) { +return withCachedResult(Parent, [&]() { + if (!Visit(E)) return false; -} + return true; +}); + } -return true; + template + bool withCachedResult(const StmtType *S, CheckFunction Function) { +// Insert false to the cache first to avoid infinite recursion. +auto [It, IsNew] = StatementCache.insert(std::make_pair(S, false)); +if (!IsNew) + return It->second; +bool Result = Function(); +It->second = Result; +return Result; } public: - using CacheTy = TrivialFunctionAnalysis::CacheTy; + using FunctionCacheTy = TrivialFunctionAnalysis::FunctionCacheTy; + using StatementCacheTy = TrivialFunctionAnalysis::StatementCacheTy; - TrivialFunctionAnalysisVisitor(CacheTy &Cache) : Cache(Cache) {} + TrivialFunctionAnalysisVisitor(FunctionCacheTy &FunctionCache, + StatementCacheTy &StatementCache) + : FunctionCache(FunctionCache), StatementCache(StatementCache) {} bool VisitStmt(const Stmt *S) { // All statements are non-trivial unless overriden later. @@ -272,13 +295,21 @@ class TrivialFunctionAnalysisVisitor bool VisitReturnStmt(const ReturnStmt *RS) { // A return statement is allowed as long as the return value is trivial. -if (auto *RV = RS->getRetValue()) - return Visit(RV); -return true; +return withCachedResult(RS, [&]() { + if (auto *RV = RS->getRetValue()) +return Visit(RV); + return true; +}); + } + + bool VisitCXXForRangeStmt(const CXXForRangeStmt *FS) { +return VisitChildren(FS); } bool VisitDeclStmt(const DeclStmt *DS) { return VisitChildren(DS); } bool VisitDoStmt(const DoStmt *DS) { return VisitChildren(DS); } + bool VisitForStmt(const ForStmt *FS) { return VisitChildren(FS); } + bool VisitWhileStmt(const WhileStmt *WS) { return VisitChildren(WS); } bool VisitIfStmt(const IfStmt *IS) { return VisitChildren(IS); } bool VisitSwitchStmt(const SwitchStmt *SS) { return VisitChildren(SS); } bool VisitCaseStmt(const CaseStmt *CS) { return VisitChildren(CS); } @@ -286,17 +317,26 @@ class TrivialFunctionAnalysisVisitor bool VisitUnaryOperator(const UnaryOperator *UO) { // Operator '*' and '!' are allowed as long as the operand is trivial. -if (UO->getOpcode() == UO_Deref || UO->getOpcode() == UO_AddrOf || -UO->getOpcode() == UO_LNot) - return Visit(UO->getSubExpr()); - -// Other operators are non-trivial. -return false; +return withCachedResult(UO, [&]() { + auto op = UO->getOpcode(); + if (op == UO_Deref || op == UO_AddrOf || op == UO_LNot) +return Visit(UO->getSubExpr()); + if (UO->isIncrementOp() || UO->isDecrementOp()) { +if (auto *RefExpr = dyn_cast(UO->getSubExpr())) { + if (auto *Decl = dyn_cast(RefExpr->getDecl())) +return Decl->i
[clang] [alpha.webkit.UncountedLocalVarsChecker] Allow uncounted object references within trivial statements (PR #82229)
https://github.com/rniwa updated https://github.com/llvm/llvm-project/pull/82229 >From 234e301ab2721ddb2f4b43589785015a7d0aa304 Mon Sep 17 00:00:00 2001 From: Ryosuke Niwa Date: Mon, 19 Feb 2024 01:07:13 -0800 Subject: [PATCH 1/6] [alpha.webkit.UncountedLocalVarsChecker] Allow uncounted object references within trivial statements This PR makes alpha.webkit.UncountedLocalVarsChecker ignore raw references and pointers to a ref counted type which appears within "trival" statements. To do this, this PR extends TrivialFunctionAnalysis so that it can also analyze "triviality" of statements as well as that of functions Each Visit* function is now augmented with withCachedResult, which is responsible for looking up and updating the cache for each Visit* functions. As this PR dramatically improves the false positive rate of the checker, it also deletes the code to ignore raw pointers and references within if and for statements. --- .../Checkers/WebKit/PtrTypesSemantics.cpp | 222 -- .../Checkers/WebKit/PtrTypesSemantics.h | 21 +- .../WebKit/UncountedLocalVarsChecker.cpp | 69 +++--- .../Analysis/Checkers/WebKit/mock-types.h | 2 + .../Checkers/WebKit/uncounted-local-vars.cpp | 92 +++- 5 files changed, 285 insertions(+), 121 deletions(-) diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp b/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp index 01b191ab0eeaf4..6c9a8aedb38a4c 100644 --- a/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp @@ -245,18 +245,41 @@ class TrivialFunctionAnalysisVisitor // Returns false if at least one child is non-trivial. bool VisitChildren(const Stmt *S) { -for (const Stmt *Child : S->children()) { - if (Child && !Visit(Child)) +return withCachedResult(S, [&]() { + for (const Stmt *Child : S->children()) { +if (Child && !Visit(Child)) + return false; + } + return true; +}); + } + + bool VisitSubExpr(const Expr *Parent, const Expr *E) { +return withCachedResult(Parent, [&]() { + if (!Visit(E)) return false; -} + return true; +}); + } -return true; + template + bool withCachedResult(const StmtType *S, CheckFunction Function) { +// Insert false to the cache first to avoid infinite recursion. +auto [It, IsNew] = StatementCache.insert(std::make_pair(S, false)); +if (!IsNew) + return It->second; +bool Result = Function(); +It->second = Result; +return Result; } public: - using CacheTy = TrivialFunctionAnalysis::CacheTy; + using FunctionCacheTy = TrivialFunctionAnalysis::FunctionCacheTy; + using StatementCacheTy = TrivialFunctionAnalysis::StatementCacheTy; - TrivialFunctionAnalysisVisitor(CacheTy &Cache) : Cache(Cache) {} + TrivialFunctionAnalysisVisitor(FunctionCacheTy &FunctionCache, + StatementCacheTy &StatementCache) + : FunctionCache(FunctionCache), StatementCache(StatementCache) {} bool VisitStmt(const Stmt *S) { // All statements are non-trivial unless overriden later. @@ -272,13 +295,21 @@ class TrivialFunctionAnalysisVisitor bool VisitReturnStmt(const ReturnStmt *RS) { // A return statement is allowed as long as the return value is trivial. -if (auto *RV = RS->getRetValue()) - return Visit(RV); -return true; +return withCachedResult(RS, [&]() { + if (auto *RV = RS->getRetValue()) +return Visit(RV); + return true; +}); + } + + bool VisitCXXForRangeStmt(const CXXForRangeStmt *FS) { +return VisitChildren(FS); } bool VisitDeclStmt(const DeclStmt *DS) { return VisitChildren(DS); } bool VisitDoStmt(const DoStmt *DS) { return VisitChildren(DS); } + bool VisitForStmt(const ForStmt *FS) { return VisitChildren(FS); } + bool VisitWhileStmt(const WhileStmt *WS) { return VisitChildren(WS); } bool VisitIfStmt(const IfStmt *IS) { return VisitChildren(IS); } bool VisitSwitchStmt(const SwitchStmt *SS) { return VisitChildren(SS); } bool VisitCaseStmt(const CaseStmt *CS) { return VisitChildren(CS); } @@ -286,17 +317,26 @@ class TrivialFunctionAnalysisVisitor bool VisitUnaryOperator(const UnaryOperator *UO) { // Operator '*' and '!' are allowed as long as the operand is trivial. -if (UO->getOpcode() == UO_Deref || UO->getOpcode() == UO_AddrOf || -UO->getOpcode() == UO_LNot) - return Visit(UO->getSubExpr()); - -// Other operators are non-trivial. -return false; +return withCachedResult(UO, [&]() { + auto op = UO->getOpcode(); + if (op == UO_Deref || op == UO_AddrOf || op == UO_LNot) +return Visit(UO->getSubExpr()); + if (UO->isIncrementOp() || UO->isDecrementOp()) { +if (auto *RefExpr = dyn_cast(UO->getSubExpr())) { + if (auto *Decl = dyn_cast(RefExpr->getDecl())) +return Decl->i
[clang] [alpha.webkit.UncountedLocalVarsChecker] Allow uncounted object references within trivial statements (PR #82229)
@@ -253,6 +253,17 @@ class TrivialFunctionAnalysisVisitor return true; } + template + bool WithCachedResult(const StmtType *S, CheckFunction Function) { rniwa wrote: Good point. Changed. https://github.com/llvm/llvm-project/pull/82229 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] Fix a crash in clang::isGetterOfRefCounted by checking nullptr in tryToFindPtrOrigin (PR #80768)
https://github.com/rniwa created https://github.com/llvm/llvm-project/pull/80768 None >From 4e10436ddd55f1b1bed2bb99856e1101b9462117 Mon Sep 17 00:00:00 2001 From: Ryosuke Niwa Date: Mon, 5 Feb 2024 16:07:09 -0800 Subject: [PATCH] Fix a crash in clang::isGetterOfRefCounted by checking nullptr in tryToFindPtrOrigin --- .../Checkers/WebKit/ASTUtils.cpp | 14 +- .../WebKit/member-function-pointer-crash.cpp | 26 +++ 2 files changed, 34 insertions(+), 6 deletions(-) create mode 100644 clang/test/Analysis/Checkers/WebKit/member-function-pointer-crash.cpp diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/ASTUtils.cpp b/clang/lib/StaticAnalyzer/Checkers/WebKit/ASTUtils.cpp index 64028b27702150..728772ed910afc 100644 --- a/clang/lib/StaticAnalyzer/Checkers/WebKit/ASTUtils.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/ASTUtils.cpp @@ -34,13 +34,15 @@ tryToFindPtrOrigin(const Expr *E, bool StopAtFirstRefCountedObj) { } if (auto *call = dyn_cast(E)) { if (auto *memberCall = dyn_cast(call)) { -std::optional IsGetterOfRefCt = isGetterOfRefCounted(memberCall->getMethodDecl()); -if (IsGetterOfRefCt && *IsGetterOfRefCt) { - E = memberCall->getImplicitObjectArgument(); - if (StopAtFirstRefCountedObj) { -return {E, true}; +if (auto *decl = memberCall->getMethodDecl()) { + std::optional IsGetterOfRefCt = isGetterOfRefCounted(memberCall->getMethodDecl()); + if (IsGetterOfRefCt && *IsGetterOfRefCt) { +E = memberCall->getImplicitObjectArgument(); +if (StopAtFirstRefCountedObj) { + return {E, true}; +} +continue; } - continue; } } diff --git a/clang/test/Analysis/Checkers/WebKit/member-function-pointer-crash.cpp b/clang/test/Analysis/Checkers/WebKit/member-function-pointer-crash.cpp new file mode 100644 index 00..16d3b89b3ac4e7 --- /dev/null +++ b/clang/test/Analysis/Checkers/WebKit/member-function-pointer-crash.cpp @@ -0,0 +1,26 @@ +// RUN: %clang_analyze_cc1 -analyzer-checker=alpha.webkit.UncountedLocalVarsChecker -verify %s + +#include "mock-types.h" + +class RenderStyle; + +class FillLayer { +public: +void ref() const; +void deref() const; +}; + +class FillLayersPropertyWrapper { +public: +typedef const FillLayer& (RenderStyle::*LayersGetter)() const; + +private: +bool canInterpolate(const RenderStyle& from) const +{ +auto* fromLayer = &(from.*m_layersGetter)(); +// expected-warning@-1{{Local variable 'fromLayer' is uncounted and unsafe}} +return true; +} + +LayersGetter m_layersGetter; +}; ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] Fix a crash in clang::isGetterOfRefCounted by checking nullptr in tryToFindPtrOrigin (PR #80768)
https://github.com/rniwa updated https://github.com/llvm/llvm-project/pull/80768 >From f9f11843c2d09775de20d47dc71c5e482a1ff8b4 Mon Sep 17 00:00:00 2001 From: Ryosuke Niwa Date: Mon, 5 Feb 2024 16:07:09 -0800 Subject: [PATCH] Fix a crash in clang::isGetterOfRefCounted by checking nullptr in tryToFindPtrOrigin --- .../Checkers/WebKit/ASTUtils.cpp | 15 ++- .../WebKit/member-function-pointer-crash.cpp | 26 +++ 2 files changed, 35 insertions(+), 6 deletions(-) create mode 100644 clang/test/Analysis/Checkers/WebKit/member-function-pointer-crash.cpp diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/ASTUtils.cpp b/clang/lib/StaticAnalyzer/Checkers/WebKit/ASTUtils.cpp index 64028b2770215..4526fac64735b 100644 --- a/clang/lib/StaticAnalyzer/Checkers/WebKit/ASTUtils.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/ASTUtils.cpp @@ -34,13 +34,16 @@ tryToFindPtrOrigin(const Expr *E, bool StopAtFirstRefCountedObj) { } if (auto *call = dyn_cast(E)) { if (auto *memberCall = dyn_cast(call)) { -std::optional IsGetterOfRefCt = isGetterOfRefCounted(memberCall->getMethodDecl()); -if (IsGetterOfRefCt && *IsGetterOfRefCt) { - E = memberCall->getImplicitObjectArgument(); - if (StopAtFirstRefCountedObj) { -return {E, true}; +if (auto *decl = memberCall->getMethodDecl()) { + std::optional IsGetterOfRefCt = + isGetterOfRefCounted(memberCall->getMethodDecl()); + if (IsGetterOfRefCt && *IsGetterOfRefCt) { +E = memberCall->getImplicitObjectArgument(); +if (StopAtFirstRefCountedObj) { + return {E, true}; +} +continue; } - continue; } } diff --git a/clang/test/Analysis/Checkers/WebKit/member-function-pointer-crash.cpp b/clang/test/Analysis/Checkers/WebKit/member-function-pointer-crash.cpp new file mode 100644 index 0..16d3b89b3ac4e --- /dev/null +++ b/clang/test/Analysis/Checkers/WebKit/member-function-pointer-crash.cpp @@ -0,0 +1,26 @@ +// RUN: %clang_analyze_cc1 -analyzer-checker=alpha.webkit.UncountedLocalVarsChecker -verify %s + +#include "mock-types.h" + +class RenderStyle; + +class FillLayer { +public: +void ref() const; +void deref() const; +}; + +class FillLayersPropertyWrapper { +public: +typedef const FillLayer& (RenderStyle::*LayersGetter)() const; + +private: +bool canInterpolate(const RenderStyle& from) const +{ +auto* fromLayer = &(from.*m_layersGetter)(); +// expected-warning@-1{{Local variable 'fromLayer' is uncounted and unsafe}} +return true; +} + +LayersGetter m_layersGetter; +}; ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] Ignore assignment to Ref / RefPtr in alpha.webkit.UncountedCallArgsChecker. (PR #80810)
https://github.com/rniwa created https://github.com/llvm/llvm-project/pull/80810 None >From 81057ef7e57f0e61d4b3edb19a7e6dc2b196b824 Mon Sep 17 00:00:00 2001 From: Ryosuke Niwa Date: Tue, 6 Feb 2024 01:13:34 -0800 Subject: [PATCH] Ignore assignment to Ref / RefPtr in alpha.webkit.UncountedCallArgsChecker. --- .../WebKit/UncountedCallArgsChecker.cpp | 9 +++ .../Checkers/WebKit/assignment-to-refptr.cpp | 71 +++ 2 files changed, 80 insertions(+) create mode 100644 clang/test/Analysis/Checkers/WebKit/assignment-to-refptr.cpp diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedCallArgsChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedCallArgsChecker.cpp index 407b6ba7a76428..b9ccf46f52649f 100644 --- a/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedCallArgsChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedCallArgsChecker.cpp @@ -126,6 +126,15 @@ class UncountedCallArgsChecker // of object on LHS. if (auto *MemberOp = dyn_cast(CE)) { // Note: assignemnt to built-in type isn't derived from CallExpr. + if (MemberOp->getOperator() == OO_Equal) { // Ignore assignment to Ref/RefPtr. +auto *callee = MemberOp->getDirectCallee(); +if (auto *calleeDecl = dyn_cast(callee)) { + if (const CXXRecordDecl *classDecl = calleeDecl->getParent()) { +if (isRefCounted(classDecl)) + return true; + } +} + } if (MemberOp->isAssignmentOp()) return false; } diff --git a/clang/test/Analysis/Checkers/WebKit/assignment-to-refptr.cpp b/clang/test/Analysis/Checkers/WebKit/assignment-to-refptr.cpp new file mode 100644 index 00..c8ad634a5493f5 --- /dev/null +++ b/clang/test/Analysis/Checkers/WebKit/assignment-to-refptr.cpp @@ -0,0 +1,71 @@ +// RUN: %clang_analyze_cc1 -analyzer-checker=alpha.webkit.UncountedCallArgsChecker -verify %s +// expected-no-diagnostics + +template +class RefPtr { +public: +inline constexpr RefPtr() : m_ptr(nullptr) { } +inline RefPtr(T* ptr) + : m_ptr(ptr) +{ +if (m_ptr) +m_ptr->ref(); +} + +inline RefPtr(const RefPtr& o) +: m_ptr(o.m_ptr) +{ +if (m_ptr) +m_ptr->ref(); +} + +inline ~RefPtr() +{ +if (m_ptr) +m_ptr->deref(); +} + +inline T* operator->() const { return m_ptr; } +explicit operator bool() const { return m_ptr; } + +RefPtr& operator=(const RefPtr&); +RefPtr& operator=(T*); + +private: +T* m_ptr; +}; + +template +inline RefPtr& RefPtr::operator=(const RefPtr& o) +{ +if (m_ptr) +m_ptr->deref(); +m_ptr = o.m_ptr; +if (m_ptr) +m_ptr->ref(); +return *this; +} + +template +inline RefPtr& RefPtr::operator=(T* optr) +{ +if (m_ptr) +m_ptr->deref(); +m_ptr = optr; +if (m_ptr) +m_ptr->ref(); +return *this; +} + +class Node { +public: +Node* nextSibling() const; + +void ref() const; +void deref() const; +}; + +static void removeDetachedChildren(Node* firstChild) +{ +for (RefPtr child = firstChild; child; child = child->nextSibling()); +} ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [analyzer] WebKit checkers: support ref and deref defined on different classes. (PR #68170)
@@ -70,29 +88,45 @@ std::optional isRefCountable(const CXXRecordDecl* R) if (!R) return std::nullopt; - if (hasPublicRefAndDeref(R)) + bool hasRef = hasPublicRefMethod(R); + bool hasDeref = hasPublicDerefMethod(R); + if (hasRef && hasDeref) return true; CXXBasePaths Paths; Paths.setOrigin(const_cast(R)); bool AnyInconclusiveBase = false; - const auto isRefCountableBase = - [&AnyInconclusiveBase](const CXXBaseSpecifier* Base, CXXBasePath&) { - std::optional IsRefCountable = clang::isRefCountable(Base); - if (!IsRefCountable) { - AnyInconclusiveBase = true; - return false; - } - return (*IsRefCountable) != nullptr; + const auto hasPublicRefInBase = + [&AnyInconclusiveBase](const CXXBaseSpecifier *Base, CXXBasePath &) { +auto hasRefInBase = clang::hasPublicRefInBase(Base); +if (!hasRefInBase) { + AnyInconclusiveBase = true; + return false; +} +return (*hasRefInBase) != nullptr; }; - bool BasesResult = R->lookupInBases(isRefCountableBase, Paths, - /*LookupInDependent =*/true); + hasRef = + R->lookupInBases(hasPublicRefInBase, Paths, /*LookupInDependent =*/true); + if (AnyInconclusiveBase) +return std::nullopt; + + const auto hasPublicDerefInBase = + [&AnyInconclusiveBase](const CXXBaseSpecifier *Base, CXXBasePath &) { +auto hasDerefInBase = clang::hasPublicDerefInBase(Base); +if (!hasDerefInBase) { + AnyInconclusiveBase = true; + return false; +} +return (*hasDerefInBase) != nullptr; + }; + hasDeref = R->lookupInBases(hasPublicDerefInBase, Paths, + /*LookupInDependent =*/true); rniwa wrote: Fixed. https://github.com/llvm/llvm-project/pull/68170 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [analyzer] WebKit checkers: support ref and deref defined on different classes. (PR #68170)
@@ -18,24 +18,26 @@ using namespace clang; namespace { -bool hasPublicRefAndDeref(const CXXRecordDecl *R) { +bool hasPublicRefMethod(const CXXRecordDecl *R) { assert(R); assert(R->hasDefinition()); - bool hasRef = false; - bool hasDeref = false; for (const CXXMethodDecl *MD : R->methods()) { const auto MethodName = safeGetName(MD); +if (MethodName == "ref" && MD->getAccess() == AS_public) + return true; + } + return false; +} -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; -} +bool hasPublicDerefMethod(const CXXRecordDecl *R) { rniwa wrote: Sure, done that. https://github.com/llvm/llvm-project/pull/68170 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [analyzer] WebKit checkers: support ref and deref defined on different classes. (PR #68170)
@@ -44,9 +46,25 @@ bool hasPublicRefAndDeref(const CXXRecordDecl *R) { namespace clang { -std::optional -isRefCountable(const CXXBaseSpecifier* Base) -{ +std::optional +hasPublicRefInBase(const CXXBaseSpecifier *Base) { + assert(Base); + + const Type *T = Base->getType().getTypePtrOrNull(); + if (!T) +return std::nullopt; rniwa wrote: Hm... this seems like a much more involved refactoring so perhaps we should do it in a separate pass? WDYT? https://github.com/llvm/llvm-project/pull/68170 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] Fix WebKit static analyzers to support ref and deref methods to be defined on different classes. (PR #69985)
https://github.com/rniwa created https://github.com/llvm/llvm-project/pull/69985 None >From d8236358e137967dbbd63606b7d43983709597e8 Mon Sep 17 00:00:00 2001 From: Ryosuke Niwa Date: Mon, 23 Oct 2023 16:38:10 -0700 Subject: [PATCH] Fix WebKit static analyzers to support ref and deref methods to be defined on different classes. --- .../Checkers/WebKit/PtrTypesSemantics.cpp | 61 ++- .../Checkers/WebKit/PtrTypesSemantics.h | 8 +-- .../WebKit/RefCntblBaseVirtualDtorChecker.cpp | 47 -- ...virtual-dtor-ref-deref-on-diff-classes.cpp | 22 +++ ...nted-members-ref-deref-on-diff-classes.cpp | 23 +++ 5 files changed, 125 insertions(+), 36 deletions(-) create mode 100644 clang/test/Analysis/Checkers/WebKit/ref-cntbl-base-virtual-dtor-ref-deref-on-diff-classes.cpp create mode 100644 clang/test/Analysis/Checkers/WebKit/uncounted-members-ref-deref-on-diff-classes.cpp diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp b/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp index c1f180f31338cb3..8c7eaa3b9a5369d 100644 --- a/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp @@ -18,24 +18,14 @@ using namespace clang; namespace { -bool hasPublicRefAndDeref(const CXXRecordDecl *R) { +bool hasPublicMethodInBaseClass(const CXXRecordDecl *R, const char* NameToMatch) { assert(R); assert(R->hasDefinition()); - 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; -} +if (MethodName == NameToMatch && MD->getAccess() == AS_public) + return true; } return false; } @@ -44,9 +34,8 @@ bool hasPublicRefAndDeref(const CXXRecordDecl *R) { namespace clang { -std::optional -isRefCountable(const CXXBaseSpecifier* Base) -{ +std::optional +hasPublicMethodInBase(const CXXBaseSpecifier *Base, const char *NameToMatch) { assert(Base); const Type *T = Base->getType().getTypePtrOrNull(); @@ -59,7 +48,7 @@ isRefCountable(const CXXBaseSpecifier* Base) if (!R->hasDefinition()) return std::nullopt; - return hasPublicRefAndDeref(R) ? R : nullptr; + return hasPublicMethodInBaseClass(R, NameToMatch) ? R : nullptr; } std::optional isRefCountable(const CXXRecordDecl* R) @@ -70,29 +59,45 @@ std::optional isRefCountable(const CXXRecordDecl* R) if (!R) return std::nullopt; - if (hasPublicRefAndDeref(R)) + bool hasRef = hasPublicMethodInBaseClass(R, "ref"); + bool hasDeref = hasPublicMethodInBaseClass(R, "deref"); + if (hasRef && hasDeref) return true; CXXBasePaths Paths; Paths.setOrigin(const_cast(R)); bool AnyInconclusiveBase = false; - const auto isRefCountableBase = - [&AnyInconclusiveBase](const CXXBaseSpecifier* Base, CXXBasePath&) { - std::optional IsRefCountable = clang::isRefCountable(Base); - if (!IsRefCountable) { - AnyInconclusiveBase = true; - return false; - } - return (*IsRefCountable) != nullptr; + const auto hasPublicRefInBase = + [&AnyInconclusiveBase](const CXXBaseSpecifier *Base, CXXBasePath &) { +auto hasRefInBase = clang::hasPublicMethodInBase(Base, "ref"); +if (!hasRefInBase) { + AnyInconclusiveBase = true; + return false; +} +return (*hasRefInBase) != nullptr; }; - bool BasesResult = R->lookupInBases(isRefCountableBase, Paths, + hasRef = hasRef || R->lookupInBases(hasPublicRefInBase, Paths, /*LookupInDependent =*/true); if (AnyInconclusiveBase) return std::nullopt; - return BasesResult; + const auto hasPublicDerefInBase = + [&AnyInconclusiveBase](const CXXBaseSpecifier *Base, CXXBasePath &) { +auto hasDerefInBase = clang::hasPublicMethodInBase(Base, "deref"); +if (!hasDerefInBase) { + AnyInconclusiveBase = true; + return false; +} +return (*hasDerefInBase) != nullptr; + }; + hasDeref = hasDeref || R->lookupInBases(hasPublicDerefInBase, Paths, + /*LookupInDependent =*/true); + if (AnyInconclusiveBase) +return std::nullopt; + + return hasRef && hasDeref; } bool isCtorOfRefCounted(const clang::FunctionDecl *F) { diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.h b/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.h index 91e3ccf2ec3047e..45b21cc0918443b 100644 --- a/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.h +++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.h @@
[clang] [analyzer] WebKit checkers: support ref and deref defined on different classes. (PR #68170)
rniwa wrote: I couldn't take over this PR so I made a new PR at https://github.com/llvm/llvm-project/pull/69985. https://github.com/llvm/llvm-project/pull/68170 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] Fix WebKit static analyzers to support ref and deref methods to be defined on different classes. (PR #69985)
https://github.com/rniwa updated https://github.com/llvm/llvm-project/pull/69985 >From 1832737f3e198386d1ebdeccd7d0ca55ef6094c0 Mon Sep 17 00:00:00 2001 From: Ryosuke Niwa Date: Mon, 23 Oct 2023 16:38:10 -0700 Subject: [PATCH] Fix WebKit static analyzers to support ref and deref methods to be defined on different classes. --- .../Checkers/WebKit/PtrTypesSemantics.cpp | 62 ++- .../Checkers/WebKit/PtrTypesSemantics.h | 8 +-- .../WebKit/RefCntblBaseVirtualDtorChecker.cpp | 47 -- ...virtual-dtor-ref-deref-on-diff-classes.cpp | 22 +++ ...nted-members-ref-deref-on-diff-classes.cpp | 23 +++ 5 files changed, 126 insertions(+), 36 deletions(-) create mode 100644 clang/test/Analysis/Checkers/WebKit/ref-cntbl-base-virtual-dtor-ref-deref-on-diff-classes.cpp create mode 100644 clang/test/Analysis/Checkers/WebKit/uncounted-members-ref-deref-on-diff-classes.cpp diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp b/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp index c1f180f31338cb3..d2b663410580008 100644 --- a/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp @@ -18,24 +18,15 @@ using namespace clang; namespace { -bool hasPublicRefAndDeref(const CXXRecordDecl *R) { +bool hasPublicMethodInBaseClass(const CXXRecordDecl *R, +const char *NameToMatch) { assert(R); assert(R->hasDefinition()); - 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; -} +if (MethodName == NameToMatch && MD->getAccess() == AS_public) + return true; } return false; } @@ -44,9 +35,8 @@ bool hasPublicRefAndDeref(const CXXRecordDecl *R) { namespace clang { -std::optional -isRefCountable(const CXXBaseSpecifier* Base) -{ +std::optional +hasPublicMethodInBase(const CXXBaseSpecifier *Base, const char *NameToMatch) { assert(Base); const Type *T = Base->getType().getTypePtrOrNull(); @@ -59,7 +49,7 @@ isRefCountable(const CXXBaseSpecifier* Base) if (!R->hasDefinition()) return std::nullopt; - return hasPublicRefAndDeref(R) ? R : nullptr; + return hasPublicMethodInBaseClass(R, NameToMatch) ? R : nullptr; } std::optional isRefCountable(const CXXRecordDecl* R) @@ -70,29 +60,45 @@ std::optional isRefCountable(const CXXRecordDecl* R) if (!R) return std::nullopt; - if (hasPublicRefAndDeref(R)) + bool hasRef = hasPublicMethodInBaseClass(R, "ref"); + bool hasDeref = hasPublicMethodInBaseClass(R, "deref"); + if (hasRef && hasDeref) return true; CXXBasePaths Paths; Paths.setOrigin(const_cast(R)); bool AnyInconclusiveBase = false; - const auto isRefCountableBase = - [&AnyInconclusiveBase](const CXXBaseSpecifier* Base, CXXBasePath&) { - std::optional IsRefCountable = clang::isRefCountable(Base); - if (!IsRefCountable) { - AnyInconclusiveBase = true; - return false; - } - return (*IsRefCountable) != nullptr; + const auto hasPublicRefInBase = + [&AnyInconclusiveBase](const CXXBaseSpecifier *Base, CXXBasePath &) { +auto hasRefInBase = clang::hasPublicMethodInBase(Base, "ref"); +if (!hasRefInBase) { + AnyInconclusiveBase = true; + return false; +} +return (*hasRefInBase) != nullptr; }; - bool BasesResult = R->lookupInBases(isRefCountableBase, Paths, + hasRef = hasRef || R->lookupInBases(hasPublicRefInBase, Paths, /*LookupInDependent =*/true); if (AnyInconclusiveBase) return std::nullopt; - return BasesResult; + const auto hasPublicDerefInBase = + [&AnyInconclusiveBase](const CXXBaseSpecifier *Base, CXXBasePath &) { +auto hasDerefInBase = clang::hasPublicMethodInBase(Base, "deref"); +if (!hasDerefInBase) { + AnyInconclusiveBase = true; + return false; +} +return (*hasDerefInBase) != nullptr; + }; + hasDeref = hasDeref || R->lookupInBases(hasPublicDerefInBase, Paths, + /*LookupInDependent =*/true); + if (AnyInconclusiveBase) +return std::nullopt; + + return hasRef && hasDeref; } bool isCtorOfRefCounted(const clang::FunctionDecl *F) { diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.h b/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.h index 91e3ccf2ec3047e..45b21cc0918443b 100644 --- a/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.h +++ b/clang/lib/StaticAnalyzer/Checkers/WebK
[clang] Add a new attribute value for suppressing WebKit's unsafe member variable warning (PR #70124)
https://github.com/rniwa created https://github.com/llvm/llvm-project/pull/70124 None >From 3b420fcb0a6d74dc034cfdf2d7b28e13e75f0e08 Mon Sep 17 00:00:00 2001 From: Ryosuke Niwa Date: Tue, 24 Oct 2023 13:51:50 -0700 Subject: [PATCH] Add a new attribute value for suppressing WebKit's unsafe member variable warning --- .../Checkers/WebKit/NoUncountedMembersChecker.cpp | 7 +++ clang/test/Analysis/Checkers/WebKit/uncounted-members.cpp | 1 + 2 files changed, 8 insertions(+) diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/NoUncountedMembersChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/WebKit/NoUncountedMembersChecker.cpp index 66d8588e2531589..6f03e4ac27c4613 100644 --- a/clang/lib/StaticAnalyzer/Checkers/WebKit/NoUncountedMembersChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/NoUncountedMembersChecker.cpp @@ -75,6 +75,13 @@ class NoUncountedMemberChecker if (!MemberType) continue; + if (std::any_of( +Member->specific_attr_begin(), +Member->specific_attr_end(), [](const AnnotateAttr *Ann) { + return Ann->getAnnotation() == "webkit_uncountedmember_exception"; +})) + continue; + if (auto *MemberCXXRD = MemberType->getPointeeCXXRecordDecl()) { // If we don't see the definition we just don't know. if (MemberCXXRD->hasDefinition()) { diff --git a/clang/test/Analysis/Checkers/WebKit/uncounted-members.cpp b/clang/test/Analysis/Checkers/WebKit/uncounted-members.cpp index a0ea61e0e2a13b1..b175f9491ab5b73 100644 --- a/clang/test/Analysis/Checkers/WebKit/uncounted-members.cpp +++ b/clang/test/Analysis/Checkers/WebKit/uncounted-members.cpp @@ -16,6 +16,7 @@ namespace members { RefCountable& c = silenceWarningAboutInit; // expected-warning@-1{{Member variable 'c' in 'members::Foo' is a reference to ref-countable type 'RefCountable'}} Ref d; +__attribute__((annotate("webkit_uncountedmember_exception"))) RefCountable* e; }; template ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [alpha.webkit.UncountedCallArgsChecker] Detect & ignore trivial function calls. (PR #81808)
https://github.com/rniwa created https://github.com/llvm/llvm-project/pull/81808 This PR introduces the concept of a "trivial function" which applies to a function that only calls other trivial functions and contain literals and expressions that don't result in heap mutations (specifically it does not call deref). This is implemented using ConstStmtVisitor and checking each statement and expression's trivialness. This PR also introduces the concept of a "ingleton function", which is a static member function or a free standing function which ends with the suffix "singleton". Such a function's return value is understood to be safe to call any function with. >From 26f7904095ddd54ab54a94b3ae84db61d2135833 Mon Sep 17 00:00:00 2001 From: Ryosuke Niwa Date: Wed, 14 Feb 2024 16:21:33 -0800 Subject: [PATCH] [alpha.webkit.UncountedCallArgsChecker] Detect & ignore trivial function calls. This PR introduces the concept of a "trivial function" which applies to a function that only calls other trivial functions and contain literals and expressions that don't result in heap mutations (specifically it does not call deref). This is implemented using ConstStmtVisitor and checking each statement and expression's trivialness. This PR also introduces the concept of a "ingleton function", which is a static member function or a free standing function which ends with the suffix "singleton". Such a function's return value is understood to be safe to call any function with. --- .../Checkers/WebKit/ASTUtils.cpp | 4 + .../Checkers/WebKit/PtrTypesSemantics.cpp | 246 ++ .../Checkers/WebKit/PtrTypesSemantics.h | 23 ++ .../WebKit/UncountedCallArgsChecker.cpp | 8 +- .../Analysis/Checkers/WebKit/call-args.cpp| 33 +-- .../Checkers/WebKit/uncounted-obj-arg.cpp | 231 6 files changed, 528 insertions(+), 17 deletions(-) diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/ASTUtils.cpp b/clang/lib/StaticAnalyzer/Checkers/WebKit/ASTUtils.cpp index b76c0551c77bb0..94eaa81af51772 100644 --- a/clang/lib/StaticAnalyzer/Checkers/WebKit/ASTUtils.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/ASTUtils.cpp @@ -66,9 +66,13 @@ tryToFindPtrOrigin(const Expr *E, bool StopAtFirstRefCountedObj) { E = call->getArg(0); continue; } + if (isReturnValueRefCounted(callee)) return {E, true}; +if (isSingleton(callee)) + return {E, true}; + if (isPtrConversion(callee)) { E = call->getArg(0); continue; diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp b/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp index 907244013d0871..b2169bf023c6e9 100644 --- a/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp @@ -12,6 +12,7 @@ #include "clang/AST/Decl.h" #include "clang/AST/DeclCXX.h" #include "clang/AST/ExprCXX.h" +#include "clang/AST/StmtVisitor.h" #include using namespace clang; @@ -222,4 +223,249 @@ bool isPtrConversion(const FunctionDecl *F) { return false; } +bool isSingleton(const FunctionDecl *F) { + assert(F); + // FIXME: check # of params == 1 + if (auto *MethodDecl = dyn_cast(F)) { +if (!MethodDecl->isStatic()) + return false; + } + const auto &Name = safeGetName(F); + std::string SingletonStr = "singleton"; + auto index = Name.find(SingletonStr); + return index != std::string::npos && index == Name.size() - SingletonStr.size(); +} + + +// We only care about statements so let's use the simple +// (non-recursive) visitor. +class TrivialFunctionAnalysisVisitor +: public ConstStmtVisitor { + + // Returns false if at least one child is non-trivial. + bool VisitChildren(const Stmt *S) { +for (const Stmt *Child : S->children()) { + if (Child && !Visit(Child)) +return false; +} + +return true; + } + +public: + using CacheTy = TrivialFunctionAnalysis::CacheTy; + + TrivialFunctionAnalysisVisitor(CacheTy &Cache): Cache(Cache) {} + + bool VisitStmt(const Stmt *S) { +// All statements are non-trivial unless overriden later. +// Don't even recurse into children by default. +return false; + } + + bool VisitDeclStmt(const DeclStmt *DS) { +return VisitChildren(DS); + } + + bool VisitCompoundStmt(const CompoundStmt *CS) { +// A compound statement is allowed as long each individual sub-statement +// is trivial. +return VisitChildren(CS); + } + + bool VisitReturnStmt(const ReturnStmt *RS) { +// A return statement is allowed as long as the return value is trivial. +return Visit(RS->getRetValue()); + } + + bool VisitDoStmt(const DoStmt *DS) { +return VisitChildren(DS); + } + + bool VisitIfStmt(const IfStmt *IS) { +return VisitChildren(IS); + } + + bool VisitSwitchStmt(const SwitchStmt *SS) { +return VisitChildren(SS); + } + + bool Vi
[clang] [alpha.webkit.UncountedCallArgsChecker] Detect & ignore trivial function calls. (PR #81808)
https://github.com/rniwa updated https://github.com/llvm/llvm-project/pull/81808 >From 5a5b26fc3ed2c1c263cd3495b8844e2daeb2e54b Mon Sep 17 00:00:00 2001 From: Ryosuke Niwa Date: Wed, 14 Feb 2024 16:21:33 -0800 Subject: [PATCH] [alpha.webkit.UncountedCallArgsChecker] Detect & ignore trivial function calls. This PR introduces the concept of a "trivial function" which applies to a function that only calls other trivial functions and contain literals and expressions that don't result in heap mutations (specifically it does not call deref). This is implemented using ConstStmtVisitor and checking each statement and expression's trivialness. This PR also introduces the concept of a "ingleton function", which is a static member function or a free standing function which ends with the suffix "singleton". Such a function's return value is understood to be safe to call any function with. --- .../Checkers/WebKit/ASTUtils.cpp | 4 + .../Checkers/WebKit/PtrTypesSemantics.cpp | 208 .../Checkers/WebKit/PtrTypesSemantics.h | 21 ++ .../WebKit/UncountedCallArgsChecker.cpp | 8 +- .../Analysis/Checkers/WebKit/call-args.cpp| 33 +-- .../Checkers/WebKit/uncounted-obj-arg.cpp | 231 ++ 6 files changed, 488 insertions(+), 17 deletions(-) diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/ASTUtils.cpp b/clang/lib/StaticAnalyzer/Checkers/WebKit/ASTUtils.cpp index b76c0551c77bb0..94eaa81af51772 100644 --- a/clang/lib/StaticAnalyzer/Checkers/WebKit/ASTUtils.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/ASTUtils.cpp @@ -66,9 +66,13 @@ tryToFindPtrOrigin(const Expr *E, bool StopAtFirstRefCountedObj) { E = call->getArg(0); continue; } + if (isReturnValueRefCounted(callee)) return {E, true}; +if (isSingleton(callee)) + return {E, true}; + if (isPtrConversion(callee)) { E = call->getArg(0); continue; diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp b/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp index 907244013d0871..ba02c7a786871d 100644 --- a/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp @@ -12,6 +12,7 @@ #include "clang/AST/Decl.h" #include "clang/AST/DeclCXX.h" #include "clang/AST/ExprCXX.h" +#include "clang/AST/StmtVisitor.h" #include using namespace clang; @@ -222,4 +223,211 @@ bool isPtrConversion(const FunctionDecl *F) { return false; } +bool isSingleton(const FunctionDecl *F) { + assert(F); + // FIXME: check # of params == 1 + if (auto *MethodDecl = dyn_cast(F)) { +if (!MethodDecl->isStatic()) + return false; + } + const auto &Name = safeGetName(F); + std::string SingletonStr = "singleton"; + auto index = Name.find(SingletonStr); + return index != std::string::npos && + index == Name.size() - SingletonStr.size(); +} + + +// We only care about statements so let's use the simple +// (non-recursive) visitor. +class TrivialFunctionAnalysisVisitor +: public ConstStmtVisitor { + + // Returns false if at least one child is non-trivial. + bool VisitChildren(const Stmt *S) { +for (const Stmt *Child : S->children()) { + if (Child && !Visit(Child)) +return false; +} + +return true; + } + +public: + using CacheTy = TrivialFunctionAnalysis::CacheTy; + + TrivialFunctionAnalysisVisitor(CacheTy &Cache) : Cache(Cache) {} + + bool VisitStmt(const Stmt *S) { +// All statements are non-trivial unless overriden later. +// Don't even recurse into children by default. +return false; + } + + bool VisitCompoundStmt(const CompoundStmt *CS) { +// A compound statement is allowed as long each individual sub-statement +// is trivial. +return VisitChildren(CS); + } + + bool VisitReturnStmt(const ReturnStmt *RS) { +// A return statement is allowed as long as the return value is trivial. +return Visit(RS->getRetValue()); + } + + bool VisitDeclStmt(const DeclStmt *DS) { return VisitChildren(DS); } + bool VisitDoStmt(const DoStmt *DS) { return VisitChildren(DS); } + bool VisitIfStmt(const IfStmt *IS) { return VisitChildren(IS); } + bool VisitSwitchStmt(const SwitchStmt *SS) { return VisitChildren(SS); } + bool VisitCaseStmt(const CaseStmt *CS) { return VisitChildren(CS); } + bool VisitDefaultStmt(const DefaultStmt *DS) { return VisitChildren(DS); } + + bool VisitUnaryOperator(const UnaryOperator *UO) { +// Operator '*' and '!' are allowed as long as the operand is trivial. +if (UO->getOpcode() == UO_Deref || UO->getOpcode() == UO_LNot) + return Visit(UO->getSubExpr()); + +// Other operators are non-trivial. +return false; + } + + bool VisitBinaryOperator(const BinaryOperator *BO) { +// Binary operators are trivial if their operands are trivial. +return Visit(BO->getLHS()) && Visit(BO->getRHS(
[clang] [alpha.webkit.UncountedCallArgsChecker] Detect & ignore trivial function calls. (PR #81808)
https://github.com/rniwa updated https://github.com/llvm/llvm-project/pull/81808 >From 857decc27550e2b15938a7846a03561f9ad73f48 Mon Sep 17 00:00:00 2001 From: Ryosuke Niwa Date: Wed, 14 Feb 2024 16:21:33 -0800 Subject: [PATCH] [alpha.webkit.UncountedCallArgsChecker] Detect & ignore trivial function calls. This PR introduces the concept of a "trivial function" which applies to a function that only calls other trivial functions and contain literals and expressions that don't result in heap mutations (specifically it does not call deref). This is implemented using ConstStmtVisitor and checking each statement and expression's trivialness. This PR also introduces the concept of a "ingleton function", which is a static member function or a free standing function which ends with the suffix "singleton". Such a function's return value is understood to be safe to call any function with. --- .../Checkers/WebKit/ASTUtils.cpp | 4 + .../Checkers/WebKit/PtrTypesSemantics.cpp | 207 .../Checkers/WebKit/PtrTypesSemantics.h | 21 ++ .../WebKit/UncountedCallArgsChecker.cpp | 8 +- .../Analysis/Checkers/WebKit/call-args.cpp| 33 +-- .../Checkers/WebKit/uncounted-obj-arg.cpp | 231 ++ 6 files changed, 487 insertions(+), 17 deletions(-) diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/ASTUtils.cpp b/clang/lib/StaticAnalyzer/Checkers/WebKit/ASTUtils.cpp index b76c0551c77bb0..94eaa81af51772 100644 --- a/clang/lib/StaticAnalyzer/Checkers/WebKit/ASTUtils.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/ASTUtils.cpp @@ -66,9 +66,13 @@ tryToFindPtrOrigin(const Expr *E, bool StopAtFirstRefCountedObj) { E = call->getArg(0); continue; } + if (isReturnValueRefCounted(callee)) return {E, true}; +if (isSingleton(callee)) + return {E, true}; + if (isPtrConversion(callee)) { E = call->getArg(0); continue; diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp b/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp index 907244013d0871..17f59c2d304ddc 100644 --- a/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp @@ -12,6 +12,7 @@ #include "clang/AST/Decl.h" #include "clang/AST/DeclCXX.h" #include "clang/AST/ExprCXX.h" +#include "clang/AST/StmtVisitor.h" #include using namespace clang; @@ -222,4 +223,210 @@ bool isPtrConversion(const FunctionDecl *F) { return false; } +bool isSingleton(const FunctionDecl *F) { + assert(F); + // FIXME: check # of params == 1 + if (auto *MethodDecl = dyn_cast(F)) { +if (!MethodDecl->isStatic()) + return false; + } + const auto &Name = safeGetName(F); + std::string SingletonStr = "singleton"; + auto index = Name.find(SingletonStr); + return index != std::string::npos && + index == Name.size() - SingletonStr.size(); +} + +// We only care about statements so let's use the simple +// (non-recursive) visitor. +class TrivialFunctionAnalysisVisitor +: public ConstStmtVisitor { + + // Returns false if at least one child is non-trivial. + bool VisitChildren(const Stmt *S) { +for (const Stmt *Child : S->children()) { + if (Child && !Visit(Child)) +return false; +} + +return true; + } + +public: + using CacheTy = TrivialFunctionAnalysis::CacheTy; + + TrivialFunctionAnalysisVisitor(CacheTy &Cache) : Cache(Cache) {} + + bool VisitStmt(const Stmt *S) { +// All statements are non-trivial unless overriden later. +// Don't even recurse into children by default. +return false; + } + + bool VisitCompoundStmt(const CompoundStmt *CS) { +// A compound statement is allowed as long each individual sub-statement +// is trivial. +return VisitChildren(CS); + } + + bool VisitReturnStmt(const ReturnStmt *RS) { +// A return statement is allowed as long as the return value is trivial. +return Visit(RS->getRetValue()); + } + + bool VisitDeclStmt(const DeclStmt *DS) { return VisitChildren(DS); } + bool VisitDoStmt(const DoStmt *DS) { return VisitChildren(DS); } + bool VisitIfStmt(const IfStmt *IS) { return VisitChildren(IS); } + bool VisitSwitchStmt(const SwitchStmt *SS) { return VisitChildren(SS); } + bool VisitCaseStmt(const CaseStmt *CS) { return VisitChildren(CS); } + bool VisitDefaultStmt(const DefaultStmt *DS) { return VisitChildren(DS); } + + bool VisitUnaryOperator(const UnaryOperator *UO) { +// Operator '*' and '!' are allowed as long as the operand is trivial. +if (UO->getOpcode() == UO_Deref || UO->getOpcode() == UO_LNot) + return Visit(UO->getSubExpr()); + +// Other operators are non-trivial. +return false; + } + + bool VisitBinaryOperator(const BinaryOperator *BO) { +// Binary operators are trivial if their operands are trivial. +return Visit(BO->getLHS()) && Visit(BO->getRHS())
[clang] [alpha.webkit.UncountedCallArgsChecker] Detect & ignore trivial function calls. (PR #81808)
https://github.com/rniwa updated https://github.com/llvm/llvm-project/pull/81808 >From 857decc27550e2b15938a7846a03561f9ad73f48 Mon Sep 17 00:00:00 2001 From: Ryosuke Niwa Date: Wed, 14 Feb 2024 16:21:33 -0800 Subject: [PATCH 1/2] [alpha.webkit.UncountedCallArgsChecker] Detect & ignore trivial function calls. This PR introduces the concept of a "trivial function" which applies to a function that only calls other trivial functions and contain literals and expressions that don't result in heap mutations (specifically it does not call deref). This is implemented using ConstStmtVisitor and checking each statement and expression's trivialness. This PR also introduces the concept of a "ingleton function", which is a static member function or a free standing function which ends with the suffix "singleton". Such a function's return value is understood to be safe to call any function with. --- .../Checkers/WebKit/ASTUtils.cpp | 4 + .../Checkers/WebKit/PtrTypesSemantics.cpp | 207 .../Checkers/WebKit/PtrTypesSemantics.h | 21 ++ .../WebKit/UncountedCallArgsChecker.cpp | 8 +- .../Analysis/Checkers/WebKit/call-args.cpp| 33 +-- .../Checkers/WebKit/uncounted-obj-arg.cpp | 231 ++ 6 files changed, 487 insertions(+), 17 deletions(-) diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/ASTUtils.cpp b/clang/lib/StaticAnalyzer/Checkers/WebKit/ASTUtils.cpp index b76c0551c77bb0..94eaa81af51772 100644 --- a/clang/lib/StaticAnalyzer/Checkers/WebKit/ASTUtils.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/ASTUtils.cpp @@ -66,9 +66,13 @@ tryToFindPtrOrigin(const Expr *E, bool StopAtFirstRefCountedObj) { E = call->getArg(0); continue; } + if (isReturnValueRefCounted(callee)) return {E, true}; +if (isSingleton(callee)) + return {E, true}; + if (isPtrConversion(callee)) { E = call->getArg(0); continue; diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp b/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp index 907244013d0871..17f59c2d304ddc 100644 --- a/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp @@ -12,6 +12,7 @@ #include "clang/AST/Decl.h" #include "clang/AST/DeclCXX.h" #include "clang/AST/ExprCXX.h" +#include "clang/AST/StmtVisitor.h" #include using namespace clang; @@ -222,4 +223,210 @@ bool isPtrConversion(const FunctionDecl *F) { return false; } +bool isSingleton(const FunctionDecl *F) { + assert(F); + // FIXME: check # of params == 1 + if (auto *MethodDecl = dyn_cast(F)) { +if (!MethodDecl->isStatic()) + return false; + } + const auto &Name = safeGetName(F); + std::string SingletonStr = "singleton"; + auto index = Name.find(SingletonStr); + return index != std::string::npos && + index == Name.size() - SingletonStr.size(); +} + +// We only care about statements so let's use the simple +// (non-recursive) visitor. +class TrivialFunctionAnalysisVisitor +: public ConstStmtVisitor { + + // Returns false if at least one child is non-trivial. + bool VisitChildren(const Stmt *S) { +for (const Stmt *Child : S->children()) { + if (Child && !Visit(Child)) +return false; +} + +return true; + } + +public: + using CacheTy = TrivialFunctionAnalysis::CacheTy; + + TrivialFunctionAnalysisVisitor(CacheTy &Cache) : Cache(Cache) {} + + bool VisitStmt(const Stmt *S) { +// All statements are non-trivial unless overriden later. +// Don't even recurse into children by default. +return false; + } + + bool VisitCompoundStmt(const CompoundStmt *CS) { +// A compound statement is allowed as long each individual sub-statement +// is trivial. +return VisitChildren(CS); + } + + bool VisitReturnStmt(const ReturnStmt *RS) { +// A return statement is allowed as long as the return value is trivial. +return Visit(RS->getRetValue()); + } + + bool VisitDeclStmt(const DeclStmt *DS) { return VisitChildren(DS); } + bool VisitDoStmt(const DoStmt *DS) { return VisitChildren(DS); } + bool VisitIfStmt(const IfStmt *IS) { return VisitChildren(IS); } + bool VisitSwitchStmt(const SwitchStmt *SS) { return VisitChildren(SS); } + bool VisitCaseStmt(const CaseStmt *CS) { return VisitChildren(CS); } + bool VisitDefaultStmt(const DefaultStmt *DS) { return VisitChildren(DS); } + + bool VisitUnaryOperator(const UnaryOperator *UO) { +// Operator '*' and '!' are allowed as long as the operand is trivial. +if (UO->getOpcode() == UO_Deref || UO->getOpcode() == UO_LNot) + return Visit(UO->getSubExpr()); + +// Other operators are non-trivial. +return false; + } + + bool VisitBinaryOperator(const BinaryOperator *BO) { +// Binary operators are trivial if their operands are trivial. +return Visit(BO->getLHS()) && Visit(BO->getRH
[clang] [alpha.webkit.UncountedCallArgsChecker] Detect & ignore trivial function calls. (PR #81808)
@@ -222,4 +223,210 @@ bool isPtrConversion(const FunctionDecl *F) { return false; } +bool isSingleton(const FunctionDecl *F) { + assert(F); + // FIXME: check # of params == 1 + if (auto *MethodDecl = dyn_cast(F)) { +if (!MethodDecl->isStatic()) + return false; + } + const auto &Name = safeGetName(F); + std::string SingletonStr = "singleton"; + auto index = Name.find(SingletonStr); + return index != std::string::npos && + index == Name.size() - SingletonStr.size(); +} + +// We only care about statements so let's use the simple +// (non-recursive) visitor. +class TrivialFunctionAnalysisVisitor +: public ConstStmtVisitor { + + // Returns false if at least one child is non-trivial. + bool VisitChildren(const Stmt *S) { +for (const Stmt *Child : S->children()) { + if (Child && !Visit(Child)) +return false; +} + +return true; + } + +public: + using CacheTy = TrivialFunctionAnalysis::CacheTy; + + TrivialFunctionAnalysisVisitor(CacheTy &Cache) : Cache(Cache) {} + + bool VisitStmt(const Stmt *S) { +// All statements are non-trivial unless overriden later. +// Don't even recurse into children by default. +return false; + } + + bool VisitCompoundStmt(const CompoundStmt *CS) { +// A compound statement is allowed as long each individual sub-statement +// is trivial. +return VisitChildren(CS); + } + + bool VisitReturnStmt(const ReturnStmt *RS) { +// A return statement is allowed as long as the return value is trivial. +return Visit(RS->getRetValue()); rniwa wrote: Fixed! This as indeed the cause of the crash. https://github.com/llvm/llvm-project/pull/81808 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [alpha.webkit.UncountedCallArgsChecker] Detect & ignore trivial function calls. (PR #81808)
@@ -222,4 +223,210 @@ bool isPtrConversion(const FunctionDecl *F) { return false; } +bool isSingleton(const FunctionDecl *F) { + assert(F); + // FIXME: check # of params == 1 + if (auto *MethodDecl = dyn_cast(F)) { +if (!MethodDecl->isStatic()) + return false; + } + const auto &Name = safeGetName(F); + std::string SingletonStr = "singleton"; + auto index = Name.find(SingletonStr); + return index != std::string::npos && + index == Name.size() - SingletonStr.size(); +} + +// We only care about statements so let's use the simple +// (non-recursive) visitor. +class TrivialFunctionAnalysisVisitor +: public ConstStmtVisitor { + + // Returns false if at least one child is non-trivial. + bool VisitChildren(const Stmt *S) { +for (const Stmt *Child : S->children()) { + if (Child && !Visit(Child)) +return false; +} + +return true; + } + +public: + using CacheTy = TrivialFunctionAnalysis::CacheTy; + + TrivialFunctionAnalysisVisitor(CacheTy &Cache) : Cache(Cache) {} + + bool VisitStmt(const Stmt *S) { +// All statements are non-trivial unless overriden later. +// Don't even recurse into children by default. +return false; + } + + bool VisitCompoundStmt(const CompoundStmt *CS) { +// A compound statement is allowed as long each individual sub-statement +// is trivial. +return VisitChildren(CS); + } + + bool VisitReturnStmt(const ReturnStmt *RS) { +// A return statement is allowed as long as the return value is trivial. +return Visit(RS->getRetValue()); + } + + bool VisitDeclStmt(const DeclStmt *DS) { return VisitChildren(DS); } + bool VisitDoStmt(const DoStmt *DS) { return VisitChildren(DS); } + bool VisitIfStmt(const IfStmt *IS) { return VisitChildren(IS); } + bool VisitSwitchStmt(const SwitchStmt *SS) { return VisitChildren(SS); } + bool VisitCaseStmt(const CaseStmt *CS) { return VisitChildren(CS); } + bool VisitDefaultStmt(const DefaultStmt *DS) { return VisitChildren(DS); } + + bool VisitUnaryOperator(const UnaryOperator *UO) { +// Operator '*' and '!' are allowed as long as the operand is trivial. +if (UO->getOpcode() == UO_Deref || UO->getOpcode() == UO_LNot) + return Visit(UO->getSubExpr()); + +// Other operators are non-trivial. +return false; + } + + bool VisitBinaryOperator(const BinaryOperator *BO) { +// Binary operators are trivial if their operands are trivial. +return Visit(BO->getLHS()) && Visit(BO->getRHS()); + } + + bool VisitConditionalOperator(const ConditionalOperator *CO) { +// Ternary operators are trivial if their conditions & values are trivial. +return VisitChildren(CO); + } + + bool VisitDeclRefExpr(const DeclRefExpr *DRE) { +if (auto *decl = DRE->getDecl()) { + if (isa(decl)) +return true; +} +return false; + } + + bool VisitStaticAssertDecl(const StaticAssertDecl *SAD) { +// Any static_assert is considered trivial. +return true; + } + + bool VisitCallExpr(const CallExpr *CE) { +if (auto *MCE = dyn_cast(CE)) + return VisitCXXMemberCallExpr(MCE); rniwa wrote: Good point. Refactored. https://github.com/llvm/llvm-project/pull/81808 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [alpha.webkit.UncountedCallArgsChecker] Detect & ignore trivial function calls. (PR #81808)
https://github.com/rniwa updated https://github.com/llvm/llvm-project/pull/81808 >From 857decc27550e2b15938a7846a03561f9ad73f48 Mon Sep 17 00:00:00 2001 From: Ryosuke Niwa Date: Wed, 14 Feb 2024 16:21:33 -0800 Subject: [PATCH 1/3] [alpha.webkit.UncountedCallArgsChecker] Detect & ignore trivial function calls. This PR introduces the concept of a "trivial function" which applies to a function that only calls other trivial functions and contain literals and expressions that don't result in heap mutations (specifically it does not call deref). This is implemented using ConstStmtVisitor and checking each statement and expression's trivialness. This PR also introduces the concept of a "ingleton function", which is a static member function or a free standing function which ends with the suffix "singleton". Such a function's return value is understood to be safe to call any function with. --- .../Checkers/WebKit/ASTUtils.cpp | 4 + .../Checkers/WebKit/PtrTypesSemantics.cpp | 207 .../Checkers/WebKit/PtrTypesSemantics.h | 21 ++ .../WebKit/UncountedCallArgsChecker.cpp | 8 +- .../Analysis/Checkers/WebKit/call-args.cpp| 33 +-- .../Checkers/WebKit/uncounted-obj-arg.cpp | 231 ++ 6 files changed, 487 insertions(+), 17 deletions(-) diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/ASTUtils.cpp b/clang/lib/StaticAnalyzer/Checkers/WebKit/ASTUtils.cpp index b76c0551c77bb0..94eaa81af51772 100644 --- a/clang/lib/StaticAnalyzer/Checkers/WebKit/ASTUtils.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/ASTUtils.cpp @@ -66,9 +66,13 @@ tryToFindPtrOrigin(const Expr *E, bool StopAtFirstRefCountedObj) { E = call->getArg(0); continue; } + if (isReturnValueRefCounted(callee)) return {E, true}; +if (isSingleton(callee)) + return {E, true}; + if (isPtrConversion(callee)) { E = call->getArg(0); continue; diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp b/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp index 907244013d0871..17f59c2d304ddc 100644 --- a/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp @@ -12,6 +12,7 @@ #include "clang/AST/Decl.h" #include "clang/AST/DeclCXX.h" #include "clang/AST/ExprCXX.h" +#include "clang/AST/StmtVisitor.h" #include using namespace clang; @@ -222,4 +223,210 @@ bool isPtrConversion(const FunctionDecl *F) { return false; } +bool isSingleton(const FunctionDecl *F) { + assert(F); + // FIXME: check # of params == 1 + if (auto *MethodDecl = dyn_cast(F)) { +if (!MethodDecl->isStatic()) + return false; + } + const auto &Name = safeGetName(F); + std::string SingletonStr = "singleton"; + auto index = Name.find(SingletonStr); + return index != std::string::npos && + index == Name.size() - SingletonStr.size(); +} + +// We only care about statements so let's use the simple +// (non-recursive) visitor. +class TrivialFunctionAnalysisVisitor +: public ConstStmtVisitor { + + // Returns false if at least one child is non-trivial. + bool VisitChildren(const Stmt *S) { +for (const Stmt *Child : S->children()) { + if (Child && !Visit(Child)) +return false; +} + +return true; + } + +public: + using CacheTy = TrivialFunctionAnalysis::CacheTy; + + TrivialFunctionAnalysisVisitor(CacheTy &Cache) : Cache(Cache) {} + + bool VisitStmt(const Stmt *S) { +// All statements are non-trivial unless overriden later. +// Don't even recurse into children by default. +return false; + } + + bool VisitCompoundStmt(const CompoundStmt *CS) { +// A compound statement is allowed as long each individual sub-statement +// is trivial. +return VisitChildren(CS); + } + + bool VisitReturnStmt(const ReturnStmt *RS) { +// A return statement is allowed as long as the return value is trivial. +return Visit(RS->getRetValue()); + } + + bool VisitDeclStmt(const DeclStmt *DS) { return VisitChildren(DS); } + bool VisitDoStmt(const DoStmt *DS) { return VisitChildren(DS); } + bool VisitIfStmt(const IfStmt *IS) { return VisitChildren(IS); } + bool VisitSwitchStmt(const SwitchStmt *SS) { return VisitChildren(SS); } + bool VisitCaseStmt(const CaseStmt *CS) { return VisitChildren(CS); } + bool VisitDefaultStmt(const DefaultStmt *DS) { return VisitChildren(DS); } + + bool VisitUnaryOperator(const UnaryOperator *UO) { +// Operator '*' and '!' are allowed as long as the operand is trivial. +if (UO->getOpcode() == UO_Deref || UO->getOpcode() == UO_LNot) + return Visit(UO->getSubExpr()); + +// Other operators are non-trivial. +return false; + } + + bool VisitBinaryOperator(const BinaryOperator *BO) { +// Binary operators are trivial if their operands are trivial. +return Visit(BO->getLHS()) && Visit(BO->getRH
[clang] [alpha.webkit.UncountedCallArgsChecker] Detect & ignore trivial function calls. (PR #81808)
https://github.com/rniwa updated https://github.com/llvm/llvm-project/pull/81808 >From 857decc27550e2b15938a7846a03561f9ad73f48 Mon Sep 17 00:00:00 2001 From: Ryosuke Niwa Date: Wed, 14 Feb 2024 16:21:33 -0800 Subject: [PATCH 1/4] [alpha.webkit.UncountedCallArgsChecker] Detect & ignore trivial function calls. This PR introduces the concept of a "trivial function" which applies to a function that only calls other trivial functions and contain literals and expressions that don't result in heap mutations (specifically it does not call deref). This is implemented using ConstStmtVisitor and checking each statement and expression's trivialness. This PR also introduces the concept of a "ingleton function", which is a static member function or a free standing function which ends with the suffix "singleton". Such a function's return value is understood to be safe to call any function with. --- .../Checkers/WebKit/ASTUtils.cpp | 4 + .../Checkers/WebKit/PtrTypesSemantics.cpp | 207 .../Checkers/WebKit/PtrTypesSemantics.h | 21 ++ .../WebKit/UncountedCallArgsChecker.cpp | 8 +- .../Analysis/Checkers/WebKit/call-args.cpp| 33 +-- .../Checkers/WebKit/uncounted-obj-arg.cpp | 231 ++ 6 files changed, 487 insertions(+), 17 deletions(-) diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/ASTUtils.cpp b/clang/lib/StaticAnalyzer/Checkers/WebKit/ASTUtils.cpp index b76c0551c77bb0..94eaa81af51772 100644 --- a/clang/lib/StaticAnalyzer/Checkers/WebKit/ASTUtils.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/ASTUtils.cpp @@ -66,9 +66,13 @@ tryToFindPtrOrigin(const Expr *E, bool StopAtFirstRefCountedObj) { E = call->getArg(0); continue; } + if (isReturnValueRefCounted(callee)) return {E, true}; +if (isSingleton(callee)) + return {E, true}; + if (isPtrConversion(callee)) { E = call->getArg(0); continue; diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp b/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp index 907244013d0871..17f59c2d304ddc 100644 --- a/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp @@ -12,6 +12,7 @@ #include "clang/AST/Decl.h" #include "clang/AST/DeclCXX.h" #include "clang/AST/ExprCXX.h" +#include "clang/AST/StmtVisitor.h" #include using namespace clang; @@ -222,4 +223,210 @@ bool isPtrConversion(const FunctionDecl *F) { return false; } +bool isSingleton(const FunctionDecl *F) { + assert(F); + // FIXME: check # of params == 1 + if (auto *MethodDecl = dyn_cast(F)) { +if (!MethodDecl->isStatic()) + return false; + } + const auto &Name = safeGetName(F); + std::string SingletonStr = "singleton"; + auto index = Name.find(SingletonStr); + return index != std::string::npos && + index == Name.size() - SingletonStr.size(); +} + +// We only care about statements so let's use the simple +// (non-recursive) visitor. +class TrivialFunctionAnalysisVisitor +: public ConstStmtVisitor { + + // Returns false if at least one child is non-trivial. + bool VisitChildren(const Stmt *S) { +for (const Stmt *Child : S->children()) { + if (Child && !Visit(Child)) +return false; +} + +return true; + } + +public: + using CacheTy = TrivialFunctionAnalysis::CacheTy; + + TrivialFunctionAnalysisVisitor(CacheTy &Cache) : Cache(Cache) {} + + bool VisitStmt(const Stmt *S) { +// All statements are non-trivial unless overriden later. +// Don't even recurse into children by default. +return false; + } + + bool VisitCompoundStmt(const CompoundStmt *CS) { +// A compound statement is allowed as long each individual sub-statement +// is trivial. +return VisitChildren(CS); + } + + bool VisitReturnStmt(const ReturnStmt *RS) { +// A return statement is allowed as long as the return value is trivial. +return Visit(RS->getRetValue()); + } + + bool VisitDeclStmt(const DeclStmt *DS) { return VisitChildren(DS); } + bool VisitDoStmt(const DoStmt *DS) { return VisitChildren(DS); } + bool VisitIfStmt(const IfStmt *IS) { return VisitChildren(IS); } + bool VisitSwitchStmt(const SwitchStmt *SS) { return VisitChildren(SS); } + bool VisitCaseStmt(const CaseStmt *CS) { return VisitChildren(CS); } + bool VisitDefaultStmt(const DefaultStmt *DS) { return VisitChildren(DS); } + + bool VisitUnaryOperator(const UnaryOperator *UO) { +// Operator '*' and '!' are allowed as long as the operand is trivial. +if (UO->getOpcode() == UO_Deref || UO->getOpcode() == UO_LNot) + return Visit(UO->getSubExpr()); + +// Other operators are non-trivial. +return false; + } + + bool VisitBinaryOperator(const BinaryOperator *BO) { +// Binary operators are trivial if their operands are trivial. +return Visit(BO->getLHS()) && Visit(BO->getRH
[clang] [alpha.webkit.UncountedCallArgsChecker] Detect & ignore trivial function calls. (PR #81808)
https://github.com/rniwa updated https://github.com/llvm/llvm-project/pull/81808 >From 857decc27550e2b15938a7846a03561f9ad73f48 Mon Sep 17 00:00:00 2001 From: Ryosuke Niwa Date: Wed, 14 Feb 2024 16:21:33 -0800 Subject: [PATCH 1/5] [alpha.webkit.UncountedCallArgsChecker] Detect & ignore trivial function calls. This PR introduces the concept of a "trivial function" which applies to a function that only calls other trivial functions and contain literals and expressions that don't result in heap mutations (specifically it does not call deref). This is implemented using ConstStmtVisitor and checking each statement and expression's trivialness. This PR also introduces the concept of a "ingleton function", which is a static member function or a free standing function which ends with the suffix "singleton". Such a function's return value is understood to be safe to call any function with. --- .../Checkers/WebKit/ASTUtils.cpp | 4 + .../Checkers/WebKit/PtrTypesSemantics.cpp | 207 .../Checkers/WebKit/PtrTypesSemantics.h | 21 ++ .../WebKit/UncountedCallArgsChecker.cpp | 8 +- .../Analysis/Checkers/WebKit/call-args.cpp| 33 +-- .../Checkers/WebKit/uncounted-obj-arg.cpp | 231 ++ 6 files changed, 487 insertions(+), 17 deletions(-) diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/ASTUtils.cpp b/clang/lib/StaticAnalyzer/Checkers/WebKit/ASTUtils.cpp index b76c0551c77bb0..94eaa81af51772 100644 --- a/clang/lib/StaticAnalyzer/Checkers/WebKit/ASTUtils.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/ASTUtils.cpp @@ -66,9 +66,13 @@ tryToFindPtrOrigin(const Expr *E, bool StopAtFirstRefCountedObj) { E = call->getArg(0); continue; } + if (isReturnValueRefCounted(callee)) return {E, true}; +if (isSingleton(callee)) + return {E, true}; + if (isPtrConversion(callee)) { E = call->getArg(0); continue; diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp b/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp index 907244013d0871..17f59c2d304ddc 100644 --- a/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp @@ -12,6 +12,7 @@ #include "clang/AST/Decl.h" #include "clang/AST/DeclCXX.h" #include "clang/AST/ExprCXX.h" +#include "clang/AST/StmtVisitor.h" #include using namespace clang; @@ -222,4 +223,210 @@ bool isPtrConversion(const FunctionDecl *F) { return false; } +bool isSingleton(const FunctionDecl *F) { + assert(F); + // FIXME: check # of params == 1 + if (auto *MethodDecl = dyn_cast(F)) { +if (!MethodDecl->isStatic()) + return false; + } + const auto &Name = safeGetName(F); + std::string SingletonStr = "singleton"; + auto index = Name.find(SingletonStr); + return index != std::string::npos && + index == Name.size() - SingletonStr.size(); +} + +// We only care about statements so let's use the simple +// (non-recursive) visitor. +class TrivialFunctionAnalysisVisitor +: public ConstStmtVisitor { + + // Returns false if at least one child is non-trivial. + bool VisitChildren(const Stmt *S) { +for (const Stmt *Child : S->children()) { + if (Child && !Visit(Child)) +return false; +} + +return true; + } + +public: + using CacheTy = TrivialFunctionAnalysis::CacheTy; + + TrivialFunctionAnalysisVisitor(CacheTy &Cache) : Cache(Cache) {} + + bool VisitStmt(const Stmt *S) { +// All statements are non-trivial unless overriden later. +// Don't even recurse into children by default. +return false; + } + + bool VisitCompoundStmt(const CompoundStmt *CS) { +// A compound statement is allowed as long each individual sub-statement +// is trivial. +return VisitChildren(CS); + } + + bool VisitReturnStmt(const ReturnStmt *RS) { +// A return statement is allowed as long as the return value is trivial. +return Visit(RS->getRetValue()); + } + + bool VisitDeclStmt(const DeclStmt *DS) { return VisitChildren(DS); } + bool VisitDoStmt(const DoStmt *DS) { return VisitChildren(DS); } + bool VisitIfStmt(const IfStmt *IS) { return VisitChildren(IS); } + bool VisitSwitchStmt(const SwitchStmt *SS) { return VisitChildren(SS); } + bool VisitCaseStmt(const CaseStmt *CS) { return VisitChildren(CS); } + bool VisitDefaultStmt(const DefaultStmt *DS) { return VisitChildren(DS); } + + bool VisitUnaryOperator(const UnaryOperator *UO) { +// Operator '*' and '!' are allowed as long as the operand is trivial. +if (UO->getOpcode() == UO_Deref || UO->getOpcode() == UO_LNot) + return Visit(UO->getSubExpr()); + +// Other operators are non-trivial. +return false; + } + + bool VisitBinaryOperator(const BinaryOperator *BO) { +// Binary operators are trivial if their operands are trivial. +return Visit(BO->getLHS()) && Visit(BO->getRH
[clang] [alpha.webkit.UncountedCallArgsChecker] Detect more trivial functions (PR #81829)
https://github.com/rniwa created https://github.com/llvm/llvm-project/pull/81829 Allow address-of operator (&), enum constant, and a reference to constant as well as materializing temporqary expression and an expression with cleanups to appear within a trivial function. >From 793c72168db3a27ad189e9c95d7701d19cefec1e Mon Sep 17 00:00:00 2001 From: Ryosuke Niwa Date: Wed, 14 Feb 2024 23:30:27 -0800 Subject: [PATCH] [alpha.webkit.UncountedCallArgsChecker] Detect more trivial functions Allow address-of operator (&), enum constant, and a reference to constant as well as materializing temporqary expression and an expression with cleanups to appear within a trivial function. --- .../Checkers/WebKit/PtrTypesSemantics.cpp | 21 +++- .../Analysis/Checkers/WebKit/mock-types.h | 1 + .../Checkers/WebKit/uncounted-obj-arg.cpp | 95 ++- 3 files changed, 115 insertions(+), 2 deletions(-) diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp b/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp index bf6f9a64877c64..e8295938a855d7 100644 --- a/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp @@ -285,7 +285,7 @@ class TrivialFunctionAnalysisVisitor bool VisitUnaryOperator(const UnaryOperator *UO) { // Operator '*' and '!' are allowed as long as the operand is trivial. -if (UO->getOpcode() == UO_Deref || UO->getOpcode() == UO_LNot) +if (UO->getOpcode() == UO_Deref || UO->getOpcode() == UO_AddrOf || UO->getOpcode() == UO_LNot) return Visit(UO->getSubExpr()); // Other operators are non-trivial. @@ -306,6 +306,10 @@ class TrivialFunctionAnalysisVisitor if (auto *decl = DRE->getDecl()) { if (isa(decl)) return true; + if (isa(decl)) +return true; + if (auto *VD = dyn_cast(decl)) +return VD->hasConstantInitialization() && VD->getEvaluatedValue(); } return false; } @@ -377,6 +381,15 @@ class TrivialFunctionAnalysisVisitor return Visit(ECE->getSubExpr()); } + bool VisitMaterializeTemporaryExpr(const MaterializeTemporaryExpr *VMT) + { +return Visit(VMT->getSubExpr()); + } + + bool VisitExprWithCleanups(const ExprWithCleanups *EWC) { +return Visit(EWC->getSubExpr()); + } + bool VisitParenExpr(const ParenExpr *PE) { return Visit(PE->getSubExpr()); } bool VisitInitListExpr(const InitListExpr *ILE) { @@ -397,6 +410,12 @@ class TrivialFunctionAnalysisVisitor return true; } + bool VisitCXXNullPtrLiteralExpr(const CXXNullPtrLiteralExpr *E) + { +// nullptr is trivial. +return true; + } + // Constant literal expressions are always trivial bool VisitIntegerLiteral(const IntegerLiteral *E) { return true; } bool VisitFloatingLiteral(const FloatingLiteral *E) { return true; } diff --git a/clang/test/Analysis/Checkers/WebKit/mock-types.h b/clang/test/Analysis/Checkers/WebKit/mock-types.h index cc40487614a83d..d08a997aa8c043 100644 --- a/clang/test/Analysis/Checkers/WebKit/mock-types.h +++ b/clang/test/Analysis/Checkers/WebKit/mock-types.h @@ -19,6 +19,7 @@ template struct RefPtr { RefPtr(T *t) : t(t) {} T *get() { return t; } T *operator->() { return t; } + const T *operator->() const { return t; } T &operator*() { return *t; } RefPtr &operator=(T *) { return *this; } operator bool() { return t; } diff --git a/clang/test/Analysis/Checkers/WebKit/uncounted-obj-arg.cpp b/clang/test/Analysis/Checkers/WebKit/uncounted-obj-arg.cpp index 156a2480901bf0..83c4414d1d01aa 100644 --- a/clang/test/Analysis/Checkers/WebKit/uncounted-obj-arg.cpp +++ b/clang/test/Analysis/Checkers/WebKit/uncounted-obj-arg.cpp @@ -1,7 +1,6 @@ // RUN: %clang_analyze_cc1 -analyzer-checker=alpha.webkit.UncountedCallArgsChecker -verify %s #include "mock-types.h" -//#include void WTFBreakpointTrap(); void WTFCrashWithInfo(int, const char*, const char*, int); @@ -60,11 +59,86 @@ NO_RETURN_DUE_TO_CRASH ALWAYS_INLINE void WTFCrashWithInfo(int line, const char* WTFCrashWithInfoImpl(line, file, function, counter, wtfCrashArg(reason)); } +enum class Flags : unsigned short { + Flag1 = 1 << 0, + Flag2 = 1 << 1, + Flag3 = 1 << 2, +}; + +template class OptionSet { +public: + using StorageType = unsigned short; + + static constexpr OptionSet fromRaw(StorageType rawValue) { +return OptionSet(static_cast(rawValue), FromRawValue); + } + + constexpr OptionSet() = default; + + constexpr OptionSet(E e) +: m_storage(static_cast(e)) { + } + + constexpr StorageType toRaw() const { return m_storage; } + + constexpr bool isEmpty() const { return !m_storage; } + + constexpr explicit operator bool() const { return !isEmpty(); } + + constexpr bool contains(E option) const { return containsAny(option); } + constexpr bool containsAny(OptionSet optionSet) const { +return !!(*this & optionSet); + } + + constexpr bool containsAll(OptionSet o
[clang] [alpha.webkit.UncountedCallArgsChecker] Detect more trivial functions (PR #81829)
https://github.com/rniwa updated https://github.com/llvm/llvm-project/pull/81829 >From 382ce72e206ca80e3414d5a141afa0f4f8b8 Mon Sep 17 00:00:00 2001 From: Ryosuke Niwa Date: Wed, 14 Feb 2024 23:30:27 -0800 Subject: [PATCH] [alpha.webkit.UncountedCallArgsChecker] Detect more trivial functions Allow address-of operator (&), enum constant, and a reference to constant as well as materializing temporqary expression and an expression with cleanups to appear within a trivial function. --- .../Checkers/WebKit/PtrTypesSemantics.cpp | 20 +++- .../Analysis/Checkers/WebKit/mock-types.h | 1 + .../Checkers/WebKit/uncounted-obj-arg.cpp | 95 ++- 3 files changed, 114 insertions(+), 2 deletions(-) diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp b/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp index bf6f9a64877c64..6f236db0474079 100644 --- a/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp @@ -285,7 +285,8 @@ class TrivialFunctionAnalysisVisitor bool VisitUnaryOperator(const UnaryOperator *UO) { // Operator '*' and '!' are allowed as long as the operand is trivial. -if (UO->getOpcode() == UO_Deref || UO->getOpcode() == UO_LNot) +if (UO->getOpcode() == UO_Deref || UO->getOpcode() == UO_AddrOf || +UO->getOpcode() == UO_LNot) return Visit(UO->getSubExpr()); // Other operators are non-trivial. @@ -306,6 +307,10 @@ class TrivialFunctionAnalysisVisitor if (auto *decl = DRE->getDecl()) { if (isa(decl)) return true; + if (isa(decl)) +return true; + if (auto *VD = dyn_cast(decl)) +return VD->hasConstantInitialization() && VD->getEvaluatedValue(); } return false; } @@ -377,6 +382,14 @@ class TrivialFunctionAnalysisVisitor return Visit(ECE->getSubExpr()); } + bool VisitMaterializeTemporaryExpr(const MaterializeTemporaryExpr *VMT) { +return Visit(VMT->getSubExpr()); + } + + bool VisitExprWithCleanups(const ExprWithCleanups *EWC) { +return Visit(EWC->getSubExpr()); + } + bool VisitParenExpr(const ParenExpr *PE) { return Visit(PE->getSubExpr()); } bool VisitInitListExpr(const InitListExpr *ILE) { @@ -397,6 +410,11 @@ class TrivialFunctionAnalysisVisitor return true; } + bool VisitCXXNullPtrLiteralExpr(const CXXNullPtrLiteralExpr *E) { +// nullptr is trivial. +return true; + } + // Constant literal expressions are always trivial bool VisitIntegerLiteral(const IntegerLiteral *E) { return true; } bool VisitFloatingLiteral(const FloatingLiteral *E) { return true; } diff --git a/clang/test/Analysis/Checkers/WebKit/mock-types.h b/clang/test/Analysis/Checkers/WebKit/mock-types.h index cc40487614a83d..d08a997aa8c043 100644 --- a/clang/test/Analysis/Checkers/WebKit/mock-types.h +++ b/clang/test/Analysis/Checkers/WebKit/mock-types.h @@ -19,6 +19,7 @@ template struct RefPtr { RefPtr(T *t) : t(t) {} T *get() { return t; } T *operator->() { return t; } + const T *operator->() const { return t; } T &operator*() { return *t; } RefPtr &operator=(T *) { return *this; } operator bool() { return t; } diff --git a/clang/test/Analysis/Checkers/WebKit/uncounted-obj-arg.cpp b/clang/test/Analysis/Checkers/WebKit/uncounted-obj-arg.cpp index 156a2480901bf0..83c4414d1d01aa 100644 --- a/clang/test/Analysis/Checkers/WebKit/uncounted-obj-arg.cpp +++ b/clang/test/Analysis/Checkers/WebKit/uncounted-obj-arg.cpp @@ -1,7 +1,6 @@ // RUN: %clang_analyze_cc1 -analyzer-checker=alpha.webkit.UncountedCallArgsChecker -verify %s #include "mock-types.h" -//#include void WTFBreakpointTrap(); void WTFCrashWithInfo(int, const char*, const char*, int); @@ -60,11 +59,86 @@ NO_RETURN_DUE_TO_CRASH ALWAYS_INLINE void WTFCrashWithInfo(int line, const char* WTFCrashWithInfoImpl(line, file, function, counter, wtfCrashArg(reason)); } +enum class Flags : unsigned short { + Flag1 = 1 << 0, + Flag2 = 1 << 1, + Flag3 = 1 << 2, +}; + +template class OptionSet { +public: + using StorageType = unsigned short; + + static constexpr OptionSet fromRaw(StorageType rawValue) { +return OptionSet(static_cast(rawValue), FromRawValue); + } + + constexpr OptionSet() = default; + + constexpr OptionSet(E e) +: m_storage(static_cast(e)) { + } + + constexpr StorageType toRaw() const { return m_storage; } + + constexpr bool isEmpty() const { return !m_storage; } + + constexpr explicit operator bool() const { return !isEmpty(); } + + constexpr bool contains(E option) const { return containsAny(option); } + constexpr bool containsAny(OptionSet optionSet) const { +return !!(*this & optionSet); + } + + constexpr bool containsAll(OptionSet optionSet) const { +return (*this & optionSet) == optionSet; + } + + constexpr void add(OptionSet optionSet) { m_storage |= optionSet.m_storage; } + + constexpr void remove(OptionSet op
[clang] [alpha.webkit.UncountedCallArgsChecker] Detect more trivial functions (PR #81829)
https://github.com/rniwa updated https://github.com/llvm/llvm-project/pull/81829 >From 382ce72e206ca80e3414d5a141afa0f4f8b8 Mon Sep 17 00:00:00 2001 From: Ryosuke Niwa Date: Wed, 14 Feb 2024 23:30:27 -0800 Subject: [PATCH] [alpha.webkit.UncountedCallArgsChecker] Detect more trivial functions Allow address-of operator (&), enum constant, and a reference to constant as well as materializing temporqary expression and an expression with cleanups to appear within a trivial function. --- .../Checkers/WebKit/PtrTypesSemantics.cpp | 20 +++- .../Analysis/Checkers/WebKit/mock-types.h | 1 + .../Checkers/WebKit/uncounted-obj-arg.cpp | 95 ++- 3 files changed, 114 insertions(+), 2 deletions(-) diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp b/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp index bf6f9a64877c64..6f236db0474079 100644 --- a/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp @@ -285,7 +285,8 @@ class TrivialFunctionAnalysisVisitor bool VisitUnaryOperator(const UnaryOperator *UO) { // Operator '*' and '!' are allowed as long as the operand is trivial. -if (UO->getOpcode() == UO_Deref || UO->getOpcode() == UO_LNot) +if (UO->getOpcode() == UO_Deref || UO->getOpcode() == UO_AddrOf || +UO->getOpcode() == UO_LNot) return Visit(UO->getSubExpr()); // Other operators are non-trivial. @@ -306,6 +307,10 @@ class TrivialFunctionAnalysisVisitor if (auto *decl = DRE->getDecl()) { if (isa(decl)) return true; + if (isa(decl)) +return true; + if (auto *VD = dyn_cast(decl)) +return VD->hasConstantInitialization() && VD->getEvaluatedValue(); } return false; } @@ -377,6 +382,14 @@ class TrivialFunctionAnalysisVisitor return Visit(ECE->getSubExpr()); } + bool VisitMaterializeTemporaryExpr(const MaterializeTemporaryExpr *VMT) { +return Visit(VMT->getSubExpr()); + } + + bool VisitExprWithCleanups(const ExprWithCleanups *EWC) { +return Visit(EWC->getSubExpr()); + } + bool VisitParenExpr(const ParenExpr *PE) { return Visit(PE->getSubExpr()); } bool VisitInitListExpr(const InitListExpr *ILE) { @@ -397,6 +410,11 @@ class TrivialFunctionAnalysisVisitor return true; } + bool VisitCXXNullPtrLiteralExpr(const CXXNullPtrLiteralExpr *E) { +// nullptr is trivial. +return true; + } + // Constant literal expressions are always trivial bool VisitIntegerLiteral(const IntegerLiteral *E) { return true; } bool VisitFloatingLiteral(const FloatingLiteral *E) { return true; } diff --git a/clang/test/Analysis/Checkers/WebKit/mock-types.h b/clang/test/Analysis/Checkers/WebKit/mock-types.h index cc40487614a83d..d08a997aa8c043 100644 --- a/clang/test/Analysis/Checkers/WebKit/mock-types.h +++ b/clang/test/Analysis/Checkers/WebKit/mock-types.h @@ -19,6 +19,7 @@ template struct RefPtr { RefPtr(T *t) : t(t) {} T *get() { return t; } T *operator->() { return t; } + const T *operator->() const { return t; } T &operator*() { return *t; } RefPtr &operator=(T *) { return *this; } operator bool() { return t; } diff --git a/clang/test/Analysis/Checkers/WebKit/uncounted-obj-arg.cpp b/clang/test/Analysis/Checkers/WebKit/uncounted-obj-arg.cpp index 156a2480901bf0..83c4414d1d01aa 100644 --- a/clang/test/Analysis/Checkers/WebKit/uncounted-obj-arg.cpp +++ b/clang/test/Analysis/Checkers/WebKit/uncounted-obj-arg.cpp @@ -1,7 +1,6 @@ // RUN: %clang_analyze_cc1 -analyzer-checker=alpha.webkit.UncountedCallArgsChecker -verify %s #include "mock-types.h" -//#include void WTFBreakpointTrap(); void WTFCrashWithInfo(int, const char*, const char*, int); @@ -60,11 +59,86 @@ NO_RETURN_DUE_TO_CRASH ALWAYS_INLINE void WTFCrashWithInfo(int line, const char* WTFCrashWithInfoImpl(line, file, function, counter, wtfCrashArg(reason)); } +enum class Flags : unsigned short { + Flag1 = 1 << 0, + Flag2 = 1 << 1, + Flag3 = 1 << 2, +}; + +template class OptionSet { +public: + using StorageType = unsigned short; + + static constexpr OptionSet fromRaw(StorageType rawValue) { +return OptionSet(static_cast(rawValue), FromRawValue); + } + + constexpr OptionSet() = default; + + constexpr OptionSet(E e) +: m_storage(static_cast(e)) { + } + + constexpr StorageType toRaw() const { return m_storage; } + + constexpr bool isEmpty() const { return !m_storage; } + + constexpr explicit operator bool() const { return !isEmpty(); } + + constexpr bool contains(E option) const { return containsAny(option); } + constexpr bool containsAny(OptionSet optionSet) const { +return !!(*this & optionSet); + } + + constexpr bool containsAll(OptionSet optionSet) const { +return (*this & optionSet) == optionSet; + } + + constexpr void add(OptionSet optionSet) { m_storage |= optionSet.m_storage; } + + constexpr void remove(OptionSet op
[clang] [alpha.webkit.UncountedCallArgsChecker] Detect & ignore trivial function calls. (PR #81808)
rniwa wrote: > @rniwa the test you modified in this change seems to be failing on at least > one bot. Can you take a look or revert if you need time to investigate? > > https://lab.llvm.org/buildbot/#/builders/123/builds/25095 Oh, interesting. I suppose this is due to: ``` # | error: 'expected-error' diagnostics seen but not expected: # | File C:\b\slave\clang-x64-windows-msvc\llvm-project\clang\test\Analysis\Checkers\WebKit\uncounted-obj-arg.cpp Line 110: cast from pointer to smaller type 'unsigned long' loses information ``` https://github.com/llvm/llvm-project/pull/81808 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] Fix uncounted-obj-arg.cpp for Windows. (PR #81903)
https://github.com/rniwa created https://github.com/llvm/llvm-project/pull/81903 None >From e3a45512e1a10c341b7172ddd3c2c8d42368110b Mon Sep 17 00:00:00 2001 From: Ryosuke Niwa Date: Thu, 15 Feb 2024 10:58:13 -0800 Subject: [PATCH] Fix uncounted-obj-arg.cpp for Windows. --- clang/test/Analysis/Checkers/WebKit/uncounted-obj-arg.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clang/test/Analysis/Checkers/WebKit/uncounted-obj-arg.cpp b/clang/test/Analysis/Checkers/WebKit/uncounted-obj-arg.cpp index 156a2480901bf0..338e1b685122bc 100644 --- a/clang/test/Analysis/Checkers/WebKit/uncounted-obj-arg.cpp +++ b/clang/test/Analysis/Checkers/WebKit/uncounted-obj-arg.cpp @@ -107,7 +107,7 @@ class RefCounted { return 0; } void *trivial15() { return static_cast(this); } - unsigned long trivial16() { return reinterpret_cast(this); } + unsigned long trivial16() { return *reinterpret_cast(this); } RefCounted& trivial17() const { return const_cast(*this); } RefCounted& trivial18() const { RELEASE_ASSERT(this, "this must be not null"); return const_cast(*this); } void trivial19() const { return; } ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [alpha.webkit.UncountedCallArgsChecker] Detect & ignore trivial function calls. (PR #81808)
rniwa wrote: Fixing the test in https://github.com/llvm/llvm-project/pull/81903 https://github.com/llvm/llvm-project/pull/81808 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [alpha.webkit.UncountedCallArgsChecker] Detect more trivial functions (PR #81829)
https://github.com/rniwa updated https://github.com/llvm/llvm-project/pull/81829 >From 382ce72e206ca80e3414d5a141afa0f4f8b8 Mon Sep 17 00:00:00 2001 From: Ryosuke Niwa Date: Wed, 14 Feb 2024 23:30:27 -0800 Subject: [PATCH] [alpha.webkit.UncountedCallArgsChecker] Detect more trivial functions Allow address-of operator (&), enum constant, and a reference to constant as well as materializing temporqary expression and an expression with cleanups to appear within a trivial function. --- .../Checkers/WebKit/PtrTypesSemantics.cpp | 20 +++- .../Analysis/Checkers/WebKit/mock-types.h | 1 + .../Checkers/WebKit/uncounted-obj-arg.cpp | 95 ++- 3 files changed, 114 insertions(+), 2 deletions(-) diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp b/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp index bf6f9a64877c64..6f236db0474079 100644 --- a/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp @@ -285,7 +285,8 @@ class TrivialFunctionAnalysisVisitor bool VisitUnaryOperator(const UnaryOperator *UO) { // Operator '*' and '!' are allowed as long as the operand is trivial. -if (UO->getOpcode() == UO_Deref || UO->getOpcode() == UO_LNot) +if (UO->getOpcode() == UO_Deref || UO->getOpcode() == UO_AddrOf || +UO->getOpcode() == UO_LNot) return Visit(UO->getSubExpr()); // Other operators are non-trivial. @@ -306,6 +307,10 @@ class TrivialFunctionAnalysisVisitor if (auto *decl = DRE->getDecl()) { if (isa(decl)) return true; + if (isa(decl)) +return true; + if (auto *VD = dyn_cast(decl)) +return VD->hasConstantInitialization() && VD->getEvaluatedValue(); } return false; } @@ -377,6 +382,14 @@ class TrivialFunctionAnalysisVisitor return Visit(ECE->getSubExpr()); } + bool VisitMaterializeTemporaryExpr(const MaterializeTemporaryExpr *VMT) { +return Visit(VMT->getSubExpr()); + } + + bool VisitExprWithCleanups(const ExprWithCleanups *EWC) { +return Visit(EWC->getSubExpr()); + } + bool VisitParenExpr(const ParenExpr *PE) { return Visit(PE->getSubExpr()); } bool VisitInitListExpr(const InitListExpr *ILE) { @@ -397,6 +410,11 @@ class TrivialFunctionAnalysisVisitor return true; } + bool VisitCXXNullPtrLiteralExpr(const CXXNullPtrLiteralExpr *E) { +// nullptr is trivial. +return true; + } + // Constant literal expressions are always trivial bool VisitIntegerLiteral(const IntegerLiteral *E) { return true; } bool VisitFloatingLiteral(const FloatingLiteral *E) { return true; } diff --git a/clang/test/Analysis/Checkers/WebKit/mock-types.h b/clang/test/Analysis/Checkers/WebKit/mock-types.h index cc40487614a83d..d08a997aa8c043 100644 --- a/clang/test/Analysis/Checkers/WebKit/mock-types.h +++ b/clang/test/Analysis/Checkers/WebKit/mock-types.h @@ -19,6 +19,7 @@ template struct RefPtr { RefPtr(T *t) : t(t) {} T *get() { return t; } T *operator->() { return t; } + const T *operator->() const { return t; } T &operator*() { return *t; } RefPtr &operator=(T *) { return *this; } operator bool() { return t; } diff --git a/clang/test/Analysis/Checkers/WebKit/uncounted-obj-arg.cpp b/clang/test/Analysis/Checkers/WebKit/uncounted-obj-arg.cpp index 156a2480901bf0..83c4414d1d01aa 100644 --- a/clang/test/Analysis/Checkers/WebKit/uncounted-obj-arg.cpp +++ b/clang/test/Analysis/Checkers/WebKit/uncounted-obj-arg.cpp @@ -1,7 +1,6 @@ // RUN: %clang_analyze_cc1 -analyzer-checker=alpha.webkit.UncountedCallArgsChecker -verify %s #include "mock-types.h" -//#include void WTFBreakpointTrap(); void WTFCrashWithInfo(int, const char*, const char*, int); @@ -60,11 +59,86 @@ NO_RETURN_DUE_TO_CRASH ALWAYS_INLINE void WTFCrashWithInfo(int line, const char* WTFCrashWithInfoImpl(line, file, function, counter, wtfCrashArg(reason)); } +enum class Flags : unsigned short { + Flag1 = 1 << 0, + Flag2 = 1 << 1, + Flag3 = 1 << 2, +}; + +template class OptionSet { +public: + using StorageType = unsigned short; + + static constexpr OptionSet fromRaw(StorageType rawValue) { +return OptionSet(static_cast(rawValue), FromRawValue); + } + + constexpr OptionSet() = default; + + constexpr OptionSet(E e) +: m_storage(static_cast(e)) { + } + + constexpr StorageType toRaw() const { return m_storage; } + + constexpr bool isEmpty() const { return !m_storage; } + + constexpr explicit operator bool() const { return !isEmpty(); } + + constexpr bool contains(E option) const { return containsAny(option); } + constexpr bool containsAny(OptionSet optionSet) const { +return !!(*this & optionSet); + } + + constexpr bool containsAll(OptionSet optionSet) const { +return (*this & optionSet) == optionSet; + } + + constexpr void add(OptionSet optionSet) { m_storage |= optionSet.m_storage; } + + constexpr void remove(OptionSet op
[clang] [alpha.webkit.UncountedCallArgsChecker] Detect more trivial functions (PR #81829)
https://github.com/rniwa closed https://github.com/llvm/llvm-project/pull/81829 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [alpha.webkit.UncountedCallArgsChecker] Allow ASSERT and atomic operations in a trivial function (PR #82063)
https://github.com/rniwa created https://github.com/llvm/llvm-project/pull/82063 None >From ddaf7bf835668b761e82c25232f2e281a002ff78 Mon Sep 17 00:00:00 2001 From: Ryosuke Niwa Date: Fri, 16 Feb 2024 14:54:55 -0800 Subject: [PATCH] [alpha.webkit.UncountedCallArgsChecker] Allow ASSERT and atomic operations in a trivial function --- .../Checkers/WebKit/ASTUtils.cpp | 4 ++ .../Checkers/WebKit/PtrTypesSemantics.cpp | 18 + .../WebKit/UncountedCallArgsChecker.cpp | 9 + .../call-args-protected-return-value.cpp | 6 ++- .../Analysis/Checkers/WebKit/mock-types.h | 20 +- .../Checkers/WebKit/uncounted-obj-arg.cpp | 39 +-- 6 files changed, 81 insertions(+), 15 deletions(-) diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/ASTUtils.cpp b/clang/lib/StaticAnalyzer/Checkers/WebKit/ASTUtils.cpp index 94eaa81af51772..1a9d6d3127fb7f 100644 --- a/clang/lib/StaticAnalyzer/Checkers/WebKit/ASTUtils.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/ASTUtils.cpp @@ -23,6 +23,10 @@ tryToFindPtrOrigin(const Expr *E, bool StopAtFirstRefCountedObj) { E = tempExpr->getSubExpr(); continue; } +if (auto *tempExpr = dyn_cast(E)) { + E = tempExpr->getSubExpr(); + continue; +} if (auto *cast = dyn_cast(E)) { if (StopAtFirstRefCountedObj) { if (auto *ConversionFunc = diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp b/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp index 6f236db0474079..c713293924c45e 100644 --- a/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp @@ -315,6 +315,10 @@ class TrivialFunctionAnalysisVisitor return false; } + bool VisitAtomicExpr(const AtomicExpr* E) { +return VisitChildren(E); + } + bool VisitStaticAssertDecl(const StaticAssertDecl *SAD) { // Any static_assert is considered trivial. return true; @@ -330,12 +334,18 @@ class TrivialFunctionAnalysisVisitor const auto &Name = safeGetName(Callee); if (Name == "WTFCrashWithInfo" || Name == "WTFBreakpointTrap" || +Name == "WTFReportAssertionFailure" || Name == "compilerFenceForCrash" || Name == "__builtin_unreachable") return true; return TrivialFunctionAnalysis::isTrivialImpl(Callee, Cache); } + bool VisitPredefinedExpr(const PredefinedExpr *E) { +// A predefined identifier such as "func" is considered trivial. +return true; + } + bool VisitCXXMemberCallExpr(const CXXMemberCallExpr *MCE) { if (!checkArguments(MCE)) return false; @@ -356,6 +366,14 @@ class TrivialFunctionAnalysisVisitor return TrivialFunctionAnalysis::isTrivialImpl(Callee, Cache); } + bool VisitCXXDefaultArgExpr(const CXXDefaultArgExpr* E) { +if (auto *Expr = E->getExpr()) { + if (!Visit(Expr)) +return false; +} +return true; + } + bool checkArguments(const CallExpr *CE) { for (const Expr *Arg : CE->arguments()) { if (Arg && !Visit(Arg)) diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedCallArgsChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedCallArgsChecker.cpp index 17a64e1b1b8e04..1bed21d18ed74d 100644 --- a/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedCallArgsChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedCallArgsChecker.cpp @@ -53,6 +53,15 @@ class UncountedCallArgsChecker bool shouldVisitTemplateInstantiations() const { return true; } bool shouldVisitImplicitCode() const { return false; } + bool VisitCXXMethodDecl(const CXXMethodDecl *D) { +if (auto* Class = D->getParent()) { + auto name = safeGetName(Class); + if (isRefCounted(Class)) +return false; // Don't visit contents of Ref/RefPtr methods. +} +return true; + } + bool VisitCallExpr(const CallExpr *CE) { Checker->visitCallExpr(CE); return true; diff --git a/clang/test/Analysis/Checkers/WebKit/call-args-protected-return-value.cpp b/clang/test/Analysis/Checkers/WebKit/call-args-protected-return-value.cpp index 1c4b3df211b1e3..6a8b7464845a26 100644 --- a/clang/test/Analysis/Checkers/WebKit/call-args-protected-return-value.cpp +++ b/clang/test/Analysis/Checkers/WebKit/call-args-protected-return-value.cpp @@ -5,12 +5,14 @@ class RefCounted { public: - void ref(); - void deref(); + void ref() const; + void deref() const; }; class Object { public: + void ref() const; + void deref() const; void someFunction(RefCounted&); }; diff --git a/clang/test/Analysis/Checkers/WebKit/mock-types.h b/clang/test/Analysis/Checkers/WebKit/mock-types.h index d08a997aa8c043..82db67bb031dd6 100644 --- a/clang/test/Analysis/Checkers/WebKit/mock-types.h +++ b/clang/test/Analysis/Checkers/WebKit/mock-types.h @@ -5,7 +5,15 @@ template struct Ref { T *t; Ref() :
[clang] [alpha.webkit.UncountedCallArgsChecker] Allow ASSERT and atomic operations in a trivial function (PR #82063)
https://github.com/rniwa updated https://github.com/llvm/llvm-project/pull/82063 >From 69a593fc7a7e0c18bd2545353772d5da5588c1bb Mon Sep 17 00:00:00 2001 From: Ryosuke Niwa Date: Fri, 16 Feb 2024 14:54:55 -0800 Subject: [PATCH] [alpha.webkit.UncountedCallArgsChecker] Allow ASSERT and atomic operations in a trivial function This PR permits the use of WebKit's ASSERT macros as well as std::atomic operations to appear within a trivial function. It also skips checkers on methods of Ref/RefPtr types. --- .../Checkers/WebKit/ASTUtils.cpp | 4 ++ .../Checkers/WebKit/PtrTypesSemantics.cpp | 18 + .../WebKit/UncountedCallArgsChecker.cpp | 9 + .../call-args-protected-return-value.cpp | 6 ++- .../Analysis/Checkers/WebKit/mock-types.h | 20 +- .../Checkers/WebKit/uncounted-obj-arg.cpp | 39 +-- 6 files changed, 81 insertions(+), 15 deletions(-) diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/ASTUtils.cpp b/clang/lib/StaticAnalyzer/Checkers/WebKit/ASTUtils.cpp index 94eaa81af51772..1a9d6d3127fb7f 100644 --- a/clang/lib/StaticAnalyzer/Checkers/WebKit/ASTUtils.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/ASTUtils.cpp @@ -23,6 +23,10 @@ tryToFindPtrOrigin(const Expr *E, bool StopAtFirstRefCountedObj) { E = tempExpr->getSubExpr(); continue; } +if (auto *tempExpr = dyn_cast(E)) { + E = tempExpr->getSubExpr(); + continue; +} if (auto *cast = dyn_cast(E)) { if (StopAtFirstRefCountedObj) { if (auto *ConversionFunc = diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp b/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp index 6f236db0474079..c713293924c45e 100644 --- a/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp @@ -315,6 +315,10 @@ class TrivialFunctionAnalysisVisitor return false; } + bool VisitAtomicExpr(const AtomicExpr* E) { +return VisitChildren(E); + } + bool VisitStaticAssertDecl(const StaticAssertDecl *SAD) { // Any static_assert is considered trivial. return true; @@ -330,12 +334,18 @@ class TrivialFunctionAnalysisVisitor const auto &Name = safeGetName(Callee); if (Name == "WTFCrashWithInfo" || Name == "WTFBreakpointTrap" || +Name == "WTFReportAssertionFailure" || Name == "compilerFenceForCrash" || Name == "__builtin_unreachable") return true; return TrivialFunctionAnalysis::isTrivialImpl(Callee, Cache); } + bool VisitPredefinedExpr(const PredefinedExpr *E) { +// A predefined identifier such as "func" is considered trivial. +return true; + } + bool VisitCXXMemberCallExpr(const CXXMemberCallExpr *MCE) { if (!checkArguments(MCE)) return false; @@ -356,6 +366,14 @@ class TrivialFunctionAnalysisVisitor return TrivialFunctionAnalysis::isTrivialImpl(Callee, Cache); } + bool VisitCXXDefaultArgExpr(const CXXDefaultArgExpr* E) { +if (auto *Expr = E->getExpr()) { + if (!Visit(Expr)) +return false; +} +return true; + } + bool checkArguments(const CallExpr *CE) { for (const Expr *Arg : CE->arguments()) { if (Arg && !Visit(Arg)) diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedCallArgsChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedCallArgsChecker.cpp index 17a64e1b1b8e04..1bed21d18ed74d 100644 --- a/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedCallArgsChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedCallArgsChecker.cpp @@ -53,6 +53,15 @@ class UncountedCallArgsChecker bool shouldVisitTemplateInstantiations() const { return true; } bool shouldVisitImplicitCode() const { return false; } + bool VisitCXXMethodDecl(const CXXMethodDecl *D) { +if (auto* Class = D->getParent()) { + auto name = safeGetName(Class); + if (isRefCounted(Class)) +return false; // Don't visit contents of Ref/RefPtr methods. +} +return true; + } + bool VisitCallExpr(const CallExpr *CE) { Checker->visitCallExpr(CE); return true; diff --git a/clang/test/Analysis/Checkers/WebKit/call-args-protected-return-value.cpp b/clang/test/Analysis/Checkers/WebKit/call-args-protected-return-value.cpp index 1c4b3df211b1e3..6a8b7464845a26 100644 --- a/clang/test/Analysis/Checkers/WebKit/call-args-protected-return-value.cpp +++ b/clang/test/Analysis/Checkers/WebKit/call-args-protected-return-value.cpp @@ -5,12 +5,14 @@ class RefCounted { public: - void ref(); - void deref(); + void ref() const; + void deref() const; }; class Object { public: + void ref() const; + void deref() const; void someFunction(RefCounted&); }; diff --git a/clang/test/Analysis/Checkers/WebKit/mock-types.h b/clang/test/Analysis/Checkers/WebKit/mock-types.h index d08a997aa8c043..82db67bb031dd6 100644
[clang] [alpha.webkit.UncountedCallArgsChecker] Allow ASSERT and atomic operations in a trivial function (PR #82063)
https://github.com/rniwa updated https://github.com/llvm/llvm-project/pull/82063 >From 8d304967acfd0881a4844d630dab5f954772490c Mon Sep 17 00:00:00 2001 From: Ryosuke Niwa Date: Fri, 16 Feb 2024 14:54:55 -0800 Subject: [PATCH] [alpha.webkit.UncountedCallArgsChecker] Allow ASSERT and atomic operations in a trivial function This PR permits the use of WebKit's ASSERT macros as well as std::atomic operations to appear within a trivial function. Also exempt ref() and deref() member function calls. --- .../Checkers/WebKit/ASTUtils.cpp | 4 ++ .../Checkers/WebKit/PtrTypesSemantics.cpp | 18 + .../WebKit/UncountedCallArgsChecker.cpp | 5 +++ .../call-args-protected-return-value.cpp | 6 ++- .../Analysis/Checkers/WebKit/mock-types.h | 20 +- .../Checkers/WebKit/uncounted-obj-arg.cpp | 39 +-- 6 files changed, 77 insertions(+), 15 deletions(-) diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/ASTUtils.cpp b/clang/lib/StaticAnalyzer/Checkers/WebKit/ASTUtils.cpp index 94eaa81af51772..1a9d6d3127fb7f 100644 --- a/clang/lib/StaticAnalyzer/Checkers/WebKit/ASTUtils.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/ASTUtils.cpp @@ -23,6 +23,10 @@ tryToFindPtrOrigin(const Expr *E, bool StopAtFirstRefCountedObj) { E = tempExpr->getSubExpr(); continue; } +if (auto *tempExpr = dyn_cast(E)) { + E = tempExpr->getSubExpr(); + continue; +} if (auto *cast = dyn_cast(E)) { if (StopAtFirstRefCountedObj) { if (auto *ConversionFunc = diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp b/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp index 6f236db0474079..c713293924c45e 100644 --- a/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp @@ -315,6 +315,10 @@ class TrivialFunctionAnalysisVisitor return false; } + bool VisitAtomicExpr(const AtomicExpr* E) { +return VisitChildren(E); + } + bool VisitStaticAssertDecl(const StaticAssertDecl *SAD) { // Any static_assert is considered trivial. return true; @@ -330,12 +334,18 @@ class TrivialFunctionAnalysisVisitor const auto &Name = safeGetName(Callee); if (Name == "WTFCrashWithInfo" || Name == "WTFBreakpointTrap" || +Name == "WTFReportAssertionFailure" || Name == "compilerFenceForCrash" || Name == "__builtin_unreachable") return true; return TrivialFunctionAnalysis::isTrivialImpl(Callee, Cache); } + bool VisitPredefinedExpr(const PredefinedExpr *E) { +// A predefined identifier such as "func" is considered trivial. +return true; + } + bool VisitCXXMemberCallExpr(const CXXMemberCallExpr *MCE) { if (!checkArguments(MCE)) return false; @@ -356,6 +366,14 @@ class TrivialFunctionAnalysisVisitor return TrivialFunctionAnalysis::isTrivialImpl(Callee, Cache); } + bool VisitCXXDefaultArgExpr(const CXXDefaultArgExpr* E) { +if (auto *Expr = E->getExpr()) { + if (!Visit(Expr)) +return false; +} +return true; + } + bool checkArguments(const CallExpr *CE) { for (const Expr *Arg : CE->arguments()) { if (Arg && !Visit(Arg)) diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedCallArgsChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedCallArgsChecker.cpp index 17a64e1b1b8e04..8d344f9b63961a 100644 --- a/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedCallArgsChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedCallArgsChecker.cpp @@ -73,6 +73,11 @@ class UncountedCallArgsChecker unsigned ArgIdx = isa(CE) && isa_and_nonnull(F); if (auto *MemberCallExpr = dyn_cast(CE)) { +if (auto *MD = MemberCallExpr->getMethodDecl()) { + auto name = safeGetName(MD); + if (name == "ref" || name == "deref") +return; +} auto *E = MemberCallExpr->getImplicitObjectArgument(); QualType ArgType = MemberCallExpr->getObjectType(); std::optional IsUncounted = diff --git a/clang/test/Analysis/Checkers/WebKit/call-args-protected-return-value.cpp b/clang/test/Analysis/Checkers/WebKit/call-args-protected-return-value.cpp index 1c4b3df211b1e3..6a8b7464845a26 100644 --- a/clang/test/Analysis/Checkers/WebKit/call-args-protected-return-value.cpp +++ b/clang/test/Analysis/Checkers/WebKit/call-args-protected-return-value.cpp @@ -5,12 +5,14 @@ class RefCounted { public: - void ref(); - void deref(); + void ref() const; + void deref() const; }; class Object { public: + void ref() const; + void deref() const; void someFunction(RefCounted&); }; diff --git a/clang/test/Analysis/Checkers/WebKit/mock-types.h b/clang/test/Analysis/Checkers/WebKit/mock-types.h index d08a997aa8c043..82db67bb031dd6 100644 --- a/clang/test/Analysis/Checkers/WebKit/mock-types.h +++ b/clang/test/Analysis/Checkers/WebK
[clang] [alpha.webkit.UncountedCallArgsChecker] Allow ASSERT and atomic operations in a trivial function (PR #82063)
https://github.com/rniwa updated https://github.com/llvm/llvm-project/pull/82063 >From c46f11b98f8ec159a19004952fb67705eb0988dd Mon Sep 17 00:00:00 2001 From: Ryosuke Niwa Date: Fri, 16 Feb 2024 14:54:55 -0800 Subject: [PATCH] [alpha.webkit.UncountedCallArgsChecker] Allow ASSERT and atomic operations in a trivial function This PR permits the use of WebKit's ASSERT macros as well as std::atomic operations to appear within a trivial function. Also exempt ref() and deref() member function calls. --- .../Checkers/WebKit/ASTUtils.cpp | 4 ++ .../Checkers/WebKit/PtrTypesSemantics.cpp | 16 .../WebKit/UncountedCallArgsChecker.cpp | 5 +++ .../call-args-protected-return-value.cpp | 6 ++- .../Analysis/Checkers/WebKit/mock-types.h | 20 +- .../Checkers/WebKit/uncounted-obj-arg.cpp | 39 +-- 6 files changed, 75 insertions(+), 15 deletions(-) diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/ASTUtils.cpp b/clang/lib/StaticAnalyzer/Checkers/WebKit/ASTUtils.cpp index 94eaa81af51772..1a9d6d3127fb7f 100644 --- a/clang/lib/StaticAnalyzer/Checkers/WebKit/ASTUtils.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/ASTUtils.cpp @@ -23,6 +23,10 @@ tryToFindPtrOrigin(const Expr *E, bool StopAtFirstRefCountedObj) { E = tempExpr->getSubExpr(); continue; } +if (auto *tempExpr = dyn_cast(E)) { + E = tempExpr->getSubExpr(); + continue; +} if (auto *cast = dyn_cast(E)) { if (StopAtFirstRefCountedObj) { if (auto *ConversionFunc = diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp b/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp index 6f236db0474079..a7891d2da07c18 100644 --- a/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp @@ -315,6 +315,8 @@ class TrivialFunctionAnalysisVisitor return false; } + bool VisitAtomicExpr(const AtomicExpr *E) { return VisitChildren(E); } + bool VisitStaticAssertDecl(const StaticAssertDecl *SAD) { // Any static_assert is considered trivial. return true; @@ -330,12 +332,18 @@ class TrivialFunctionAnalysisVisitor const auto &Name = safeGetName(Callee); if (Name == "WTFCrashWithInfo" || Name == "WTFBreakpointTrap" || +Name == "WTFReportAssertionFailure" || Name == "compilerFenceForCrash" || Name == "__builtin_unreachable") return true; return TrivialFunctionAnalysis::isTrivialImpl(Callee, Cache); } + bool VisitPredefinedExpr(const PredefinedExpr *E) { +// A predefined identifier such as "func" is considered trivial. +return true; + } + bool VisitCXXMemberCallExpr(const CXXMemberCallExpr *MCE) { if (!checkArguments(MCE)) return false; @@ -356,6 +364,14 @@ class TrivialFunctionAnalysisVisitor return TrivialFunctionAnalysis::isTrivialImpl(Callee, Cache); } + bool VisitCXXDefaultArgExpr(const CXXDefaultArgExpr *E) { +if (auto *Expr = E->getExpr()) { + if (!Visit(Expr)) +return false; +} +return true; + } + bool checkArguments(const CallExpr *CE) { for (const Expr *Arg : CE->arguments()) { if (Arg && !Visit(Arg)) diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedCallArgsChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedCallArgsChecker.cpp index 17a64e1b1b8e04..8d344f9b63961a 100644 --- a/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedCallArgsChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedCallArgsChecker.cpp @@ -73,6 +73,11 @@ class UncountedCallArgsChecker unsigned ArgIdx = isa(CE) && isa_and_nonnull(F); if (auto *MemberCallExpr = dyn_cast(CE)) { +if (auto *MD = MemberCallExpr->getMethodDecl()) { + auto name = safeGetName(MD); + if (name == "ref" || name == "deref") +return; +} auto *E = MemberCallExpr->getImplicitObjectArgument(); QualType ArgType = MemberCallExpr->getObjectType(); std::optional IsUncounted = diff --git a/clang/test/Analysis/Checkers/WebKit/call-args-protected-return-value.cpp b/clang/test/Analysis/Checkers/WebKit/call-args-protected-return-value.cpp index 1c4b3df211b1e3..6a8b7464845a26 100644 --- a/clang/test/Analysis/Checkers/WebKit/call-args-protected-return-value.cpp +++ b/clang/test/Analysis/Checkers/WebKit/call-args-protected-return-value.cpp @@ -5,12 +5,14 @@ class RefCounted { public: - void ref(); - void deref(); + void ref() const; + void deref() const; }; class Object { public: + void ref() const; + void deref() const; void someFunction(RefCounted&); }; diff --git a/clang/test/Analysis/Checkers/WebKit/mock-types.h b/clang/test/Analysis/Checkers/WebKit/mock-types.h index d08a997aa8c043..82db67bb031dd6 100644 --- a/clang/test/Analysis/Checkers/WebKit/mock-types.h +++ b/clang/test/Analysis/Checkers/WebKit/mock-ty
[clang] [alpha.webkit.UncountedCallArgsChecker] Ignore calls to WTF's container methods (PR #82156)
https://github.com/rniwa created https://github.com/llvm/llvm-project/pull/82156 This PR makes the checker ignore / skip calls to methods of Web Template Platform's container types such as HashMap, HashSet, WeakHashSet, WeakHashMap, Vector, etc... >From 907c06c6675ae2ca06ec70e005966b2bdd7125c1 Mon Sep 17 00:00:00 2001 From: Ryosuke Niwa Date: Sun, 18 Feb 2024 01:32:00 -0800 Subject: [PATCH] [alpha.webkit.UncountedCallArgsChecker] Ignore calls to WTF's container methods This PR makes the checker ignore / skip calls to methods of Web Template Platform's container types such as HashMap, HashSet, WeakHashSet, WeakHashMap, Vector, etc... --- .../WebKit/UncountedCallArgsChecker.cpp | 32 .../WebKit/call-args-wtf-containers.cpp | 146 ++ 2 files changed, 178 insertions(+) create mode 100644 clang/test/Analysis/Checkers/WebKit/call-args-wtf-containers.cpp diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedCallArgsChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedCallArgsChecker.cpp index 17a64e1b1b8e04..33e640f4d64654 100644 --- a/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedCallArgsChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedCallArgsChecker.cpp @@ -25,6 +25,11 @@ using namespace ento; namespace { +bool stringEndsWith(const std::string& str, const std::string& suffix) { + auto index = str.rfind(suffix); + return index != std::string::npos && str.size() - suffix.size() == index; +} + class UncountedCallArgsChecker : public Checker> { BugType Bug{this, @@ -165,6 +170,9 @@ class UncountedCallArgsChecker if (!Callee) return false; +if (isMethodOnWTFContainerType(Callee)) + return true; + auto overloadedOperatorType = Callee->getOverloadedOperator(); if (overloadedOperatorType == OO_EqualEqual || overloadedOperatorType == OO_ExclaimEqual || @@ -193,6 +201,30 @@ class UncountedCallArgsChecker return false; } + bool isMethodOnWTFContainerType(const FunctionDecl *Decl) const { +if (!isa(Decl)) + return false; +auto *ClassDecl = Decl->getParent(); +if (!ClassDecl || !isa(ClassDecl)) + return false; + +auto *NsDecl = ClassDecl->getParent(); +if (!NsDecl || !isa(NsDecl)) + return false; + +auto methodName = safeGetName(Decl); +auto clsName = safeGetName(ClassDecl); +auto nsName = safeGetName(NsDecl); +// FIXME: These should be implemented via attributes. +return nsName == "WTF" && + (methodName == "find" || methodName == "findIf" || + methodName == "reverseFind" || methodName == "reverseFindIf" || + methodName == "get" || methodName == "inlineGet" || + methodName == "contains" || methodName == "containsIf") && + (stringEndsWith(clsName, "Vector") || stringEndsWith(clsName, "Set") || + stringEndsWith(clsName, "Map")); + } + void reportBug(const Expr *CallArg, const ParmVarDecl *Param) const { assert(CallArg); diff --git a/clang/test/Analysis/Checkers/WebKit/call-args-wtf-containers.cpp b/clang/test/Analysis/Checkers/WebKit/call-args-wtf-containers.cpp new file mode 100644 index 00..0a63a789856127 --- /dev/null +++ b/clang/test/Analysis/Checkers/WebKit/call-args-wtf-containers.cpp @@ -0,0 +1,146 @@ +// RUN: %clang_analyze_cc1 -analyzer-checker=alpha.webkit.UncountedCallArgsChecker -verify %s + +#include "mock-types.h" + +namespace WTF { + + template + class HashSet { + public: +template T* find(U&) const; +template bool contains(U&) const; +unsigned size() { return m_size; } +template void add(U&) const; +template void remove(U&) const; + + private: +T* m_table { nullptr }; +unsigned m_size { 0 }; + }; + + template + class HashMap { + public: +struct Item { + T key; + S value; +}; + +template Item* find(U&) const; +template bool contains(U&) const; +template S* get(U&) const; +template S* inlineGet(U&) const; +template void add(U&) const; +template void remove(U&) const; + + private: +Item* m_table { nullptr }; + }; + + template + class WeakHashSet { + public: +template T* find(U&) const; +template bool contains(U&) const; +template void add(U&) const; +template void remove(U&) const; + }; + + template + class Vector { + public: +unsigned size() { return m_size; } +T& at(unsigned i) { return m_buffer[i]; } +T& operator[](unsigned i) { return m_buffer[i]; } +template unsigned find(U&); +template unsigned reverseFind(U&); +template bool contains(U&); +template unsigned findIf(const MatchFunction& match) +{ + for (unsigned i = 0; i < m_size; ++i) { +if (match(at(i))) + return i; + } + return static_cast(-1); +} +template unsigned reverseFindIf(const MatchFunction& match) +{ + for (unsigned i = 0; i < m_size; ++i) { +if (match(at(m_size
[clang] [Analyzer] Support RefAllowingPartiallyDestroyed and RefPtrAllowingPartiallyDestroyed (PR #82209)
https://github.com/rniwa created https://github.com/llvm/llvm-project/pull/82209 This PR adds the support for WebKit's RefAllowingPartiallyDestroyed and RefPtrAllowingPartiallyDestroyed, which are smart pointer types which may be used after the destructor had started running. >From d26f41816f443922569fc713d1cd919fd96ba124 Mon Sep 17 00:00:00 2001 From: Ryosuke Niwa Date: Sun, 18 Feb 2024 21:47:48 -0800 Subject: [PATCH] [Analyzer] Support RefAllowingPartiallyDestroyed and RefPtrAllowingPartiallyDestroyed This PR adds the support for WebKit's RefAllowingPartiallyDestroyed and RefPtrAllowingPartiallyDestroyed, which are smart pointer types which may be used after the destructor had started running. --- .../Checkers/WebKit/PtrTypesSemantics.cpp | 27 +-- .../Analysis/Checkers/WebKit/mock-types.h | 3 +- .../ref-allowing-partially-destroyed.cpp | 46 +++ 3 files changed, 62 insertions(+), 14 deletions(-) create mode 100644 clang/test/Analysis/Checkers/WebKit/ref-allowing-partially-destroyed.cpp diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp b/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp index 6f236db0474079..1b349d48a9f881 100644 --- a/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp @@ -103,15 +103,18 @@ std::optional isRefCountable(const CXXRecordDecl* R) return hasRef && hasDeref; } +bool isRefType(const std::string& name) { + return name == "Ref" || name == "RefAllowingPartiallyDestroyed" || + name == "RefPtr" || name == "RefPtrAllowingPartiallyDestroyed"; +} + 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" || + return isRefType(FunctionName) || FunctionName == "makeRef" || + FunctionName == "makeRefPtr" || FunctionName == "UniqueRef" || + FunctionName == "makeUniqueRef" || FunctionName == "makeUniqueRefWithoutFastMallocCheck" || FunctionName == "String" || FunctionName == "AtomString" || @@ -131,7 +134,7 @@ bool isReturnValueRefCounted(const clang::FunctionDecl *F) { if (auto *specialT = type->getAs()) { if (auto *decl = specialT->getTemplateName().getAsTemplateDecl()) { auto name = decl->getNameAsString(); -return name == "Ref" || name == "RefPtr"; +return isRefType(name); } return false; } @@ -172,20 +175,18 @@ std::optional isGetterOfRefCounted(const CXXMethodDecl* M) if (isa(M)) { const CXXRecordDecl *calleeMethodsClass = M->getParent(); auto className = safeGetName(calleeMethodsClass); -auto methodName = safeGetName(M); +auto method = safeGetName(M); -if (((className == "Ref" || className == "RefPtr") && - methodName == "get") || -(className == "Ref" && methodName == "ptr") || +if ((isRefType(className) && (method == "get" || method == "ptr")) || ((className == "String" || className == "AtomString" || className == "AtomStringImpl" || className == "UniqueString" || className == "UniqueStringImpl" || className == "Identifier") && - methodName == "impl")) + method == "impl")) return true; // Ref -> T conversion // FIXME: Currently allowing any Ref -> whatever cast. -if (className == "Ref" || className == "RefPtr") { +if (isRefType(className)) { if (auto *maybeRefToRawOperator = dyn_cast(M)) { if (auto *targetConversionType = maybeRefToRawOperator->getConversionType().getTypePtrOrNull()) { @@ -202,7 +203,7 @@ bool isRefCounted(const CXXRecordDecl *R) { if (auto *TmplR = R->getTemplateInstantiationPattern()) { // FIXME: String/AtomString/UniqueString const auto &ClassName = safeGetName(TmplR); -return ClassName == "RefPtr" || ClassName == "Ref"; +return isRefType(ClassName); } return false; } diff --git a/clang/test/Analysis/Checkers/WebKit/mock-types.h b/clang/test/Analysis/Checkers/WebKit/mock-types.h index d08a997aa8c043..e43641c0c61445 100644 --- a/clang/test/Analysis/Checkers/WebKit/mock-types.h +++ b/clang/test/Analysis/Checkers/WebKit/mock-types.h @@ -5,9 +5,10 @@ template struct Ref { T *t; Ref() : t{} {}; - Ref(T *) {} + Ref(T &) {} T *get() { return t; } T *ptr() { return t; } + T *operator->() { return t; } operator const T &() const { return *t; } operator T &() { return *t; } }; diff --git a/clang/test/Analysis/Checkers/WebKit/ref-allowing-partially-destroyed.cpp b/clang/test/Analysis/Checkers/WebKit/ref-allowing-partially-destroyed.cpp new file mode 100644 index 00..f9c0f405968231 --- /dev/null +++ b/clang/te
[clang] [alpha.webkit.UncountedCallArgsChecker] Ignore calls to WTF's container methods (PR #82156)
https://github.com/rniwa updated https://github.com/llvm/llvm-project/pull/82156 >From 51b65a767eee7bae07932c073702502a701d4ef2 Mon Sep 17 00:00:00 2001 From: Ryosuke Niwa Date: Sun, 18 Feb 2024 01:32:00 -0800 Subject: [PATCH] [alpha.webkit.UncountedCallArgsChecker] Ignore calls to WTF's container methods This PR makes the checker ignore / skip calls to methods of Web Template Framework's container types such as HashMap, HashSet, WeakHashSet, WeakHashMap, Vector, etc... --- .../WebKit/UncountedCallArgsChecker.cpp | 32 .../WebKit/call-args-wtf-containers.cpp | 146 ++ 2 files changed, 178 insertions(+) create mode 100644 clang/test/Analysis/Checkers/WebKit/call-args-wtf-containers.cpp diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedCallArgsChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedCallArgsChecker.cpp index 17a64e1b1b8e04..0fed1e2c481332 100644 --- a/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedCallArgsChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedCallArgsChecker.cpp @@ -25,6 +25,11 @@ using namespace ento; namespace { +bool stringEndsWith(const std::string& str, const std::string &suffix) { + auto index = str.rfind(suffix); + return index != std::string::npos && str.size() - suffix.size() == index; +} + class UncountedCallArgsChecker : public Checker> { BugType Bug{this, @@ -165,6 +170,9 @@ class UncountedCallArgsChecker if (!Callee) return false; +if (isMethodOnWTFContainerType(Callee)) + return true; + auto overloadedOperatorType = Callee->getOverloadedOperator(); if (overloadedOperatorType == OO_EqualEqual || overloadedOperatorType == OO_ExclaimEqual || @@ -193,6 +201,30 @@ class UncountedCallArgsChecker return false; } + bool isMethodOnWTFContainerType(const FunctionDecl *Decl) const { +if (!isa(Decl)) + return false; +auto *ClassDecl = Decl->getParent(); +if (!ClassDecl || !isa(ClassDecl)) + return false; + +auto *NsDecl = ClassDecl->getParent(); +if (!NsDecl || !isa(NsDecl)) + return false; + +auto methodName = safeGetName(Decl); +auto clsName = safeGetName(ClassDecl); +auto nsName = safeGetName(NsDecl); +// FIXME: These should be implemented via attributes. +return nsName == "WTF" && + (methodName == "find" || methodName == "findIf" || +methodName == "reverseFind" || methodName == "reverseFindIf" || +methodName == "get" || methodName == "inlineGet" || +methodName == "contains" || methodName == "containsIf") && + (stringEndsWith(clsName, "Vector") || +stringEndsWith(clsName, "Set") || stringEndsWith(clsName, "Map")); + } + void reportBug(const Expr *CallArg, const ParmVarDecl *Param) const { assert(CallArg); diff --git a/clang/test/Analysis/Checkers/WebKit/call-args-wtf-containers.cpp b/clang/test/Analysis/Checkers/WebKit/call-args-wtf-containers.cpp new file mode 100644 index 00..0a63a789856127 --- /dev/null +++ b/clang/test/Analysis/Checkers/WebKit/call-args-wtf-containers.cpp @@ -0,0 +1,146 @@ +// RUN: %clang_analyze_cc1 -analyzer-checker=alpha.webkit.UncountedCallArgsChecker -verify %s + +#include "mock-types.h" + +namespace WTF { + + template + class HashSet { + public: +template T* find(U&) const; +template bool contains(U&) const; +unsigned size() { return m_size; } +template void add(U&) const; +template void remove(U&) const; + + private: +T* m_table { nullptr }; +unsigned m_size { 0 }; + }; + + template + class HashMap { + public: +struct Item { + T key; + S value; +}; + +template Item* find(U&) const; +template bool contains(U&) const; +template S* get(U&) const; +template S* inlineGet(U&) const; +template void add(U&) const; +template void remove(U&) const; + + private: +Item* m_table { nullptr }; + }; + + template + class WeakHashSet { + public: +template T* find(U&) const; +template bool contains(U&) const; +template void add(U&) const; +template void remove(U&) const; + }; + + template + class Vector { + public: +unsigned size() { return m_size; } +T& at(unsigned i) { return m_buffer[i]; } +T& operator[](unsigned i) { return m_buffer[i]; } +template unsigned find(U&); +template unsigned reverseFind(U&); +template bool contains(U&); +template unsigned findIf(const MatchFunction& match) +{ + for (unsigned i = 0; i < m_size; ++i) { +if (match(at(i))) + return i; + } + return static_cast(-1); +} +template unsigned reverseFindIf(const MatchFunction& match) +{ + for (unsigned i = 0; i < m_size; ++i) { +if (match(at(m_size - i))) + return i; + } + return static_cast(-1); +} +template bool containsIf(const MatchFunction& match)
[clang] [alpha.webkit.UncountedCallArgsChecker] Ignore calls to WTF's container methods (PR #82156)
https://github.com/rniwa updated https://github.com/llvm/llvm-project/pull/82156 >From d169fddf3896bd334bc4776059258116ac08096a Mon Sep 17 00:00:00 2001 From: Ryosuke Niwa Date: Sun, 18 Feb 2024 01:32:00 -0800 Subject: [PATCH] [alpha.webkit.UncountedCallArgsChecker] Ignore calls to WTF's container methods This PR makes the checker ignore / skip calls to methods of Web Template Framework's container types such as HashMap, HashSet, WeakHashSet, WeakHashMap, Vector, etc... --- .../WebKit/UncountedCallArgsChecker.cpp | 32 .../WebKit/call-args-wtf-containers.cpp | 146 ++ 2 files changed, 178 insertions(+) create mode 100644 clang/test/Analysis/Checkers/WebKit/call-args-wtf-containers.cpp diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedCallArgsChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedCallArgsChecker.cpp index 17a64e1b1b8e04..9ed7f2a4e065d3 100644 --- a/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedCallArgsChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedCallArgsChecker.cpp @@ -25,6 +25,11 @@ using namespace ento; namespace { +bool stringEndsWith(const std::string &str, const std::string &suffix) { + auto index = str.rfind(suffix); + return index != std::string::npos && str.size() - suffix.size() == index; +} + class UncountedCallArgsChecker : public Checker> { BugType Bug{this, @@ -165,6 +170,9 @@ class UncountedCallArgsChecker if (!Callee) return false; +if (isMethodOnWTFContainerType(Callee)) + return true; + auto overloadedOperatorType = Callee->getOverloadedOperator(); if (overloadedOperatorType == OO_EqualEqual || overloadedOperatorType == OO_ExclaimEqual || @@ -193,6 +201,30 @@ class UncountedCallArgsChecker return false; } + bool isMethodOnWTFContainerType(const FunctionDecl *Decl) const { +if (!isa(Decl)) + return false; +auto *ClassDecl = Decl->getParent(); +if (!ClassDecl || !isa(ClassDecl)) + return false; + +auto *NsDecl = ClassDecl->getParent(); +if (!NsDecl || !isa(NsDecl)) + return false; + +auto methodName = safeGetName(Decl); +auto clsName = safeGetName(ClassDecl); +auto nsName = safeGetName(NsDecl); +// FIXME: These should be implemented via attributes. +return nsName == "WTF" && + (methodName == "find" || methodName == "findIf" || +methodName == "reverseFind" || methodName == "reverseFindIf" || +methodName == "get" || methodName == "inlineGet" || +methodName == "contains" || methodName == "containsIf") && + (stringEndsWith(clsName, "Vector") || +stringEndsWith(clsName, "Set") || stringEndsWith(clsName, "Map")); + } + void reportBug(const Expr *CallArg, const ParmVarDecl *Param) const { assert(CallArg); diff --git a/clang/test/Analysis/Checkers/WebKit/call-args-wtf-containers.cpp b/clang/test/Analysis/Checkers/WebKit/call-args-wtf-containers.cpp new file mode 100644 index 00..0a63a789856127 --- /dev/null +++ b/clang/test/Analysis/Checkers/WebKit/call-args-wtf-containers.cpp @@ -0,0 +1,146 @@ +// RUN: %clang_analyze_cc1 -analyzer-checker=alpha.webkit.UncountedCallArgsChecker -verify %s + +#include "mock-types.h" + +namespace WTF { + + template + class HashSet { + public: +template T* find(U&) const; +template bool contains(U&) const; +unsigned size() { return m_size; } +template void add(U&) const; +template void remove(U&) const; + + private: +T* m_table { nullptr }; +unsigned m_size { 0 }; + }; + + template + class HashMap { + public: +struct Item { + T key; + S value; +}; + +template Item* find(U&) const; +template bool contains(U&) const; +template S* get(U&) const; +template S* inlineGet(U&) const; +template void add(U&) const; +template void remove(U&) const; + + private: +Item* m_table { nullptr }; + }; + + template + class WeakHashSet { + public: +template T* find(U&) const; +template bool contains(U&) const; +template void add(U&) const; +template void remove(U&) const; + }; + + template + class Vector { + public: +unsigned size() { return m_size; } +T& at(unsigned i) { return m_buffer[i]; } +T& operator[](unsigned i) { return m_buffer[i]; } +template unsigned find(U&); +template unsigned reverseFind(U&); +template bool contains(U&); +template unsigned findIf(const MatchFunction& match) +{ + for (unsigned i = 0; i < m_size; ++i) { +if (match(at(i))) + return i; + } + return static_cast(-1); +} +template unsigned reverseFindIf(const MatchFunction& match) +{ + for (unsigned i = 0; i < m_size; ++i) { +if (match(at(m_size - i))) + return i; + } + return static_cast(-1); +} +template bool containsIf(const MatchFunction& match)