-
Notifications
You must be signed in to change notification settings - Fork 14.3k
[alpha.webkit.UncountedCallArgsChecker] Detect more trivial functions #81829
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
@llvm/pr-subscribers-clang Author: Ryosuke Niwa (rniwa) ChangesAllow 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. Full diff: https://github.com/llvm/llvm-project/pull/81829.diff 3 Files Affected:
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<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 +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 <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
|
@llvm/pr-subscribers-clang-static-analyzer-1 Author: Ryosuke Niwa (rniwa) ChangesAllow 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. Full diff: https://github.com/llvm/llvm-project/pull/81829.diff 3 Files Affected:
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<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 +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 <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
|
✅ With the latest revision this PR passed the C/C++ code formatter. |
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.
793c721
to
382ce72
Compare
…llvm#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.
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.