[clang] [alpha.webkit.UncountedCallArgsChecker] Detect more trivial functions (PR #81829)
Ryosuke Niwa via cfe-commits
cfe-commits at lists.llvm.org
Thu Feb 15 20:32:39 PST 2024
https://github.com/rniwa updated https://github.com/llvm/llvm-project/pull/81829
>From 382ce72e206ca80e34149999d5a141afa0f4f8b8 Mon Sep 17 00:00:00 2001
From: Ryosuke Niwa <rniwa at webkit.org>
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<ParmVarDecl>(decl))
return true;
+ if (isa<EnumConstantDecl>(decl))
+ return true;
+ if (auto *VD = dyn_cast<VarDecl>(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 <typename T> 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 <type_traits>
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<typename E> class OptionSet {
+public:
+ using StorageType = unsigned short;
+
+ static constexpr OptionSet fromRaw(StorageType rawValue) {
+ return OptionSet(static_cast<E>(rawValue), FromRawValue);
+ }
+
+ constexpr OptionSet() = default;
+
+ constexpr OptionSet(E e)
+ : m_storage(static_cast<StorageType>(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 optionSet)
+ {
+ m_storage &= ~optionSet.m_storage;
+ }
+
+ constexpr void set(OptionSet optionSet, bool value)
+ {
+ if (value)
+ add(optionSet);
+ else
+ remove(optionSet);
+ }
+
+ constexpr friend OptionSet operator|(OptionSet lhs, OptionSet rhs) {
+ return fromRaw(lhs.m_storage | rhs.m_storage);
+ }
+
+ constexpr friend OptionSet operator&(OptionSet lhs, OptionSet rhs) {
+ return fromRaw(lhs.m_storage & rhs.m_storage);
+ }
+
+ constexpr friend OptionSet operator-(OptionSet lhs, OptionSet rhs) {
+ return fromRaw(lhs.m_storage & ~rhs.m_storage);
+ }
+
+ constexpr friend OptionSet operator^(OptionSet lhs, OptionSet rhs) {
+ return fromRaw(lhs.m_storage ^ rhs.m_storage);
+ }
+
+private:
+ enum InitializationTag { FromRawValue };
+ constexpr OptionSet(E e, InitializationTag)
+ : m_storage(static_cast<StorageType>(e)) {
+ }
+ StorageType m_storage { 0 };
+};
+
class Number {
public:
Number(int v) : v(v) { }
Number(double);
Number operator+(const Number&);
+ const int& value() const { return v; }
private:
int v;
};
@@ -112,6 +186,19 @@ class RefCounted {
RefCounted& trivial18() const { RELEASE_ASSERT(this, "this must be not null"); return const_cast<RefCounted&>(*this); }
void trivial19() const { return; }
+ static constexpr unsigned numBits = 4;
+ int trivial20() { return v >> numBits; }
+
+ const int* trivial21() { return number ? &number->value() : nullptr; }
+
+ enum class Enum : unsigned short {
+ Value1 = 1,
+ Value2 = 2,
+ };
+ bool trivial22() { return enumValue == Enum::Value1; }
+
+ bool trivial23() const { return OptionSet<Flags>::fromRaw(v).contains(Flags::Flag1); }
+
static RefCounted& singleton() {
static RefCounted s_RefCounted;
s_RefCounted.ref();
@@ -170,6 +257,8 @@ class RefCounted {
}
unsigned v { 0 };
+ Number* number { nullptr };
+ Enum enumValue { Enum::Value1 };
};
RefCounted* refCountedObj();
@@ -208,6 +297,10 @@ class UnrelatedClass {
getFieldTrivial().trivial17(); // no-warning
getFieldTrivial().trivial18(); // no-warning
getFieldTrivial().trivial19(); // no-warning
+ getFieldTrivial().trivial20(); // no-warning
+ getFieldTrivial().trivial21(); // no-warning
+ getFieldTrivial().trivial22(); // no-warning
+ getFieldTrivial().trivial23(); // no-warning
RefCounted::singleton().trivial18(); // no-warning
RefCounted::singleton().someFunction(); // no-warning
More information about the cfe-commits
mailing list