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.
336 lines
7.8 KiB
C++
336 lines
7.8 KiB
C++
// RUN: %clang_analyze_cc1 -analyzer-checker=alpha.webkit.UncountedCallArgsChecker -verify %s
|
|
|
|
#include "mock-types.h"
|
|
|
|
RefCountable* provide();
|
|
void consume_refcntbl(RefCountable*);
|
|
void some_function();
|
|
|
|
namespace simple {
|
|
void foo() {
|
|
consume_refcntbl(provide());
|
|
// expected-warning@-1{{Call argument is uncounted and unsafe}}
|
|
}
|
|
|
|
// Test that the checker works with [[clang::suppress]].
|
|
void foo_suppressed() {
|
|
[[clang::suppress]]
|
|
consume_refcntbl(provide()); // no-warning
|
|
}
|
|
}
|
|
|
|
namespace multi_arg {
|
|
void consume_refcntbl(int, RefCountable* foo, bool);
|
|
void foo() {
|
|
consume_refcntbl(42, provide(), true);
|
|
// expected-warning@-1{{Call argument for parameter 'foo' is uncounted and unsafe}}
|
|
}
|
|
}
|
|
|
|
namespace ref_counted {
|
|
Ref<RefCountable> provide_ref_counted() { return Ref<RefCountable>{}; }
|
|
void consume_ref_counted(Ref<RefCountable>) {}
|
|
|
|
void foo() {
|
|
consume_refcntbl(provide_ref_counted().get());
|
|
// no warning
|
|
}
|
|
}
|
|
|
|
namespace methods {
|
|
struct Consumer {
|
|
void consume_ptr(RefCountable* ptr);
|
|
void consume_ref(const RefCountable& ref);
|
|
};
|
|
|
|
void foo() {
|
|
Consumer c;
|
|
|
|
c.consume_ptr(provide());
|
|
// expected-warning@-1{{Call argument for parameter 'ptr' is uncounted and unsafe}}
|
|
c.consume_ref(*provide());
|
|
// expected-warning@-1{{Call argument for parameter 'ref' is uncounted and unsafe}}
|
|
}
|
|
|
|
void foo2() {
|
|
struct Consumer {
|
|
void consume(RefCountable*) { some_function(); }
|
|
void whatever() {
|
|
consume(provide());
|
|
// expected-warning@-1{{Call argument is uncounted and unsafe}}
|
|
}
|
|
};
|
|
}
|
|
|
|
void foo3() {
|
|
struct Consumer {
|
|
void consume(RefCountable*) { some_function(); }
|
|
void whatever() {
|
|
this->consume(provide());
|
|
// expected-warning@-1{{Call argument is uncounted and unsafe}}
|
|
}
|
|
};
|
|
}
|
|
}
|
|
|
|
namespace casts {
|
|
RefCountable* downcast(RefCountable*);
|
|
|
|
void foo() {
|
|
consume_refcntbl(provide());
|
|
// expected-warning@-1{{Call argument is uncounted and unsafe}}
|
|
|
|
consume_refcntbl(static_cast<RefCountable*>(provide()));
|
|
// expected-warning@-1{{Call argument is uncounted and unsafe}}
|
|
|
|
consume_refcntbl(dynamic_cast<RefCountable*>(provide()));
|
|
// expected-warning@-1{{Call argument is uncounted and unsafe}}
|
|
|
|
consume_refcntbl(const_cast<RefCountable*>(provide()));
|
|
// expected-warning@-1{{Call argument is uncounted and unsafe}}
|
|
|
|
consume_refcntbl(reinterpret_cast<RefCountable*>(provide()));
|
|
// expected-warning@-1{{Call argument is uncounted and unsafe}}
|
|
|
|
consume_refcntbl(downcast(provide()));
|
|
// expected-warning@-1{{Call argument is uncounted and unsafe}}
|
|
|
|
consume_refcntbl(
|
|
static_cast<RefCountable*>(
|
|
downcast(
|
|
static_cast<RefCountable*>(
|
|
provide()
|
|
)
|
|
)
|
|
)
|
|
);
|
|
// expected-warning@-8{{Call argument is uncounted and unsafe}}
|
|
}
|
|
}
|
|
|
|
namespace null_ptr {
|
|
void foo_ref() {
|
|
consume_refcntbl(nullptr);
|
|
consume_refcntbl(0);
|
|
}
|
|
}
|
|
|
|
namespace ref_counted_lookalike {
|
|
struct Decoy {
|
|
RefCountable* get() { return nullptr; }
|
|
};
|
|
|
|
void foo() {
|
|
Decoy D;
|
|
|
|
consume_refcntbl(D.get());
|
|
// expected-warning@-1{{Call argument is uncounted and unsafe}}
|
|
}
|
|
}
|
|
|
|
namespace Ref_to_reference_conversion_operator {
|
|
template<typename T> struct Ref {
|
|
Ref() = default;
|
|
Ref(T*) { }
|
|
T* get() { return nullptr; }
|
|
operator T& () { return t; }
|
|
T t;
|
|
};
|
|
|
|
void consume_ref(RefCountable&) {}
|
|
|
|
void foo() {
|
|
Ref<RefCountable> bar;
|
|
consume_ref(bar);
|
|
}
|
|
}
|
|
|
|
namespace param_formarding_function {
|
|
void consume_ref_countable_ref(RefCountable&);
|
|
void consume_ref_countable_ptr(RefCountable*);
|
|
|
|
namespace ptr {
|
|
void foo(RefCountable* param) {
|
|
consume_ref_countable_ptr(param);
|
|
}
|
|
}
|
|
|
|
namespace ref {
|
|
void foo(RefCountable& param) {
|
|
consume_ref_countable_ref(param);
|
|
}
|
|
}
|
|
|
|
namespace ref_deref_operators {
|
|
void foo_ref(RefCountable& param) {
|
|
consume_ref_countable_ptr(¶m);
|
|
}
|
|
|
|
void foo_ptr(RefCountable* param) {
|
|
consume_ref_countable_ref(*param);
|
|
}
|
|
}
|
|
|
|
namespace casts {
|
|
|
|
RefCountable* downcast(RefCountable*) { return nullptr; }
|
|
|
|
template<class T>
|
|
T* bitwise_cast(T*) { return nullptr; }
|
|
|
|
void foo(RefCountable* param) {
|
|
consume_ref_countable_ptr(downcast(param));
|
|
consume_ref_countable_ptr(bitwise_cast(param));
|
|
}
|
|
}
|
|
}
|
|
|
|
namespace param_formarding_lambda {
|
|
auto consume_ref_countable_ref = [](RefCountable&) { some_function(); };
|
|
auto consume_ref_countable_ptr = [](RefCountable*) { some_function(); };
|
|
|
|
namespace ptr {
|
|
void foo(RefCountable* param) {
|
|
consume_ref_countable_ptr(param);
|
|
}
|
|
}
|
|
|
|
namespace ref {
|
|
void foo(RefCountable& param) {
|
|
consume_ref_countable_ref(param);
|
|
}
|
|
}
|
|
|
|
namespace ref_deref_operators {
|
|
void foo_ref(RefCountable& param) {
|
|
consume_ref_countable_ptr(¶m);
|
|
}
|
|
|
|
void foo_ptr(RefCountable* param) {
|
|
consume_ref_countable_ref(*param);
|
|
}
|
|
}
|
|
|
|
namespace casts {
|
|
|
|
RefCountable* downcast(RefCountable*) { return nullptr; }
|
|
|
|
template<class T>
|
|
T* bitwise_cast(T*) { return nullptr; }
|
|
|
|
void foo(RefCountable* param) {
|
|
consume_ref_countable_ptr(downcast(param));
|
|
consume_ref_countable_ptr(bitwise_cast(param));
|
|
}
|
|
}
|
|
}
|
|
|
|
namespace param_forwarding_method {
|
|
struct methodclass {
|
|
void consume_ref_countable_ref(RefCountable&) {};
|
|
static void consume_ref_countable_ptr(RefCountable*) {};
|
|
};
|
|
|
|
namespace ptr {
|
|
void foo(RefCountable* param) {
|
|
methodclass::consume_ref_countable_ptr(param);
|
|
}
|
|
}
|
|
|
|
namespace ref {
|
|
void foo(RefCountable& param) {
|
|
methodclass mc;
|
|
mc.consume_ref_countable_ref(param);
|
|
}
|
|
}
|
|
|
|
namespace ref_deref_operators {
|
|
void foo_ref(RefCountable& param) {
|
|
methodclass::consume_ref_countable_ptr(¶m);
|
|
}
|
|
|
|
void foo_ptr(RefCountable* param) {
|
|
methodclass mc;
|
|
mc.consume_ref_countable_ref(*param);
|
|
}
|
|
}
|
|
|
|
namespace casts {
|
|
|
|
RefCountable* downcast(RefCountable*) { return nullptr; }
|
|
|
|
template<class T>
|
|
T* bitwise_cast(T*) { return nullptr; }
|
|
|
|
void foo(RefCountable* param) {
|
|
methodclass::consume_ref_countable_ptr(downcast(param));
|
|
methodclass::consume_ref_countable_ptr(bitwise_cast(param));
|
|
}
|
|
}
|
|
}
|
|
|
|
namespace downcast {
|
|
void consume_ref_countable(RefCountable*) {}
|
|
RefCountable* downcast(RefCountable*) { return nullptr; }
|
|
|
|
void foo() {
|
|
RefPtr<RefCountable> bar;
|
|
consume_ref_countable( downcast(bar.get()) );
|
|
}
|
|
}
|
|
|
|
namespace string_impl {
|
|
struct String {
|
|
RefCountable* impl() { return nullptr; }
|
|
};
|
|
|
|
struct AtomString {
|
|
RefCountable rc;
|
|
RefCountable& impl() { return rc; }
|
|
};
|
|
|
|
void consume_ptr(RefCountable*) {}
|
|
void consume_ref(RefCountable&) {}
|
|
|
|
namespace simple {
|
|
void foo() {
|
|
String s;
|
|
AtomString as;
|
|
consume_ptr(s.impl());
|
|
consume_ref(as.impl());
|
|
}
|
|
}
|
|
}
|
|
|
|
namespace default_arg {
|
|
RefCountable* global;
|
|
|
|
void function_with_default_arg(RefCountable* param = global);
|
|
// expected-warning@-1{{Call argument for parameter 'param' is uncounted and unsafe}}
|
|
|
|
void foo() {
|
|
function_with_default_arg();
|
|
}
|
|
}
|
|
|
|
namespace cxx_member_operator_call {
|
|
// The hidden this-pointer argument without a corresponding parameter caused couple bugs in parameter <-> argument attribution.
|
|
struct Foo {
|
|
Foo& operator+(RefCountable* bad);
|
|
friend Foo& operator-(Foo& lhs, RefCountable* bad);
|
|
void operator()(RefCountable* bad);
|
|
};
|
|
|
|
RefCountable* global;
|
|
|
|
void foo() {
|
|
Foo f;
|
|
f + global;
|
|
// expected-warning@-1{{Call argument for parameter 'bad' is uncounted and unsafe}}
|
|
f - global;
|
|
// expected-warning@-1{{Call argument for parameter 'bad' is uncounted and unsafe}}
|
|
f(global);
|
|
// expected-warning@-1{{Call argument for parameter 'bad' is uncounted and unsafe}}
|
|
}
|
|
}
|