// RUN: %clang_analyze_cc1 -analyzer-checker=alpha.webkit.UncountedCallArgsChecker -verify %s #include "mock-types.h" #include "mock-system-header.h" void WTFBreakpointTrap(); void WTFCrashWithInfo(int, const char*, const char*, int); void WTFReportAssertionFailure(const char* file, int line, const char* function, const char* assertion); void WTFReportBacktrace(void); void WTFCrash(void); void WTFCrashWithSecurityImplication(void); inline void compilerFenceForCrash() { asm volatile("" ::: "memory"); } inline void isIntegralOrPointerType() { } template 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 ALWAYS_INLINE unsigned long wtfCrashArg(T* arg) { return reinterpret_cast(arg); } template ALWAYS_INLINE unsigned long wtfCrashArg(T arg) { return arg; } template void WTFCrashWithInfo(int line, const char* file, const char* function, int counter, T reason) { WTFCrashWithInfoImpl(line, file, function, counter, wtfCrashArg(reason)); } template ToType bitwise_cast(FromType from); namespace std { template T* addressof(T& arg); template T&& forward(T& arg); template T&& move( T&& t ); } // namespace std bool isMainThread(); bool isMainThreadOrGCThread(); bool isMainRunLoop(); bool isWebThread(); bool isUIThread(); bool mayBeGCThread(); 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 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(e)) { } StorageType m_storage { 0 }; }; int atoi(const char* str); class Number { public: Number(int v) : v(v) { } Number(double); Number(const char* str) : v(atoi(str)) { } Number operator+(const Number&); Number& operator++() { ++v; return *this; } Number operator++(int) { Number returnValue(v); ++v; return returnValue; } const int& value() const { return v; } void someMethod(); private: int v; }; class DerivedNumber : public Number { public: DerivedNumber(char c) : Number(c - '0') { } DerivedNumber(const char* str) : Number(atoi(str)) { } }; class ComplexNumber { public: ComplexNumber() : realPart(0), complexPart(0) { } ComplexNumber(int real, const char* str) : realPart(real), complexPart(str) { } ComplexNumber(const ComplexNumber&); ComplexNumber& operator++() { realPart.someMethod(); return *this; } ComplexNumber operator++(int); ComplexNumber& operator<<(int); ComplexNumber& operator+(); const Number& real() const { return realPart; } private: Number realPart; Number complexPart; }; class ObjectWithNonTrivialDestructor { public: ObjectWithNonTrivialDestructor() { } ObjectWithNonTrivialDestructor(unsigned v) : v(v) { } ~ObjectWithNonTrivialDestructor() { } unsigned value() const { return v; } private: unsigned v { 0 }; }; class ObjectWithMutatingDestructor { public: ObjectWithMutatingDestructor() : n(0) { } ObjectWithMutatingDestructor(int n) : n(n) { } ~ObjectWithMutatingDestructor() { n.someMethod(); } unsigned value() const { return n.value(); } private: Number n; }; class BaseType { public: BaseType() : n(0) { } BaseType(int v) : n(v) { } BaseType(const char*); private: Number n; }; class SomeType : public BaseType { public: using BaseType::BaseType; }; void __libcpp_verbose_abort(const char *__format, ...); class RefCounted { public: void ref() const; void deref() const; void method(); void someFunction(); int otherFunction(); unsigned recursiveTrivialFunction(int n) { return !n ? 1 : recursiveTrivialFunction(n - 1); } unsigned recursiveComplexFunction(int n) { return !n ? otherFunction() : recursiveComplexFunction(n - 1); } unsigned mutuallyRecursiveFunction1(int n) { return n < 0 ? 1 : (n % 2 ? mutuallyRecursiveFunction2(n - 2) : mutuallyRecursiveFunction1(n - 1)); } unsigned mutuallyRecursiveFunction2(int n) { return n < 0 ? 1 : (n % 3 ? mutuallyRecursiveFunction2(n - 3) : mutuallyRecursiveFunction1(n - 2)); } unsigned mutuallyRecursiveFunction3(int n) { return n < 0 ? 1 : (n % 5 ? mutuallyRecursiveFunction3(n - 5) : mutuallyRecursiveFunction4(n - 3)); } unsigned mutuallyRecursiveFunction4(int n) { return n < 0 ? 1 : (n % 7 ? otherFunction() : mutuallyRecursiveFunction3(n - 3)); } unsigned recursiveFunction5(unsigned n) { return n > 100 ? 2 : (n % 2 ? recursiveFunction5(n + 1) : recursiveFunction6(n + 2)); } unsigned recursiveFunction6(unsigned n) { return n > 100 ? 3 : (n % 2 ? recursiveFunction6(n % 7) : recursiveFunction7(n % 5)); } unsigned recursiveFunction7(unsigned n) { return n > 100 ? 5 : recursiveFunction7(n * 5); } void mutuallyRecursive8() { mutuallyRecursive9(); someFunction(); } void mutuallyRecursive9() { mutuallyRecursive8(); } int recursiveCost() { unsigned totalCost = 0; for (unsigned i = 0; i < sizeof(children)/sizeof(*children); ++i) { if (auto* child = children[i]) totalCost += child->recursiveCost(); } return totalCost; } 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(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; } 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::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; } bool trivial28() { return true; } bool trivial29() { return false; } unsigned trivial30(unsigned v) { unsigned r = 0xff; r |= v; return r; } int trivial31(int* v) { return v[0]; } unsigned trivial32() { return sizeof(int); } unsigned trivial33() { return ~0xff; } template unsigned trivial34() { return v; } void trivial35() { v++; } void trivial36() { ++(*number); } void trivial37() { (*number)++; } void trivial38() { v++; if (__builtin_expect(!!(number), 1)) (*number)++; } int trivial39() { return -v; } int trivial40() { return v << 2; } unsigned trivial41() { v = ++s_v; return v; } unsigned trivial42() { return bitwise_cast(nullptr); } Number* trivial43() { return std::addressof(*number); } Number* trivial44() { return new Number(1); } ComplexNumber* trivial45() { return new ComplexNumber(); } void trivial46() { ASSERT(isMainThread()); } void trivial47() { ASSERT(isMainThreadOrGCThread()); } void trivial48() { ASSERT(isMainRunLoop()); } void trivial49() { ASSERT(isWebThread()); } void trivial50() { ASSERT(isUIThread()); } void trivial51() { ASSERT(mayBeGCThread()); } void trivial52() { WTFCrash(); } void trivial53() { WTFCrashWithSecurityImplication(); } unsigned trivial54() { return ComplexNumber().real().value(); } Number&& trivial55() { return std::forward(*number); } unsigned trivial56() { Number n { 5 }; return std::move(n).value(); } void trivial57() { do { break; } while (1); } void trivial58() { do { continue; } while (0); } void trivial59() { do { goto label; } while (0); label: return; } unsigned trivial60() { return ObjectWithNonTrivialDestructor { 5 }.value(); } unsigned trivial61() { return DerivedNumber('7').value(); } void trivial62() { WTFReportBacktrace(); } SomeType trivial63() { return SomeType(0); } SomeType trivial64() { return SomeType(); } void trivial65() { __libcpp_verbose_abort("%s", "aborting"); } RefPtr trivial66() { return children[0]; } Ref trivial67() { return *children[0]; } static RefCounted& singleton() { static RefCounted s_RefCounted; s_RefCounted.ref(); return s_RefCounted; } static RefCounted& otherSingleton() { 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; } int nonTrivial13() { return ~otherFunction(); } int nonTrivial14() { int r = 0xff; r |= otherFunction(); return r; } void nonTrivial15() { ++complex; } void nonTrivial16() { complex++; } ComplexNumber nonTrivial17() { return complex << 2; } ComplexNumber nonTrivial18() { return +complex; } ComplexNumber* nonTrivial19() { return new ComplexNumber(complex); } unsigned nonTrivial20() { return ObjectWithMutatingDestructor { 7 }.value(); } unsigned nonTrivial21() { return Number("123").value(); } unsigned nonTrivial22() { return ComplexNumber(123, "456").real().value(); } unsigned nonTrivial23() { return DerivedNumber("123").value(); } SomeType nonTrivial24() { return SomeType("123"); } static unsigned s_v; unsigned v { 0 }; Number* number { nullptr }; ComplexNumber complex; Enum enumValue { Enum::Value1 }; RefCounted* children[4]; }; unsigned RefCounted::s_v = 0; RefCounted* refCountedObj(); void test() { refCountedObj()->someFunction(); // expected-warning@-1{{Call argument for 'this' parameter is uncounted and unsafe}} } class UnrelatedClass { RefPtr 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 getFieldTrivial().trivial28(); // no-warning getFieldTrivial().trivial29(); // no-warning getFieldTrivial().trivial30(7); // no-warning int a[] = {1, 2}; getFieldTrivial().trivial31(a); // no-warning getFieldTrivial().trivial32(); // no-warning getFieldTrivial().trivial33(); // no-warning getFieldTrivial().trivial34<7>(); // no-warning getFieldTrivial().trivial35(); // no-warning getFieldTrivial().trivial36(); // no-warning getFieldTrivial().trivial37(); // no-warning getFieldTrivial().trivial38(); // no-warning getFieldTrivial().trivial39(); // no-warning getFieldTrivial().trivial40(); // no-warning getFieldTrivial().trivial41(); // no-warning getFieldTrivial().trivial42(); // no-warning getFieldTrivial().trivial43(); // no-warning getFieldTrivial().trivial44(); // no-warning getFieldTrivial().trivial45(); // no-warning getFieldTrivial().trivial46(); // no-warning getFieldTrivial().trivial47(); // no-warning getFieldTrivial().trivial48(); // no-warning getFieldTrivial().trivial49(); // no-warning getFieldTrivial().trivial50(); // no-warning getFieldTrivial().trivial51(); // no-warning getFieldTrivial().trivial52(); // no-warning getFieldTrivial().trivial53(); // no-warning getFieldTrivial().trivial54(); // no-warning getFieldTrivial().trivial55(); // no-warning getFieldTrivial().trivial56(); // no-warning getFieldTrivial().trivial57(); // no-warning getFieldTrivial().trivial58(); // no-warning getFieldTrivial().trivial59(); // no-warning getFieldTrivial().trivial60(); // no-warning getFieldTrivial().trivial61(); // no-warning getFieldTrivial().trivial62(); // no-warning getFieldTrivial().trivial63(); // no-warning getFieldTrivial().trivial64(); // no-warning getFieldTrivial().trivial65(); // no-warning getFieldTrivial().trivial66()->trivial6(); // no-warning getFieldTrivial().trivial67()->trivial6(); // no-warning RefCounted::singleton().trivial18(); // no-warning RefCounted::singleton().someFunction(); // no-warning RefCounted::otherSingleton().trivial18(); // no-warning RefCounted::otherSingleton().someFunction(); // no-warning getFieldTrivial().recursiveTrivialFunction(7); // no-warning getFieldTrivial().recursiveComplexFunction(9); // expected-warning@-1{{Call argument for 'this' parameter is uncounted and unsafe}} getFieldTrivial().mutuallyRecursiveFunction1(11); // no-warning getFieldTrivial().mutuallyRecursiveFunction2(13); // no-warning getFieldTrivial().mutuallyRecursiveFunction3(17); // expected-warning@-1{{Call argument for 'this' parameter is uncounted and unsafe}} getFieldTrivial().mutuallyRecursiveFunction4(19); // expected-warning@-1{{Call argument for 'this' parameter is uncounted and unsafe}} getFieldTrivial().recursiveFunction5(23); // no-warning getFieldTrivial().recursiveFunction6(29); // no-warning getFieldTrivial().recursiveFunction7(31); // no-warning getFieldTrivial().mutuallyRecursive8(); // expected-warning@-1{{Call argument for 'this' parameter is uncounted and unsafe}} getFieldTrivial().mutuallyRecursive9(); // expected-warning@-1{{Call argument for 'this' parameter is uncounted and unsafe}} getFieldTrivial().recursiveCost(); // 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}} getFieldTrivial().nonTrivial13(); // expected-warning@-1{{Call argument for 'this' parameter is uncounted and unsafe}} getFieldTrivial().nonTrivial14(); // expected-warning@-1{{Call argument for 'this' parameter is uncounted and unsafe}} getFieldTrivial().nonTrivial15(); // expected-warning@-1{{Call argument for 'this' parameter is uncounted and unsafe}} getFieldTrivial().nonTrivial16(); // expected-warning@-1{{Call argument for 'this' parameter is uncounted and unsafe}} getFieldTrivial().nonTrivial17(); // expected-warning@-1{{Call argument for 'this' parameter is uncounted and unsafe}} getFieldTrivial().nonTrivial18(); // expected-warning@-1{{Call argument for 'this' parameter is uncounted and unsafe}} getFieldTrivial().nonTrivial19(); // expected-warning@-1{{Call argument for 'this' parameter is uncounted and unsafe}} getFieldTrivial().nonTrivial20(); // expected-warning@-1{{Call argument for 'this' parameter is uncounted and unsafe}} getFieldTrivial().nonTrivial21(); // expected-warning@-1{{Call argument for 'this' parameter is uncounted and unsafe}} getFieldTrivial().nonTrivial22(); // expected-warning@-1{{Call argument for 'this' parameter is uncounted and unsafe}} getFieldTrivial().nonTrivial23(); // expected-warning@-1{{Call argument for 'this' parameter is uncounted and unsafe}} getFieldTrivial().nonTrivial24(); // expected-warning@-1{{Call argument for 'this' parameter is uncounted and unsafe}} } void setField(RefCounted*); }; class UnrelatedClass2 { RefPtr Field; public: UnrelatedClass &getFieldTrivial() { return *Field.get(); } RefCounted &getFieldTrivialRecursively() { return getFieldTrivial().getFieldTrivial(); } RefCounted *getFieldTrivialTernary() { return Field ? Field->getFieldTernary() : nullptr; } template void callSetField(T&& item, AdditionalArgs&&... args) { item.setField(std::forward(args)...); } template void callSetField2(T&& item, AdditionalArgs&&... args) { item.setField(std::move(args)...); } void test() { getFieldTrivialRecursively().trivial1(); // no-warning getFieldTrivialTernary()->trivial2(); // no-warning getFieldTrivialRecursively().someFunction(); // expected-warning@-1{{Call argument for 'this' parameter is uncounted and unsafe}} callSetField(getFieldTrivial(), refCountedObj()); // no-warning } }; RefPtr object(); void someFunction(const RefCounted&); void test2() { someFunction(*object()); } void system_header() { callMethod(object); }