379 lines
11 KiB
C++
379 lines
11 KiB
C++
// RUN: %clang_analyze_cc1 -analyzer-checker=alpha.webkit.UncountedCallArgsChecker -verify %s
|
|
|
|
#include "mock-types.h"
|
|
|
|
void WTFBreakpointTrap();
|
|
void WTFCrashWithInfo(int, const char*, const char*, int);
|
|
void WTFReportAssertionFailure(const char* file, int line, const char* function, const char* assertion);
|
|
|
|
inline void compilerFenceForCrash()
|
|
{
|
|
asm volatile("" ::: "memory");
|
|
}
|
|
|
|
inline void isIntegralOrPointerType() { }
|
|
|
|
template<typename T, typename... Types>
|
|
void isIntegralOrPointerType(T, Types... types)
|
|
{
|
|
static_assert(sizeof(char) < sizeof(short), "All types need to be bitwise_cast-able to integral type for logging");
|
|
isIntegralOrPointerType(types...);
|
|
}
|
|
|
|
#define CRASH_WITH_INFO(...) do { \
|
|
isIntegralOrPointerType(__VA_ARGS__); \
|
|
compilerFenceForCrash(); \
|
|
WTFBreakpointTrap(); \
|
|
__builtin_unreachable(); \
|
|
} while (0)
|
|
|
|
#define RELEASE_ASSERT(assertion, ...) do { \
|
|
if (!(assertion)) \
|
|
CRASH_WITH_INFO(__VA_ARGS__); \
|
|
} while (0)
|
|
|
|
#define ASSERT(assertion, ...) do { \
|
|
if (!(assertion)) { \
|
|
WTFReportAssertionFailure(__FILE__, __LINE__, __PRETTY_FUNCTION__, #assertion); \
|
|
CRASH_WITH_INFO(__VA_ARGS__); \
|
|
} \
|
|
} while (0)
|
|
|
|
#if !defined(ALWAYS_INLINE)
|
|
#define ALWAYS_INLINE inline
|
|
#endif
|
|
|
|
void WTFCrashWithInfoImpl(int line, const char* file, const char* function, int counter, unsigned long reason);
|
|
void WTFCrashWithInfo(int line, const char* file, const char* function, int counter);
|
|
|
|
template<typename T>
|
|
ALWAYS_INLINE unsigned long wtfCrashArg(T* arg) { return reinterpret_cast<unsigned long>(arg); }
|
|
|
|
template<typename T>
|
|
ALWAYS_INLINE unsigned long wtfCrashArg(T arg) { return arg; }
|
|
|
|
template<typename T>
|
|
void WTFCrashWithInfo(int line, const char* file, const char* function, int counter, T reason)
|
|
{
|
|
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;
|
|
};
|
|
|
|
class RefCounted {
|
|
public:
|
|
void ref() const;
|
|
void deref() const;
|
|
|
|
void someFunction();
|
|
int otherFunction();
|
|
|
|
int trivial1() { return 123; }
|
|
float trivial2() { return 0.3; }
|
|
float trivial3() { return (float)0.4; }
|
|
float trivial4() { return 0.5f; }
|
|
char trivial5() { return 'a'; }
|
|
const char *trivial6() { return "abc"; }
|
|
int trivial7() { return (1); }
|
|
Number trivial8() { return Number { 5 }; }
|
|
int trivial9() { return 3 + 4; }
|
|
int trivial10() { return 0x1010 | 0x1; }
|
|
int trivial11(int v) { return v + 1; }
|
|
const char *trivial12(char *p) { return p ? "str" : "null"; }
|
|
int trivial13(int v) {
|
|
if (v)
|
|
return 123;
|
|
else
|
|
return 0;
|
|
}
|
|
int trivial14(int v) {
|
|
switch (v) {
|
|
case 1:
|
|
return 100;
|
|
case 2:
|
|
return 200;
|
|
default:
|
|
return 300;
|
|
}
|
|
return 0;
|
|
}
|
|
void *trivial15() { return static_cast<void*>(this); }
|
|
unsigned long trivial16() { return *reinterpret_cast<unsigned long*>(this); }
|
|
RefCounted& trivial17() const { return const_cast<RefCounted&>(*this); }
|
|
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); }
|
|
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;
|
|
s_RefCounted.ref();
|
|
return s_RefCounted;
|
|
}
|
|
|
|
Number nonTrivial1() { return Number(3) + Number(4); }
|
|
Number nonTrivial2() { return Number { 0.3 }; }
|
|
int nonTrivial3() { return v ? otherFunction() : 0; }
|
|
int nonTrivial4() {
|
|
if (v)
|
|
return 8;
|
|
else
|
|
return otherFunction();
|
|
}
|
|
|
|
int nonTrivial5() {
|
|
if (v)
|
|
return otherFunction();
|
|
else
|
|
return 9;
|
|
}
|
|
|
|
int nonTrivial6() {
|
|
if (otherFunction())
|
|
return 1;
|
|
else
|
|
return 0;
|
|
}
|
|
|
|
int nonTrivial7() {
|
|
switch (v) {
|
|
case 1:
|
|
return otherFunction();
|
|
default:
|
|
return 7;
|
|
}
|
|
}
|
|
|
|
int nonTrivial8() {
|
|
switch (v) {
|
|
case 1:
|
|
return 9;
|
|
default:
|
|
return otherFunction();
|
|
}
|
|
}
|
|
|
|
int nonTrivial9() {
|
|
switch (otherFunction()) {
|
|
case 0:
|
|
return -1;
|
|
default:
|
|
return 12;
|
|
}
|
|
}
|
|
|
|
static unsigned* another();
|
|
unsigned nonTrivial10() const {
|
|
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 };
|
|
};
|
|
|
|
RefCounted* refCountedObj();
|
|
|
|
void test()
|
|
{
|
|
refCountedObj()->someFunction();
|
|
// expected-warning@-1{{Call argument for 'this' parameter is uncounted and unsafe}}
|
|
}
|
|
|
|
class UnrelatedClass {
|
|
RefPtr<RefCounted> Field;
|
|
bool value;
|
|
|
|
public:
|
|
RefCounted &getFieldTrivial() { return *Field.get(); }
|
|
RefCounted *getFieldTernary() { return value ? Field.get() : nullptr; }
|
|
|
|
void test() {
|
|
getFieldTrivial().trivial1(); // no-warning
|
|
getFieldTrivial().trivial2(); // no-warning
|
|
getFieldTrivial().trivial3(); // no-warning
|
|
getFieldTrivial().trivial4(); // no-warning
|
|
getFieldTrivial().trivial5(); // no-warning
|
|
getFieldTrivial().trivial6(); // no-warning
|
|
getFieldTrivial().trivial7(); // no-warning
|
|
getFieldTrivial().trivial8(); // no-warning
|
|
getFieldTrivial().trivial9(); // no-warning
|
|
getFieldTrivial().trivial10(); // no-warning
|
|
getFieldTrivial().trivial11(1); // no-warning
|
|
getFieldTrivial().trivial12(nullptr); // no-warning
|
|
getFieldTrivial().trivial13(0); // no-warning
|
|
getFieldTrivial().trivial14(3); // no-warning
|
|
getFieldTrivial().trivial15(); // no-warning
|
|
getFieldTrivial().trivial16(); // no-warning
|
|
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
|
|
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
|
|
|
|
getFieldTrivial().someFunction();
|
|
// expected-warning@-1{{Call argument for 'this' parameter is uncounted and unsafe}}
|
|
getFieldTrivial().nonTrivial1();
|
|
// expected-warning@-1{{Call argument for 'this' parameter is uncounted and unsafe}}
|
|
getFieldTrivial().nonTrivial2();
|
|
// expected-warning@-1{{Call argument for 'this' parameter is uncounted and unsafe}}
|
|
getFieldTrivial().nonTrivial3();
|
|
// expected-warning@-1{{Call argument for 'this' parameter is uncounted and unsafe}}
|
|
getFieldTrivial().nonTrivial4();
|
|
// expected-warning@-1{{Call argument for 'this' parameter is uncounted and unsafe}}
|
|
getFieldTrivial().nonTrivial5();
|
|
// expected-warning@-1{{Call argument for 'this' parameter is uncounted and unsafe}}
|
|
getFieldTrivial().nonTrivial6();
|
|
// expected-warning@-1{{Call argument for 'this' parameter is uncounted and unsafe}}
|
|
getFieldTrivial().nonTrivial7();
|
|
// expected-warning@-1{{Call argument for 'this' parameter is uncounted and unsafe}}
|
|
getFieldTrivial().nonTrivial8();
|
|
// expected-warning@-1{{Call argument for 'this' parameter is uncounted and unsafe}}
|
|
getFieldTrivial().nonTrivial9();
|
|
// 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}}
|
|
}
|
|
};
|
|
|
|
class UnrelatedClass2 {
|
|
RefPtr<UnrelatedClass> Field;
|
|
|
|
public:
|
|
UnrelatedClass &getFieldTrivial() { return *Field.get(); }
|
|
RefCounted &getFieldTrivialRecursively() { return getFieldTrivial().getFieldTrivial(); }
|
|
RefCounted *getFieldTrivialTernary() { return Field ? Field->getFieldTernary() : nullptr; }
|
|
|
|
void test() {
|
|
getFieldTrivialRecursively().trivial1(); // no-warning
|
|
getFieldTrivialTernary()->trivial2(); // no-warning
|
|
getFieldTrivialRecursively().someFunction();
|
|
// expected-warning@-1{{Call argument for 'this' parameter is uncounted and unsafe}}
|
|
}
|
|
};
|
|
|
|
RefPtr<RefCounted> object();
|
|
void someFunction(const RefCounted&);
|
|
|
|
void test2() {
|
|
someFunction(*object());
|
|
}
|