|  | // RUN: %clang_cc1 -std=c++20 -Wunsafe-buffer-usage \ | 
|  | // RUN:            -fsafe-buffer-usage-suggestions -verify %s | 
|  |  | 
|  | [[clang::unsafe_buffer_usage]] | 
|  | void deprecatedFunction3(); | 
|  |  | 
|  | void deprecatedFunction4(int z); | 
|  |  | 
|  | void someFunction(); | 
|  |  | 
|  | [[clang::unsafe_buffer_usage]] | 
|  | void overloading(int* x); | 
|  |  | 
|  | void overloading(char c[]); | 
|  |  | 
|  | void overloading(int* x, int size); | 
|  |  | 
|  | [[clang::unsafe_buffer_usage]] | 
|  | void deprecatedFunction4(int z); | 
|  |  | 
|  | void caller(int z, int* x, int size, char c[]) { | 
|  | deprecatedFunction3(); // expected-warning{{function introduces unsafe buffer manipulation}} | 
|  | deprecatedFunction4(z); // expected-warning{{function introduces unsafe buffer manipulation}} | 
|  | someFunction(); | 
|  |  | 
|  | overloading(x); // expected-warning{{function introduces unsafe buffer manipulation}} | 
|  | overloading(x, size); | 
|  | overloading(c); | 
|  | } | 
|  |  | 
|  | [[clang::unsafe_buffer_usage]] | 
|  | void overloading(char c[]); | 
|  |  | 
|  | // Test variadics | 
|  | [[clang::unsafe_buffer_usage]] | 
|  | void testVariadics(int *ptr, ...); | 
|  |  | 
|  | template<typename T, typename... Args> | 
|  | [[clang::unsafe_buffer_usage]] | 
|  | T adder(T first, Args... args); | 
|  |  | 
|  | template <typename T> | 
|  | void foo(T t) {} | 
|  |  | 
|  | template<> | 
|  | [[clang::unsafe_buffer_usage]] | 
|  | void foo<int *>(int *t) {} | 
|  |  | 
|  | void caller1(int *p, int *q) { | 
|  | testVariadics(p, q);  // expected-warning{{function introduces unsafe buffer manipulation}} | 
|  | adder(p, q);  // expected-warning{{function introduces unsafe buffer manipulation}} | 
|  |  | 
|  | int x; | 
|  | foo(x); | 
|  | foo(&x);  // expected-warning{{function introduces unsafe buffer manipulation}} | 
|  | } | 
|  |  | 
|  | // Test virtual functions | 
|  | class BaseClass { | 
|  | public: | 
|  | [[clang::unsafe_buffer_usage]] | 
|  | virtual void func() {} | 
|  |  | 
|  | virtual void func1() {} | 
|  | }; | 
|  |  | 
|  | class DerivedClass : public BaseClass { | 
|  | public: | 
|  | void func() {} | 
|  |  | 
|  | [[clang::unsafe_buffer_usage]] | 
|  | void func1() {} | 
|  | }; | 
|  |  | 
|  | void testInheritance() { | 
|  | DerivedClass DC; | 
|  | DC.func(); | 
|  | DC.func1();  // expected-warning{{function introduces unsafe buffer manipulation}} | 
|  |  | 
|  | BaseClass *BC; | 
|  | BC->func();  // expected-warning{{function introduces unsafe buffer manipulation}} | 
|  | BC->func1(); | 
|  |  | 
|  | BC = &DC; | 
|  | BC->func();  // expected-warning{{function introduces unsafe buffer manipulation}} | 
|  | BC->func1(); | 
|  | } | 
|  |  | 
|  | class UnsafeMembers { | 
|  | public: | 
|  | UnsafeMembers() {} | 
|  |  | 
|  | [[clang::unsafe_buffer_usage]] | 
|  | UnsafeMembers(int) {} | 
|  |  | 
|  | [[clang::unsafe_buffer_usage]] | 
|  | explicit operator int() { return 0; } | 
|  |  | 
|  | [[clang::unsafe_buffer_usage]] | 
|  | void Method() {} | 
|  |  | 
|  | [[clang::unsafe_buffer_usage]] | 
|  | void operator()() {} | 
|  |  | 
|  | [[clang::unsafe_buffer_usage]] | 
|  | int operator+(UnsafeMembers) { return 0; } | 
|  | }; | 
|  |  | 
|  | template <class... Vs> | 
|  | int testFoldExpression(Vs&&... v) { | 
|  | return (... + v);  // expected-warning{{function introduces unsafe buffer manipulation}} | 
|  | } | 
|  |  | 
|  | struct HoldsUnsafeMembers { | 
|  | HoldsUnsafeMembers() | 
|  | : FromCtor(3),  // expected-warning{{function introduces unsafe buffer manipulation}} | 
|  | FromCtor2{3}  // expected-warning{{function introduces unsafe buffer manipulation}} | 
|  | {} | 
|  |  | 
|  | [[clang::unsafe_buffer_usage]] | 
|  | HoldsUnsafeMembers(int i) | 
|  | : FromCtor(i), | 
|  | FromCtor2{i} {} | 
|  |  | 
|  | HoldsUnsafeMembers(float f) | 
|  | : HoldsUnsafeMembers(0) {}  // expected-warning{{function introduces unsafe buffer manipulation}} | 
|  |  | 
|  | UnsafeMembers FromCtor; | 
|  | UnsafeMembers FromCtor2; | 
|  | UnsafeMembers FromField{3};  // expected-warning {{function introduces unsafe buffer manipulation}} | 
|  | }; | 
|  |  | 
|  | struct SubclassUnsafeMembers : public UnsafeMembers { | 
|  | SubclassUnsafeMembers() | 
|  | : UnsafeMembers(3)  // expected-warning{{function introduces unsafe buffer manipulation}} | 
|  | {} | 
|  |  | 
|  | [[clang::unsafe_buffer_usage]] | 
|  | SubclassUnsafeMembers(int i) | 
|  | : UnsafeMembers(i){} | 
|  | }; | 
|  |  | 
|  | // https://github.com/llvm/llvm-project/issues/80482 | 
|  | void testClassMembers() { | 
|  | UnsafeMembers(3);  // expected-warning{{function introduces unsafe buffer manipulation}} | 
|  |  | 
|  | (void)static_cast<int>(UnsafeMembers());  // expected-warning{{function introduces unsafe buffer manipulation}} | 
|  |  | 
|  | UnsafeMembers().Method();  // expected-warning{{function introduces unsafe buffer manipulation}} | 
|  |  | 
|  | UnsafeMembers()();  // expected-warning{{function introduces unsafe buffer manipulation}} | 
|  |  | 
|  | testFoldExpression(UnsafeMembers(), UnsafeMembers()); | 
|  |  | 
|  | HoldsUnsafeMembers(); | 
|  | HoldsUnsafeMembers(3);  // expected-warning{{function introduces unsafe buffer manipulation}} | 
|  |  | 
|  | SubclassUnsafeMembers(); | 
|  | SubclassUnsafeMembers(3);  // expected-warning{{function introduces unsafe buffer manipulation}} | 
|  | } | 
|  |  | 
|  | // Not an aggregate, so its constructor is not implicit code and will be | 
|  | // visited/checked for warnings. | 
|  | struct NotCalledHoldsUnsafeMembers { | 
|  | NotCalledHoldsUnsafeMembers() | 
|  | : FromCtor(3),  // expected-warning{{function introduces unsafe buffer manipulation}} | 
|  | FromCtor2{3}  // expected-warning{{function introduces unsafe buffer manipulation}} | 
|  | {} | 
|  |  | 
|  | UnsafeMembers FromCtor; | 
|  | UnsafeMembers FromCtor2; | 
|  | UnsafeMembers FromField{3};  // expected-warning{{function introduces unsafe buffer manipulation}} | 
|  | }; | 
|  |  | 
|  | // An aggregate, so its constructor is implicit code. Since it's not called, it | 
|  | // is never generated. | 
|  | struct AggregateUnused { | 
|  | UnsafeMembers f1; | 
|  | // While this field would trigger the warning during initialization, since | 
|  | // it's unused, there's no code generated that does the initialization, so | 
|  | // no warning. | 
|  | UnsafeMembers f2{3}; | 
|  | }; | 
|  |  | 
|  | struct AggregateExplicitlyInitializedSafe { | 
|  | UnsafeMembers f1; | 
|  | // The warning is not fired as the field is always explicltly initialized | 
|  | // elsewhere. This initializer is never used. | 
|  | UnsafeMembers f2{3}; | 
|  | }; | 
|  |  | 
|  | void testAggregateExplicitlyInitializedSafe() { | 
|  | AggregateExplicitlyInitializedSafe A{ | 
|  | .f2 = UnsafeMembers(),  // A safe constructor. | 
|  | }; | 
|  | } | 
|  |  | 
|  | struct AggregateExplicitlyInitializedUnsafe { | 
|  | UnsafeMembers f1; | 
|  | // The warning is not fired as the field is always explicltly initialized | 
|  | // elsewhere. This initializer is never used. | 
|  | UnsafeMembers f2{3}; | 
|  | }; | 
|  |  | 
|  | void testAggregateExplicitlyInitializedUnsafe() { | 
|  | AggregateExplicitlyInitializedUnsafe A{ | 
|  | .f2 = UnsafeMembers(3),  // expected-warning{{function introduces unsafe buffer manipulation}} | 
|  | }; | 
|  | } | 
|  |  | 
|  | struct AggregateViaAggregateInit { | 
|  | UnsafeMembers f1; | 
|  | // FIXME: A construction of this class does initialize the field through | 
|  | // this initializer, so it should warn. Ideally it should also point to | 
|  | // where the site of the construction is in testAggregateViaAggregateInit(). | 
|  | UnsafeMembers f2{3}; | 
|  | }; | 
|  |  | 
|  | void testAggregateViaAggregateInit() { | 
|  | AggregateViaAggregateInit A{}; | 
|  | }; | 
|  |  | 
|  | struct AggregateViaValueInit { | 
|  | UnsafeMembers f1; | 
|  | // FIXME: A construction of this class does initialize the field through | 
|  | // this initializer, so it should warn. Ideally it should also point to | 
|  | // where the site of the construction is in testAggregateViaValueInit(). | 
|  | UnsafeMembers f2{3}; | 
|  | }; | 
|  |  | 
|  | void testAggregateViaValueInit() { | 
|  | auto A = AggregateViaValueInit(); | 
|  | }; | 
|  |  | 
|  | struct AggregateViaDefaultInit { | 
|  | UnsafeMembers f1; | 
|  | // FIXME: A construction of this class does initialize the field through | 
|  | // this initializer, so it should warn. Ideally it should also point to | 
|  | // where the site of the construction is in testAggregateViaValueInit(). | 
|  | UnsafeMembers f2{3}; | 
|  | }; | 
|  |  | 
|  | void testAggregateViaDefaultInit() { | 
|  | AggregateViaDefaultInit A; | 
|  | }; | 
|  |  | 
|  | struct A { | 
|  | int arr[2]; | 
|  |  | 
|  | [[clang::unsafe_buffer_usage]] | 
|  | int *ptr; | 
|  | }; | 
|  |  | 
|  | namespace std{ | 
|  | template <typename T> class span { | 
|  |  | 
|  | T *elements; | 
|  |  | 
|  | public: | 
|  |  | 
|  | constexpr span(T *, unsigned){} | 
|  |  | 
|  | template<class Begin, class End> | 
|  | constexpr span(Begin first, End last){} | 
|  |  | 
|  | constexpr T* data() const noexcept { | 
|  | return elements; | 
|  | } | 
|  | }; | 
|  | } | 
|  |  | 
|  | [[clang::unsafe_buffer_usage]] | 
|  | void check_no_warnings(unsigned idx) { | 
|  | int *arr = new int[20]; | 
|  |  | 
|  | int k = arr[idx]; // no-warning | 
|  |  | 
|  | std::span<int> sp = {arr, 20}; // no-warning | 
|  | A *ptr = reinterpret_cast<A*> (sp.data()); // no-warning | 
|  | A a; | 
|  | a.ptr = arr; // no-warning | 
|  | } | 
|  |  | 
|  | [[clang::unsafe_buffer_usage]] | 
|  | void check_no_warning_variadic(unsigned idx, int arr[20], ...) { | 
|  | int k = arr[idx]; // no-warning | 
|  |  | 
|  | std::span<int> sp = {arr, 20}; // no-warning | 
|  | A *ptr = reinterpret_cast<A*> (sp.data()); // no-warning | 
|  | A a; | 
|  | a.ptr = arr; // no-warning | 
|  | } | 
|  |  | 
|  | template<typename T> | 
|  | [[clang::unsafe_buffer_usage]] | 
|  | void check_no_warnings_template(unsigned idx, T* arr) { | 
|  | int k = arr[idx]; // no-warning | 
|  |  | 
|  | std::span<int> sp = {arr, 20}; // no-warning | 
|  | A *ptr = reinterpret_cast<A*> (sp.data()); // no-warning | 
|  | A a; | 
|  | a.ptr = arr; // no-warning | 
|  | } | 
|  |  | 
|  | void invoke_methods() { | 
|  | int array[20]; | 
|  | check_no_warnings(30); //expected-warning{{function introduces unsafe buffer manipulation}} | 
|  | check_no_warning_variadic(15, array); //expected-warning{{function introduces unsafe buffer manipulation}} | 
|  | check_no_warnings_template(10, array); //expected-warning{{function introduces unsafe buffer manipulation}} | 
|  | } |