diff options
Diffstat (limited to 'clang/test/Sema')
30 files changed, 1687 insertions, 65 deletions
diff --git a/clang/test/Sema/AArch64/builtin_vectorelements.c b/clang/test/Sema/AArch64/builtin_vectorelements.c new file mode 100644 index 0000000..3391da3 --- /dev/null +++ b/clang/test/Sema/AArch64/builtin_vectorelements.c @@ -0,0 +1,46 @@ +// RUN: %clang_cc1 -triple aarch64-none-linux-gnu -fsyntax-only -verify %s +// REQUIRES: aarch64-registered-target + +#include <arm_sve.h> + +__attribute__((target("sve"))) +long test_builtin_vectorelements_sve(void) { + return __builtin_vectorelements(svuint8_t); +} + +__attribute__((target("sve2p1"))) +long test_builtin_vectorelements_sve2p1(void) { + return __builtin_vectorelements(svuint8_t); +} + +long test_builtin_vectorelements_no_sve(void) { + // expected-error@+1 {{SVE vector type 'svuint8_t' (aka '__SVUint8_t') cannot be used in a target without sve}} + return __builtin_vectorelements(svuint8_t); +} + +__attribute__((target("sme"))) +long test_builtin_vectorelements_sme_streaming(void) __arm_streaming { + return __builtin_vectorelements(svuint8_t); +} + +__attribute__((target("sme2p1"))) +long test_builtin_vectorelements_sme2p1_streaming(void) __arm_streaming { + return __builtin_vectorelements(svuint8_t); +} + +__attribute__((target("sme"))) +long test_builtin_vectorelements_sme(void) { + // expected-error@+1 {{SVE vector type 'svuint8_t' (aka '__SVUint8_t') cannot be used in a non-streaming function}} + return __builtin_vectorelements(svuint8_t); +} + +__attribute__((target("sve,sme"))) +long test_builtin_vectorelements_sve_sme_streaming_compatible(void) __arm_streaming_compatible { + return __builtin_vectorelements(svuint8_t); +} + +__attribute__((target("sme"))) +long test_builtin_vectorelements_sme_streaming_compatible(void) __arm_streaming_compatible { + // expected-error@+1 {{SVE vector type 'svuint8_t' (aka '__SVUint8_t') cannot be used in a non-streaming function}} + return __builtin_vectorelements(svuint8_t); +} diff --git a/clang/test/Sema/AArch64/sve-vector-conditional-op.cpp b/clang/test/Sema/AArch64/sve-vector-conditional-op.cpp new file mode 100644 index 0000000..7fa4ce8 --- /dev/null +++ b/clang/test/Sema/AArch64/sve-vector-conditional-op.cpp @@ -0,0 +1,47 @@ +// RUN: %clang_cc1 %s -fsyntax-only -triple aarch64-none-linux-gnu -target-feature +sve -verify + +typedef int fixed_vector __attribute__((vector_size(4))); + +auto error_fixed_vector_result(__SVBool_t svbool, fixed_vector a, fixed_vector b) { + // expected-error@+1 {{vector condition type '__SVBool_t' and result type 'fixed_vector' (vector of 1 'int' value) do not have the same number of elements}} + return svbool ? a : b; +} + +auto error_void_result(__SVBool_t svbool) { + // expected-error@+1 {{GNU vector conditional operand cannot be void}} + return svbool ? (void)0 : (void)1; +} + +auto error_sve_splat_result_unsupported(__SVBool_t svbool, long long a, long long b) { + // expected-error@+1 {{scalar type 'long long' not supported with vector condition type '__SVBool_t'}} + return svbool ? a : b; +} + +auto error_sve_vector_result_matched_element_count(__SVBool_t svbool, __SVUint32_t a, __SVUint32_t b) { + // expected-error@+1 {{vector condition type '__SVBool_t' and result type '__SVUint32_t' do not have the same number of elements}} + return svbool ? a : b; +} + +auto error_fixed_cond_mixed_scalar_and_vector_operands(fixed_vector cond, unsigned char a, __SVUint8_t b) { + // expected-error@+1 {{cannot mix vectors and sizeless vectors in a vector conditional}} + return cond ? a : b; +} + +auto error_scalable_cond_mixed_scalar_and_vector_operands(__SVBool_t svbool, unsigned char a, fixed_vector b) { + // expected-error@+1 {{cannot mix vectors and sizeless vectors in a vector conditional}} + return svbool ? a : b; +} + +// The following cases should be supported: + +__SVBool_t cond_svbool(__SVBool_t a, __SVBool_t b) { + return a < b ? a : b; +} + +__SVFloat32_t cond_svf32(__SVFloat32_t a, __SVFloat32_t b) { + return a < b ? a : b; +} + +__SVUint64_t cond_u64_splat(__SVUint64_t a) { + return a < 1ul ? a : 1ul; +} diff --git a/clang/test/Sema/PR166843.cpp b/clang/test/Sema/PR166843.cpp new file mode 100644 index 0000000..5a6223b --- /dev/null +++ b/clang/test/Sema/PR166843.cpp @@ -0,0 +1,7 @@ +// RUN: %clang_cc1 -fsyntax-only %s -verify +namespace a { +template <class b> +void c() { + ((::c::x)); // expected-error {{'c' is not a class, namespace, or enumeration}} +} +} diff --git a/clang/test/Sema/aarch64-tme-errors.c b/clang/test/Sema/aarch64-tme-errors.c deleted file mode 100644 index 1cb6f690..0000000 --- a/clang/test/Sema/aarch64-tme-errors.c +++ /dev/null @@ -1,8 +0,0 @@ -// RUN: %clang_cc1 -triple aarch64 -verify %s - -#include "arm_acle.h" - -void test_no_tme_funcs(void) { - __tstart(); // expected-error{{call to undeclared function '__tstart'; ISO C99 and later do not support implicit function declarations}} - __builtin_tstart(); // expected-error{{use of unknown builtin '__builtin_tstart'}} -} diff --git a/clang/test/Sema/aarch64-tme-tcancel-errors.c b/clang/test/Sema/aarch64-tme-tcancel-errors.c deleted file mode 100644 index 365bf81..0000000 --- a/clang/test/Sema/aarch64-tme-tcancel-errors.c +++ /dev/null @@ -1,9 +0,0 @@ -// RUN: %clang_cc1 -triple aarch64 -target-feature +tme -verify %s -void t_cancel_const(unsigned short u) { - __builtin_arm_tcancel(u); // expected-error{{argument to '__builtin_arm_tcancel' must be a constant integer}} -} - -// RUN: %clang_cc1 -triple aarch64 -target-feature +tme -verify %s -void t_cancel_range(void) { - __builtin_arm_tcancel(0x12345u); // expected-error{{argument value 74565 is outside the valid range [0, 65535]}} -} diff --git a/clang/test/Sema/attr-malloc_span.c b/clang/test/Sema/attr-malloc_span.c new file mode 100644 index 0000000..9238b60 --- /dev/null +++ b/clang/test/Sema/attr-malloc_span.c @@ -0,0 +1,82 @@ +// RUN: %clang_cc1 -verify -fsyntax-only %s + +typedef __SIZE_TYPE__ size_t; + +typedef struct { + void *ptr; + size_t n; +} span; +span returns_span (void) __attribute((malloc_span)); // no-warning + +typedef struct { + size_t n; + void *ptr; +} span2; +span2 returns_span2 (void) __attribute((malloc_span)); // no-warning + +typedef struct { + void *ptr; + void *ptr2; +} span3; +span3 returns_span3 (void) __attribute((malloc_span)); // no-warning + +typedef struct { + void *ptr; + int n; +} span4; +span4 returns_span4 (void) __attribute((malloc_span)); // no-warning + +typedef struct incomplete_span incomplete_span; +// expected-warning@+2 {{attribute only applies to functions that return span-like structures}} +// expected-note@+1 {{returned type is incomplete}} +incomplete_span returns_incomplete_span (void) __attribute((malloc_span)); + +// expected-warning@+2 {{attribute only applies to functions that return span-like structures}} +// expected-note@+1 {{returned type is not a struct type}} +int *returns_int_ptr (void) __attribute((malloc_span)); + +typedef struct { + void *ptr; + size_t n; + size_t n2; +} too_long_span; +// expected-warning@+2 {{attribute only applies to functions that return span-like structures}} +// expected-note@+1 {{returned struct has 3 fields, expected 2}} +too_long_span returns_too_long_span (void) __attribute((malloc_span)); + +// Function pointers are not allowed. +typedef struct { + int (*func_ptr)(void); + size_t n; +} func_span; +// expected-warning@+2 {{attribute only applies to functions that return span-like structures}} +// expected-note@+1 {{returned struct fields are not a supported combination}} +func_span returns_func_span (void) __attribute((malloc_span)); + +// Integer should not be an enum. +enum some_enum { some_value, other_value }; +typedef struct { + void *ptr; + enum some_enum field; +} enum_span; +// expected-warning@+2 {{attribute only applies to functions that return span-like structures}} +// expected-note@+1 {{2nd field is expected to be an integer}} +enum_span returns_enum_span (void) __attribute((malloc_span)); + +// Bit integers are also not supported. +typedef struct { + void *ptr; + _BitInt(16) n; +} bit_span; +// expected-warning@+2 {{attribute only applies to functions that return span-like structures}} +// expected-note@+1 {{2nd field is expected to be an integer}} +bit_span returns_bit_span (void) __attribute((malloc_span)); + +// Integer must be at least as big as int. +typedef struct { + void *ptr; + short n; +} short_span; +// expected-warning@+2 {{attribute only applies to functions that return span-like structures}} +// expected-note@+1 {{2nd field of span-like type is not a wide enough integer (minimum width: 32)}} +short_span returns_short_span (void) __attribute((malloc_span)); diff --git a/clang/test/Sema/attr-modular-format.c b/clang/test/Sema/attr-modular-format.c new file mode 100644 index 0000000..fc5b28b --- /dev/null +++ b/clang/test/Sema/attr-modular-format.c @@ -0,0 +1,26 @@ +//RUN: %clang_cc1 -fsyntax-only -verify %s + +int printf(const char *fmt, ...) __attribute__((modular_format(__modular_printf, "__printf", "float"))); // no-error +int myprintf(const char *fmt, ...) __attribute__((modular_format(__modular_printf, "__printf", "float"))); // expected-error {{'modular_format' attribute requires 'format' attribute}} + +int dupe(const char *fmt, ...) __attribute__((modular_format(__modular_printf, "__printf", "float", "int", "float"), format(printf, 1, 2))); // expected-error {{duplicate aspect 'float' in 'modular_format' attribute}} +int multi_dupe(const char *fmt, ...) __attribute__((modular_format(__modular_printf, "__printf", "float", "int", "float", "int"), format(printf, 1, 2))); // expected-error {{duplicate aspect 'float' in 'modular_format' attribute}} \ + // expected-error {{duplicate aspect 'int' in 'modular_format' attribute}} + +// Test with multiple identical attributes on the same declaration. +int same_attr(const char *fmt, ...) __attribute__((modular_format(__modular_printf, "__printf", "float"), modular_format(__modular_printf, "__printf", "float"), format(printf, 1, 2))); // no-warning + +// Test with multiple different attributes on the same declaration. +int diff_attr(const char *fmt, ...) __attribute__((modular_format(__modular_printf, "__printf", "float"), format(printf, 1, 2), modular_format(__modular_printf, "__printf", "int"))); // expected-error {{attribute 'modular_format' is already applied with different arguments}} expected-note {{conflicting attribute is here}} + +int diff_attr2(const char *fmt, ...) __attribute__((modular_format(__modular_printf, "__printf", "float"), format(printf, 1, 2), modular_format(__modular_printf, "__other", "float"))); // expected-error {{attribute 'modular_format' is already applied with different arguments}} expected-note {{conflicting attribute is here}} + +int diff_attr3(const char *fmt, ...) __attribute__((modular_format(__modular_printf, "__printf", "float"), format(printf, 1, 2), modular_format(__other, "__printf", "float"))); // expected-error {{attribute 'modular_format' is already applied with different arguments}} expected-note {{conflicting attribute is here}} + +// Test with same attributes but different aspect order. +int diff_order(const char *fmt, ...) __attribute__((modular_format(__modular_printf, "__printf", "float", "int"), format(printf, 1, 2), modular_format(__modular_printf, "__printf", "int", "float"))); // no-error + +// Test with multiple different attributes on a declaration and a redeclaration +int redecl(const char *fmt, ...) __attribute__((format(printf, 1, 2))); // no-error +int redecl(const char *fmt, ...) __attribute__((modular_format(__modular_printf, "__printf", "float"))); // expected-note {{conflicting attribute is here}} +int redecl(const char *fmt, ...) __attribute__((modular_format(__modular_printf, "__printf", "int"))); // expected-error {{attribute 'modular_format' is already applied with different arguments}} diff --git a/clang/test/Sema/attr-target-clones-aarch64.c b/clang/test/Sema/attr-target-clones-aarch64.c index 93d87ce..e468fd8 100644 --- a/clang/test/Sema/attr-target-clones-aarch64.c +++ b/clang/test/Sema/attr-target-clones-aarch64.c @@ -80,3 +80,15 @@ int useage(void) { int __attribute__((target_clones("sve2-sha3+ssbs", "sm4"))) mv_after_use(void) { return 1; } // expected-error@+1 {{'main' cannot be a multiversioned function}} int __attribute__((target_clones("i8mm"))) main() { return 1; } + +int __attribute__((target_clones("aes + sve2 ; priority=100", "default"))) priority_whitespace(void) { return 0; } + +//expected-warning@+2 {{unsupported 'priority=10' in the 'target_clones' attribute string; 'target_clones' attribute ignored}} +//expected-warning@+1 {{version list contains entries that don't impact code generation}} +int __attribute__((target_clones("priority=10;aes", "default"))) priority_before_features(void) { return 0; } + +//expected-warning@+1 {{version priority '0' is outside the allowed range [1-255]; ignoring priority}} +int __attribute__((target_clones("aes;priority=0", "default"))) priority_out_of_range(void) { return 0; } + +//expected-warning@+1 {{priority of default version cannot be overridden; ignoring priority}} +int __attribute__((target_clones("aes", "default;priority=10"))) priority_default_version(void) { return 0; } diff --git a/clang/test/Sema/attr-target-clones.c b/clang/test/Sema/attr-target-clones.c index 4597ea5..4068877 100644 --- a/clang/test/Sema/attr-target-clones.c +++ b/clang/test/Sema/attr-target-clones.c @@ -28,6 +28,17 @@ int __attribute__((target_clones("sse4.2", "arch=atom", "default"))) redecl4(voi int __attribute__((target_clones("sse4.2", "arch=sandybridge", "default"))) redecl4(void) { return 1; } +int __attribute__((target_clones("sse4.2", "default"))) redecl5(void); +int redecl5(void) { return 1; } + +int redecl6(void); +int __attribute__((target_clones("sse4.2", "default"))) redecl6(void) { return 1; } + +int __attribute__((target_clones("sse4.2", "default"))) redecl7(void); +// expected-error@+2 {{multiversioning attributes cannot be combined}} +// expected-note@-2 {{previous declaration is here}} +int __attribute__((target("sse4.2"))) redecl7(void) { return 1; } + int __attribute__((target("sse4.2"))) redef2(void) { return 1; } // expected-error@+2 {{multiversioning attributes cannot be combined}} // expected-note@-2 {{previous declaration is here}} @@ -87,6 +98,8 @@ int useage(void) { int __attribute__((target_clones("sse4.2", "default"))) mv_after_use(void) { return 1; } void bad_overload1(void) __attribute__((target_clones("mmx", "sse4.2", "default"))); +// expected-error@+2 {{conflicting types for 'bad_overload1'}} +// expected-note@-2 {{previous declaration is here}} void bad_overload1(int p) {} void bad_overload2(int p) {} diff --git a/clang/test/Sema/attr-target-version.c b/clang/test/Sema/attr-target-version.c index d062212..29dfc3e 100644 --- a/clang/test/Sema/attr-target-version.c +++ b/clang/test/Sema/attr-target-version.c @@ -117,3 +117,14 @@ int unspec_args_implicit_default_first(); int __attribute__((target_version("aes"))) unspec_args_implicit_default_first() { return -1; } // expected-note@+1 {{function multiversioning caused by this declaration}} int __attribute__((target_version("default"))) unspec_args_implicit_default_first() { return 0; } + +int __attribute__((target_version("aes + sve2 ; priority=100"))) priority_whitespace(void) { return 0; } + +//expected-warning@+1 {{unsupported 'priority=10' in the 'target_version' attribute string; 'target_version' attribute ignored}} +int __attribute__((target_version("priority=10;aes"))) priority_before_features(void) { return 0; } + +//expected-warning@+1 {{version priority '256' is outside the allowed range [1-255]; ignoring priority}} +int __attribute__((target_version("aes;priority=256"))) priority_out_of_range(void) { return 0; } + +//expected-warning@+1 {{priority of default version cannot be overridden; ignoring priority}} +int __attribute__((target_version("default;priority=10"))) priority_default_version(void) { return 0; } diff --git a/clang/test/Sema/builtin-counted-by-ref.c b/clang/test/Sema/builtin-counted-by-ref.c index a9f46a3..ed766d3 100644 --- a/clang/test/Sema/builtin-counted-by-ref.c +++ b/clang/test/Sema/builtin-counted-by-ref.c @@ -32,14 +32,14 @@ void test2(struct fam_struct *ptr, int idx) { } void test3(struct fam_struct *ptr, int idx) { - __builtin_counted_by_ref(&ptr->array[0]); // expected-error {{'__builtin_counted_by_ref' argument must reference a flexible array member}} - __builtin_counted_by_ref(&ptr->array[idx]); // expected-error {{'__builtin_counted_by_ref' argument must reference a flexible array member}} - __builtin_counted_by_ref(&ptr->array); // expected-error {{'__builtin_counted_by_ref' argument must reference a flexible array member}} - __builtin_counted_by_ref(ptr->x); // expected-error {{'__builtin_counted_by_ref' argument must reference a flexible array member}} - __builtin_counted_by_ref(&ptr->x); // expected-error {{'__builtin_counted_by_ref' argument must reference a flexible array member}} - __builtin_counted_by_ref(global_array); // expected-error {{'__builtin_counted_by_ref' argument must reference a flexible array member}} - __builtin_counted_by_ref(global_int); // expected-error {{'__builtin_counted_by_ref' argument must reference a flexible array member}} - __builtin_counted_by_ref(&global_int); // expected-error {{'__builtin_counted_by_ref' argument must reference a flexible array member}} + __builtin_counted_by_ref(&ptr->array[0]); // expected-error {{'__builtin_counted_by_ref' argument must reference a member with the 'counted_by' attribute}} + __builtin_counted_by_ref(&ptr->array[idx]); // expected-error {{'__builtin_counted_by_ref' argument must reference a member with the 'counted_by' attribute}} + __builtin_counted_by_ref(&ptr->array); // expected-error {{'__builtin_counted_by_ref' argument must reference a member with the 'counted_by' attribute}} + __builtin_counted_by_ref(ptr->x); // expected-error {{'__builtin_counted_by_ref' argument must reference a member with the 'counted_by' attribute}} + __builtin_counted_by_ref(&ptr->x); // expected-error {{'__builtin_counted_by_ref' argument must reference a member with the 'counted_by' attribute}} + __builtin_counted_by_ref(global_array); // expected-error {{'__builtin_counted_by_ref' argument must reference a member with the 'counted_by' attribute}} + __builtin_counted_by_ref(global_int); // expected-error {{'__builtin_counted_by_ref' argument must reference a member with the 'counted_by' attribute}} + __builtin_counted_by_ref(&global_int); // expected-error {{'__builtin_counted_by_ref' argument must reference a member with the 'counted_by' attribute}} } void test4(struct fam_struct *ptr, int idx) { @@ -78,10 +78,12 @@ struct non_fam_struct { }; void *test7(struct non_fam_struct *ptr, int size) { - *__builtin_counted_by_ref(ptr->array) = size // expected-error {{'__builtin_counted_by_ref' argument must reference a flexible array member}} - *__builtin_counted_by_ref(&ptr->array[0]) = size; // expected-error {{'__builtin_counted_by_ref' argument must reference a flexible array member}} - *__builtin_counted_by_ref(ptr->pointer) = size; // expected-error {{'__builtin_counted_by_ref' argument must reference a flexible array member}} - *__builtin_counted_by_ref(&ptr->pointer[0]) = size; // expected-error {{'__builtin_counted_by_ref' argument must reference a flexible array member}} + // Arrays and pointers without counted_by return void* + _Static_assert(_Generic(__builtin_counted_by_ref(ptr->array), void * : 1, default : 0) == 1, "should be void*"); + _Static_assert(_Generic(__builtin_counted_by_ref(ptr->pointer), void * : 1, default : 0) == 1, "should be void*"); + // These are not direct member accesses, so they error + __builtin_counted_by_ref(&ptr->array[0]); // expected-error {{'__builtin_counted_by_ref' argument must reference a member with the 'counted_by' attribute}} + __builtin_counted_by_ref(&ptr->pointer[0]); // expected-error {{'__builtin_counted_by_ref' argument must reference a member with the 'counted_by' attribute}} } struct char_count { @@ -122,3 +124,64 @@ void test8(void) { _Static_assert(_Generic(__builtin_counted_by_ref(lp->array), long * : 1, default : 0) == 1, "wrong return type"); _Static_assert(_Generic(__builtin_counted_by_ref(ulp->array), unsigned long * : 1, default : 0) == 1, "wrong return type"); } + +// Tests for pointer members with counted_by attribute +struct ptr_char_count { + char count; + int *ptr __attribute__((counted_by(count))); +} *pcp; + +struct ptr_short_count { + short count; + int *ptr __attribute__((counted_by(count))); +} *psp; + +struct ptr_int_count { + int count; + int *ptr __attribute__((counted_by(count))); +} *pip; + +struct ptr_unsigned_count { + unsigned count; + int *ptr __attribute__((counted_by(count))); +} *pup; + +struct ptr_long_count { + long count; + int *ptr __attribute__((counted_by(count))); +} *plp; + +struct ptr_unsigned_long_count { + unsigned long count; + int *ptr __attribute__((counted_by(count))); +} *pulp; + +void test9(struct ptr_int_count *ptr, int size) { + size_t size_of = sizeof(__builtin_counted_by_ref(ptr->ptr)); // ok + int align_of = __alignof(__builtin_counted_by_ref(ptr->ptr)); // ok + size_t __ignored_assignment; + + *__builtin_counted_by_ref(ptr->ptr) = size; // ok + *_Generic(__builtin_counted_by_ref(ptr->ptr), + void *: &__ignored_assignment, + default: __builtin_counted_by_ref(ptr->ptr)) = 42; // ok +} + +void test10(void) { + _Static_assert(_Generic(__builtin_counted_by_ref(pcp->ptr), char * : 1, default : 0) == 1, "wrong return type"); + _Static_assert(_Generic(__builtin_counted_by_ref(psp->ptr), short * : 1, default : 0) == 1, "wrong return type"); + _Static_assert(_Generic(__builtin_counted_by_ref(pip->ptr), int * : 1, default : 0) == 1, "wrong return type"); + _Static_assert(_Generic(__builtin_counted_by_ref(pup->ptr), unsigned int * : 1, default : 0) == 1, "wrong return type"); + _Static_assert(_Generic(__builtin_counted_by_ref(plp->ptr), long * : 1, default : 0) == 1, "wrong return type"); + _Static_assert(_Generic(__builtin_counted_by_ref(pulp->ptr), unsigned long * : 1, default : 0) == 1, "wrong return type"); +} + +// Pointer without counted_by returns void* +struct ptr_no_attr { + int count; + int *ptr; // No counted_by attribute +}; + +void test11(struct ptr_no_attr *p) { + _Static_assert(_Generic(__builtin_counted_by_ref(p->ptr), void * : 1, default : 0) == 1, "should be void*"); +} diff --git a/clang/test/Sema/constant-builtins-2.c b/clang/test/Sema/constant-builtins-2.c index e465a3c5f..9579052b 100644 --- a/clang/test/Sema/constant-builtins-2.c +++ b/clang/test/Sema/constant-builtins-2.c @@ -479,6 +479,28 @@ int h0 = __builtin_types_compatible_p(int, float); int h3 = __builtin_bswap16(0x1234) == 0x3412 ? 1 : f(); int h4 = __builtin_bswap32(0x1234) == 0x34120000 ? 1 : f(); int h5 = __builtin_bswap64(0x1234) == 0x3412000000000000 ? 1 : f(); +int h6 = __builtin_bswapg((char)(0x12)) == (char)(0x12) ? 1 : f(); +int h7 = __builtin_bswapg((short)(0x1234)) == (short)(0x3412) ? 1 : f(); +int h8 = __builtin_bswapg(0x00001234) == 0x34120000 ? 1 : f(); +int h9 = __builtin_bswapg(0x0000000000001234ULL) == 0x3412000000000000 ? 1 : f(); +float h10 = __builtin_bswapg(1.0f); // expected-error {{1st argument must be a scalar integer type (was 'float')}} +double h12 = __builtin_bswapg(1.0L); // expected-error {{1st argument must be a scalar integer type (was 'long double')}} +char *h13 = __builtin_bswapg("hello"); // expected-error {{1st argument must be a scalar integer type (was 'char[6]')}} +int h14 = __builtin_bswapg(1, 2); // expected-error {{too many arguments to function call, expected 1, have 2}} +int *h15 = __builtin_bswapg(&h9); // expected-error {{1st argument must be a scalar integer type (was 'int *')}} +int arr[4] = {0x12, 0x34, 0x56, 0x78}; +int h16 = __builtin_bswapg(arr); // expected-error {{1st argument must be a scalar integer type (was 'int[4]')}} +enum BasicEnum { + ENUM_VALUE1 = 0x1234, +}; +int h17 = __builtin_bswapg(ENUM_VALUE1) == 0x34120000 ? 1 : f(); +int h18 = __builtin_bswapg((_BitInt(8))0x12) == (_BitInt(8))0x12 ? 1 : f(); +int h19 = __builtin_bswapg((_BitInt(16))0x1234) == (_BitInt(16))0x3412 ? 1 : f(); +int h20 = __builtin_bswapg((_BitInt(32))0x00001234) == (_BitInt(32))0x34120000 ? 1 : f(); +int h21 = __builtin_bswapg((_BitInt(64))0x0000000000001234) == (_BitInt(64))0x3412000000000000 ? 1 : f(); +int h22 = __builtin_bswapg(~(_BitInt(128))0) == (~(_BitInt(128))0) ? 1 : f(); +int h23 = __builtin_bswapg((_BitInt(24))0x1234) == (_BitInt(24))0x3412 ? 1 : f(); +// expected-error@-1 {{_BitInt type '_BitInt(24)' (24 bits) must be a multiple of 16 bits for byte swapping}} extern long int bi0; extern __typeof__(__builtin_expect(0, 0)) bi0; diff --git a/clang/test/Sema/constant-builtins.c b/clang/test/Sema/constant-builtins.c index 964ab59..6c13fe9 100644 --- a/clang/test/Sema/constant-builtins.c +++ b/clang/test/Sema/constant-builtins.c @@ -25,7 +25,10 @@ int h0 = __builtin_types_compatible_p(int,float); int h3 = __builtin_bswap16(0x1234) == 0x3412 ? 1 : f(); int h4 = __builtin_bswap32(0x1234) == 0x34120000 ? 1 : f(); int h5 = __builtin_bswap64(0x1234) == 0x3412000000000000 ? 1 : f(); - +int h6 = __builtin_bswapg((char)0x12) == (char)0x12 ? 1 : f(); +int h7 = __builtin_bswapg((short)(0x1234)) == (short)(0x3412) ? 1 : f(); +int h8 = __builtin_bswapg(0x00001234) == 0x34120000 ? 1 : f(); +int h9 = __builtin_bswapg(0x0000000000001234ULL) == 0x3412000000000000 ? 1 : f(); short somefunc(void); short t = __builtin_constant_p(5353) ? 42 : somefunc(); diff --git a/clang/test/Sema/format-attr-missing-gnu.c b/clang/test/Sema/format-attr-missing-gnu.c new file mode 100644 index 0000000..196b45c --- /dev/null +++ b/clang/test/Sema/format-attr-missing-gnu.c @@ -0,0 +1,55 @@ +// RUN: %clang_cc1 -fsyntax-only -fblocks -verify -std=gnu11 -Wmissing-format-attribute %s +// RUN: %clang_cc1 -fsyntax-only -fblocks -verify -x c++ -std=gnu++98 -Wmissing-format-attribute %s +// RUN: %clang_cc1 -fsyntax-only -fblocks -std=gnu11 -Wmissing-format-attribute -fdiagnostics-parseable-fixits %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -fsyntax-only -fblocks -x c++ -std=gnu++98 -Wmissing-format-attribute -fdiagnostics-parseable-fixits %s 2>&1 | FileCheck %s + +typedef unsigned long size_t; +typedef long ssize_t; +typedef __builtin_va_list va_list; + +__attribute__((format(printf, 1, 2))) +int printf(const char *, ...); + +__attribute__((format(printf, 1, 0))) +int vprintf(const char *, va_list); + +// Test that attribute fixit is specified using the GNU extension format when -std=gnuXY or -std=gnu++XY. + +// CHECK: fix-it:"{{.*}}":{[[@LINE+1]]:1-[[@LINE+1]]:1}:"__attribute__((format(printf, 1, 0))) " +void f1(char *out, va_list args) // #f1 +{ + vprintf(out, args); // expected-warning {{diagnostic behavior may be improved by adding the 'format(printf, 1, 0)' attribute to the declaration of 'f1'}} + // expected-note@#f1 {{'f1' declared here}} +} + +void f2(void) { + void (^b1)(const char *, ...) = ^(const char *fmt, ...) { // #b1 + va_list args; + vprintf(fmt, args); // expected-warning {{diagnostic behavior may be improved by adding the 'format(printf, 1, 2)' attribute to the declaration of block}} + // expected-note@#b1 {{block declared here}} + }; + + void (^b2)(const char *, va_list) = ^(const char *fmt, va_list args) { // #b2 + vprintf(fmt, args); // expected-warning {{diagnostic behavior may be improved by adding the 'format(printf, 1, 0)' attribute to the declaration of block}}. + // expected-note@#b2 {{block declared here}} + }; + + void (^b3)(const char *, int x, float y) = ^(const char *fmt, int x, float y) { // #b3 + printf(fmt, x, y); // expected-warning {{diagnostic behavior may be improved by adding the 'format(printf, 1, 2)' attribute to the declaration of block}}. + // expected-note@#b3 {{block declared here}} + }; + + void __attribute__((__format__(__printf__, 1, 2))) (^b4)(const char *, ...) = + ^(const char *fmt, ...) __attribute__((__format__(__printf__, 1, 2))) { + va_list args; + vprintf(fmt, args); + }; + + void __attribute__((__format__(__printf__, 2, 3))) (^b5)(const char *, const char *, ...) = + ^(const char *not_fmt, const char *fmt, ...) __attribute__((__format__(__printf__, 2, 3))) { // #b5 + va_list args; + vprintf(fmt, args); + vprintf(not_fmt, args); // expected-warning{{diagnostic behavior may be improved by adding the 'format(printf, 1, 3)' attribute to the declaration of block}} + // expected-note@#b5 {{block declared here}} + }; +} diff --git a/clang/test/Sema/format-attr-missing.c b/clang/test/Sema/format-attr-missing.c new file mode 100644 index 0000000..5133e59 --- /dev/null +++ b/clang/test/Sema/format-attr-missing.c @@ -0,0 +1,228 @@ +// RUN: %clang_cc1 -fsyntax-only -verify -std=c23 -Wmissing-format-attribute %s +// RUN: %clang_cc1 -fsyntax-only -std=c23 -Wmissing-format-attribute -fdiagnostics-parseable-fixits %s 2>&1 | FileCheck %s + +typedef unsigned long size_t; +typedef long ssize_t; +typedef __builtin_va_list va_list; + +[[gnu::format(printf, 1, 2)]] +int printf(const char *, ...); + +[[gnu::format(scanf, 1, 2)]] +int scanf(const char *, ...); + +[[gnu::format(printf, 1, 0)]] +int vprintf(const char *, va_list); + +[[gnu::format(scanf, 1, 0)]] +int vscanf(const char *, va_list); + +[[gnu::format(printf, 2, 0)]] +int vsprintf(char *, const char *, va_list); + +struct tm { unsigned i; }; +[[gnu::format(strftime, 3, 0)]] +size_t strftime(char *, size_t, const char *, const struct tm *); + +[[gnu::format(strfmon, 3, 4)]] +ssize_t strfmon(char *, size_t, const char *, ...); + +[[gnu::format_matches(printf, 1, "%d %f \"'")]] +int custom_print(const char *, va_list); + +// CHECK: fix-it:"{{.*}}":{[[@LINE+1]]:1-[[@LINE+1]]:1}:"{{\[\[}}gnu::format(printf, 1, 0)]] " +void f1(const char *fmt, va_list args) // #f1 +{ + vprintf(fmt, args); // expected-warning {{diagnostic behavior may be improved by adding the 'format(printf, 1, 0)' attribute to the declaration of 'f1'}} + // expected-note@#f1 {{'f1' declared here}} +} + +// CHECK: fix-it:"{{.*}}":{[[@LINE+1]]:1-[[@LINE+1]]:1}:"{{\[\[}}gnu::format(scanf, 1, 0)]] " +void f2(const char *fmt, va_list args) // #f2 +{ + vscanf(fmt, args); // expected-warning {{diagnostic behavior may be improved by adding the 'format(scanf, 1, 0)' attribute to the declaration of 'f2'}} + // expected-note@#f2 {{'f2' declared here}} +} + +// CHECK: fix-it:"{{.*}}":{[[@LINE+1]]:1-[[@LINE+1]]:1}:"{{\[\[}}gnu::format(printf, 1, 2)]] " +void f3(const char *fmt, ... /* args */) // #f3 +{ + va_list args; + vprintf(fmt, args); // expected-warning {{diagnostic behavior may be improved by adding the 'format(printf, 1, 2)' attribute to the declaration of 'f3'}} + // expected-note@#f3 {{'f3' declared here}} +} + +// CHECK: fix-it:"{{.*}}":{[[@LINE+1]]:1-[[@LINE+1]]:1}:"{{\[\[}}gnu::format(scanf, 1, 2)]] " +void f4(const char *fmt, ... /* args */) // #f4 +{ + va_list args; + vscanf(fmt, args); // expected-warning {{diagnostic behavior may be improved by adding the 'format(scanf, 1, 2)' attribute to the declaration of 'f4'}} + // expected-note@#f4 {{'f4' declared here}} +} + +// CHECK: fix-it:"{{.*}}":{[[@LINE+2]]:1-[[@LINE+2]]:1}:"{{\[\[}}gnu::format(printf, 2, 3)]] " +[[gnu::format(printf, 1, 3)]] +void f5(char *out, const char *format, ... /* args */) // #f5 +{ + va_list args; + vsprintf(out, format, args); // expected-warning {{diagnostic behavior may be improved by adding the 'format(printf, 2, 3)' attribute to the declaration of 'f5'}} + // expected-note@#f5 {{'f5' declared here}} +} + +// CHECK: fix-it:"{{.*}}":{[[@LINE+2]]:1-[[@LINE+2]]:1}:"{{\[\[}}gnu::format(printf, 2, 3)]] " +[[gnu::format(scanf, 1, 3)]] +void f6(char *out, const char *format, ... /* args */) // #f6 +{ + va_list args; + vsprintf(out, format, args); // expected-warning {{diagnostic behavior may be improved by adding the 'format(printf, 2, 3)' attribute to the declaration of 'f6'}} + // expected-note@#f6 {{'f6' declared here}} +} + +// Ok, out is not passed to print functions. +void f7(char* out, ... /* args */) +{ + va_list args; + + const char *ch = "format"; + vprintf(ch, args); + vprintf("test", args); +} + +// Ok, format string is not passed to format functions. +void f8(va_list args) +{ + const char * const ch = "format"; + vprintf(ch, args); + vprintf("test", args); + + vscanf(ch, args); + vscanf("test", args); + + char out[10]; + + struct tm tm_arg; + tm_arg.i = 0; + strftime(out, sizeof(out), ch, &tm_arg); + strftime(out, sizeof(out), "test", &tm_arg); + + strfmon(out, sizeof(out), ch); + strfmon(out, sizeof(out), "test"); +} + +// CHECK: fix-it:"{{.*}}":{[[@LINE+1]]:1-[[@LINE+1]]:1}:"{{\[\[}}gnu::format_matches(printf, 1, \"%d %f \\\"'\")]] " +void f9(const char *fmt, ...) // #f9 +{ + va_list args; + custom_print(fmt, args); // expected-warning {{diagnostic behavior may be improved by adding the 'format_matches(printf, 1, "%d %f \"'")' attribute to the declaration of 'f9'}} + // expected-note@#f9 {{'f9' declared here}} +} + +// CHECK: fix-it:"{{.*}}":{[[@LINE+1]]:1-[[@LINE+1]]:1}:"{{\[\[}}gnu::format(scanf, 1, 2)]] " +void f10(const char *out, ... /* args */) // #f10 +{ + va_list args; + vscanf(out, args); // expected-warning {{diagnostic behavior may be improved by adding the 'format(scanf, 1, 2)' attribute to the declaration of 'f10'}} + // expected-note@#f10 {{'f10' declared here}} + vprintf(out, args); // expected-warning {{passing 'scanf' format string where 'printf' format string is expected}} +} + +// CHECK: fix-it:"{{.*}}":{[[@LINE+1]]:1-[[@LINE+1]]:1}:"{{\[\[}}gnu::format(printf, 1, 2)]] " +void f11(const char out[], ... /* args */) // #f11 +{ + va_list args; + char ch[10] = "format"; + vprintf(ch, args); + vsprintf(ch, out, args); // expected-warning {{diagnostic behavior may be improved by adding the 'format(printf, 1, 2)' attribute to the declaration of 'f11'}} + // expected-note@#f11 {{'f11' declared here}} +} + +// CHECK: fix-it:"{{.*}}":{[[@LINE+1]]:1-[[@LINE+1]]:1}:"{{\[\[}}gnu::format(printf, 1, 0)]] " +void f12(char* out) // #f12 +{ + va_list args; + const char *ch = "format"; + vsprintf(out, ch, args); + vprintf(out, args); // expected-warning {{diagnostic behavior may be improved by adding the 'format(printf, 1, 0)' attribute to the declaration of 'f12'}} + // expected-note@#f12 {{'f12' declared here}} +} + +// CHECK: fix-it:"{{.*}}":{[[@LINE+1]]:1-[[@LINE+1]]:1}:"{{\[\[}}gnu::format(printf, 1, 0)]] " +void f13(char *out, va_list args) // #f13 +{ + vprintf(out, args); // expected-warning {{diagnostic behavior may be improved by adding the 'format(printf, 1, 0)' attribute to the declaration of 'f13'}} + // expected-note@#f13 {{'f13' declared here}} + vscanf(out, args); // expected-warning {{passing 'printf' format string where 'scanf' format string is expected}} +} + +// CHECK: fix-it:"{{.*}}":{[[@LINE+1]]:1-[[@LINE+1]]:1}:"{{\[\[}}gnu::format(scanf, 1, 2)]] " +void f14(char *out, ... /* args */) // #f14 +{ + va_list args; + vscanf(out, args); // expected-warning {{diagnostic behavior may be improved by adding the 'format(scanf, 1, 2)' attribute to the declaration of 'f14'}} + // expected-note@#f14 {{'f14' declared here}} + vscanf(out, args); +} + +// CHECK: fix-it:"{{.*}}":{[[@LINE+2]]:1-[[@LINE+2]]:1}:"{{\[\[}}gnu::format(printf, 1, 3)]] " +// CHECK: fix-it:"{{.*}}":{[[@LINE+1]]:1-[[@LINE+1]]:1}:"{{\[\[}}gnu::format(printf, 2, 3)]] " +void f15(char *ch, const char *out, ... /* args */) // #f15 +{ + va_list args; + vprintf(ch, args); // expected-warning {{diagnostic behavior may be improved by adding the 'format(printf, 1, 3)' attribute to the declaration of 'f15'}} + // expected-note@#f15 {{'f15' declared here}} + vprintf(out, args); // expected-warning {{diagnostic behavior may be improved by adding the 'format(printf, 2, 3)' attribute to the declaration of 'f15'}} + // expected-note@#f15 {{'f15' declared here}} +} + +// CHECK: fix-it:"{{.*}}":{[[@LINE+1]]:1-[[@LINE+1]]:1}:"{{\[\[}}gnu::format(printf, 1, 2)]] " +void f16(const char *a, ...) // #f16 +{ + va_list args; + const char *const b = a; + vprintf(b, args); // expected-warning {{diagnostic behavior may be improved by adding the 'format(printf, 1, 2)' attribute to the declaration of 'f16'}} + // expected-note@#f16 {{'f16' declared here}} +} + +// CHECK: fix-it:"{{.*}}":{[[@LINE+1]]:1-[[@LINE+1]]:1}:"{{\[\[}}gnu::format(printf, 1, 2)]] " +void f17(char *fmt, unsigned x, unsigned y, unsigned z) // #f17 +{ + printf(fmt, x, y, z); // expected-warning {{diagnostic behavior may be improved by adding the 'format(printf, 1, 2)' attribute to the declaration of 'f17'}} + // expected-note@#f17 {{'f17' declared here}} +} + +void f18(char *fmt, unsigned x, unsigned y, unsigned z) // #f18 +{ + // Arguments are not passed in the same order. + printf(fmt, x, z, y); +} + +void f19(char *out, ... /* args */) +{ + printf(out, 1); // No warning, arguments are not passed to printf. +} + +// CHECK: fix-it:"{{.*}}":{[[@LINE+1]]:1-[[@LINE+1]]:1}:"{{\[\[}}gnu::format(strftime, 3, 0)]] " +void f20(char *out, const size_t len, const char *format) // #f20 +{ + struct tm tm_arg; + tm_arg.i = 0; + strftime(out, len, format, &tm_arg); // expected-warning {{diagnostic behavior may be improved by adding the 'format(strftime, 3, 0)' attribute to the declaration of 'f20'}} + // expected-note@#f20 {{'f20' declared here}} +} + +// CHECK: fix-it:"{{.*}}":{[[@LINE+1]]:1-[[@LINE+1]]:1}:"{{\[\[}}gnu::format(strfmon, 3, 4)]] " +void f21(char *out, const size_t len, const char *format, int x, int y) // #f21 +{ + strfmon(out, len, format, x, y); // expected-warning {{diagnostic behavior may be improved by adding the 'format(strfmon, 3, 4)' attribute to the declaration of 'f21'}} + // expected-note@#f21 {{'f21' declared here}} +} + +// CHECK: fix-it:"{{.*}}":{[[@LINE+1]]:1-[[@LINE+1]]:1}:"{{\[\[}}gnu::format(printf, 1, 2)]] " +void f22(const char *fmt, ... /* args */); // #f22 + +void f22(const char *fmt, ... /* args */) +{ + va_list args; + vprintf(fmt, args); // expected-warning {{diagnostic behavior may be improved by adding the 'format(printf, 1, 2)' attribute to the declaration of 'f22'}} + // expected-note@#f22 {{'f22' declared here}} +} diff --git a/clang/test/Sema/format-attr-missing.cpp b/clang/test/Sema/format-attr-missing.cpp new file mode 100644 index 0000000..06b5ad1a --- /dev/null +++ b/clang/test/Sema/format-attr-missing.cpp @@ -0,0 +1,68 @@ +// RUN: %clang_cc1 -fsyntax-only -verify -std=c++23 -Wmissing-format-attribute %s +// RUN: %clang_cc1 -fsyntax-only -Wmissing-format-attribute -fdiagnostics-parseable-fixits -std=c++23 %s 2>&1 | FileCheck %s + +typedef __SIZE_TYPE__ size_t; +typedef __builtin_va_list va_list; + +[[gnu::format(printf, 1, 2)]] +int printf(const char *, ...); + +[[gnu::format(scanf, 1, 2)]] +int scanf(const char *, ...); + +[[gnu::format(printf, 1, 0)]] +int vprintf(const char *, va_list); + +[[gnu::format(scanf, 1, 0)]] +int vscanf(const char *, va_list); + +[[gnu::format(printf, 2, 0)]] +int vsprintf(char *, const char *, va_list); + +[[gnu::format(printf, 3, 0)]] +int vsnprintf(char *, size_t, const char *, va_list); + +struct S1 +{ + // CHECK: fix-it:"{{.*}}":{[[@LINE+1]]:3-[[@LINE+1]]:3}:"{{\[\[}}gnu::format(scanf, 2, 3)]] " + void fn1(const char *out, ... /* args */) // #S1_fn1 + { + va_list args; + vscanf(out, args); // expected-warning {{diagnostic behavior may be improved by adding the 'format(scanf, 2, 3)' attribute to the declaration of 'fn1'}} + // expected-note@#S1_fn1 {{'fn1' declared here}} + } + + [[gnu::format(printf, 2, 0)]] + void print(const char *out, va_list args); + + // CHECK: fix-it:"{{.*}}":{[[@LINE+1]]:3-[[@LINE+1]]:3}:"{{\[\[}}gnu::format(printf, 2, 3)]] " + void fn2(const char *out, ... /* args */) // #S1_fn2 + { + va_list args; + print(out, args); // expected-warning {{diagnostic behavior may be improved by adding the 'format(printf, 2, 3)' attribute to the declaration of 'fn2'}} + // expected-note@#S1_fn2 {{'fn2' declared here}} + } + + // CHECK: fix-it:"{{.*}}":{[[@LINE+1]]:3-[[@LINE+1]]:3}:"{{\[\[}}gnu::format(printf, 2, 0)]] " + void fn3(const char *out, va_list args) // #S1_fn3 + { + print(out, args); // expected-warning {{diagnostic behavior may be improved by adding the 'format(printf, 2, 0)' attribute to the declaration of 'fn3'}} + // expected-note@#S1_fn3 {{'fn3' declared here}} + } + + // CHECK: fix-it:"{{.*}}":{[[@LINE+1]]:3-[[@LINE+1]]:3}:"{{\[\[}}gnu::format(printf, 2, 3)]] " + void fn4(this S1& self, const char *out, ... /* args */) // #S1_fn4 + { + va_list args; + self.print(out, args); // expected-warning {{diagnostic behavior may be improved by adding the 'format(printf, 2, 3)' attribute to the declaration of 'fn4'}} + // expected-note@#S1_fn4 {{'fn4' declared here}} + } + + // CHECK: fix-it:"{{.*}}":{[[@LINE+1]]:3-[[@LINE+1]]:3}:"{{\[\[}}gnu::format(printf, 2, 0)]] " + void fn5(this S1& self, const char *out, va_list args) // #S1_fn5 + { + self.print(out, args); // expected-warning {{diagnostic behavior may be improved by adding the 'format(printf, 2, 0)' attribute to the declaration of 'fn5'}} + // expected-note@#S1_fn5 {{'fn5' declared here}} + } +}; + diff --git a/clang/test/Sema/format-attr-missing.m b/clang/test/Sema/format-attr-missing.m new file mode 100644 index 0000000..159b4ba --- /dev/null +++ b/clang/test/Sema/format-attr-missing.m @@ -0,0 +1,68 @@ +// RUN: %clang_cc1 -fsyntax-only -verify -Wmissing-format-attribute %s +// RUN: %clang_cc1 -fsyntax-only -Wmissing-format-attribute -fdiagnostics-parseable-fixits %s 2>&1 | FileCheck %s + +#include <stdarg.h> + +@interface PrintCallee +-(void)printf:(const char *)fmt, ... __attribute__((format(printf, 1, 2))); +-(void)vprintf:(const char *)fmt list:(va_list)ap __attribute__((format(printf, 1, 0))); +@end + +// CHECK: fix-it:"{{.*}}":{[[@LINE+1]]:1-[[@LINE+1]]:1}:"__attribute__((format(printf, 2, 3))) " +void f1(PrintCallee *p, const char *fmt, int x) // #f1 +{ + [p printf:fmt, x]; // expected-warning {{diagnostic behavior may be improved by adding the 'format(printf, 2, 3)' attribute to the declaration of 'f1'}} + // expected-note@#f1 {{'f1' declared here}} +} + +// CHECK: fix-it:"{{.*}}":{[[@LINE+1]]:1-[[@LINE+1]]:1}:"__attribute__((format(printf, 2, 3))) " +void f2(PrintCallee *p, const char *fmt, ...) // #f2 +{ + va_list ap; + [p vprintf:fmt list:ap]; // expected-warning {{diagnostic behavior may be improved by adding the 'format(printf, 2, 3)' attribute to the declaration of 'f2'}} + // expected-note@#f2 {{'f2' declared here}} +} + +// CHECK: fix-it:"{{.*}}":{[[@LINE+1]]:1-[[@LINE+1]]:1}:"__attribute__((format(printf, 2, 0))) " +void f3(PrintCallee *p, const char *fmt, va_list ap) // #f3 +{ + [p vprintf:fmt list:ap]; // expected-warning {{diagnostic behavior may be improved by adding the 'format(printf, 2, 0)' attribute to the declaration of 'f3'}} + // expected-note@#f3 {{'f3' declared here}} +} + +__attribute__((format(printf, 1, 2))) +int printf(const char *, ...); +__attribute__((format(printf, 1, 0))) +int vprintf(const char *, va_list ap); + +__attribute__((objc_root_class)) +@interface PrintCaller +// CHECK: fix-it:"{{.*}}":{[[@LINE+1]]:33-[[@LINE+1]]:33}:" __attribute__((format(printf, 1, 2)))" +-(void)f4:(const char *)fmt, ...; // #f4 + +// CHECK: fix-it:"{{.*}}":{[[@LINE+1]]:45-[[@LINE+1]]:45}:" __attribute__((format(printf, 1, 0)))" +-(void)f5:(const char *)fmt list:(va_list)ap; // #f5 + +// CHECK: fix-it:"{{.*}}":{[[@LINE+1]]:37-[[@LINE+1]]:37}:" __attribute__((format(printf, 1, 2)))" +-(void)f6:(const char *)fmt x:(int)x; // #f6 +@end + +@implementation PrintCaller +-(void)f4:(const char *)fmt, ... { + va_list ap; + va_start(ap, fmt); + vprintf(fmt, ap); // expected-warning {{diagnostic behavior may be improved by adding the 'format(printf, 1, 2)' attribute to the declaration of 'f4:'}} + // expected-note@#f4 {{'f4:' declared here}} + va_end(ap); +} + +-(void)f5:(const char *)fmt list:(va_list)ap { + vprintf(fmt, ap); // expected-warning {{diagnostic behavior may be improved by adding the 'format(printf, 1, 0)' attribute to the declaration of 'f5:list:'}} + // expected-note@#f5 {{'f5:list:' declared here}} +} + +-(void)f6:(const char *)fmt x:(int)x { + printf(fmt, x); // expected-warning {{diagnostic behavior may be improved by adding the 'format(printf, 1, 2)' attribute to the declaration of 'f6:x:'}} + // expected-note@#f6 {{'f6:x:' declared here}} +} +@end diff --git a/clang/test/Sema/format-strings-nonnull.c b/clang/test/Sema/format-strings-nonnull.c new file mode 100644 index 0000000..b9eeb59 --- /dev/null +++ b/clang/test/Sema/format-strings-nonnull.c @@ -0,0 +1,79 @@ +// RUN: %clang_cc1 -fsyntax-only --std=c23 -verify -Wnonnull -Wno-format-security %s + +#define NULL (void*)0 + +typedef struct _FILE FILE; +typedef __SIZE_TYPE__ size_t; +typedef __builtin_va_list va_list; +int printf(char const* restrict, ...); +int __builtin_printf(char const* restrict, ...); +int fprintf(FILE* restrict, char const* restrict, ...); +int snprintf(char* restrict, size_t, char const* restrict, ...); +int sprintf(char* restrict, char const* restrict, ...); +int vprintf(char const* restrict, __builtin_va_list); +int vfprintf(FILE* restrict, char const* restrict, __builtin_va_list); +int vsnprintf(char* restrict, size_t, char const* restrict, __builtin_va_list); +int vsprintf(char* restrict, char const* restrict, __builtin_va_list); + +int scanf(char const* restrict, ...); +int fscanf(FILE* restrict, char const* restrict, ...); +int sscanf(char const* restrict, char const* restrict, ...); +int vscanf(char const* restrict, __builtin_va_list); +int vfscanf(FILE* restrict, char const* restrict, __builtin_va_list); +int vsscanf(char const* restrict, char const* restrict, __builtin_va_list); + + +void check_format_string(FILE *fp, va_list ap) { + char buf[256]; + int num; + char* const fmt = NULL; + + printf(fmt); + // expected-warning@-1{{null passed to a callee that requires a non-null argument}} + + __builtin_printf(NULL, "xxd"); + // expected-warning@-1{{null passed to a callee that requires a non-null argument}} + + fprintf(fp, NULL, 25); + // expected-warning@-1{{null passed to a callee that requires a non-null argument}} + + sprintf(NULL, NULL, 42); + // expected-warning@-1{{null passed to a callee that requires a non-null argument}} + // expected-warning@-2{{null passed to a callee that requires a non-null argument}} + + snprintf(buf, 10, 0, 42); + // expected-warning@-1{{null passed to a callee that requires a non-null argument}} + + vprintf(fmt, ap); + // expected-warning@-1{{null passed to a callee that requires a non-null argument}} + + vfprintf(fp, 0, ap); + // expected-warning@-1{{null passed to a callee that requires a non-null argument}} + + vsprintf(buf, nullptr, ap); + // expected-warning@-1{{null passed to a callee that requires a non-null argument}} + + vsnprintf(buf, 10, fmt, ap); + // expected-warning@-1{{null passed to a callee that requires a non-null argument}} + + scanf(NULL); + // expected-warning@-1{{null passed to a callee that requires a non-null argument}} + + fscanf(nullptr, nullptr); + // expected-warning@-1{{null passed to a callee that requires a non-null argument}} + // expected-warning@-2{{null passed to a callee that requires a non-null argument}} + + sscanf(NULL, "%d %s", &num, buf); + // expected-warning@-1{{null passed to a callee that requires a non-null argument}} + sscanf(buf, fmt); + // expected-warning@-1{{null passed to a callee that requires a non-null argument}} + + vscanf(NULL, ap); + // expected-warning@-1{{null passed to a callee that requires a non-null argument}} + + vfscanf(fp, fmt, ap); + // expected-warning@-1{{null passed to a callee that requires a non-null argument}} + + vsscanf(buf, NULL, ap); + // expected-warning@-1{{null passed to a callee that requires a non-null argument}} +} diff --git a/clang/test/Sema/format-strings-nonnull.cpp b/clang/test/Sema/format-strings-nonnull.cpp new file mode 100644 index 0000000..6c25679 --- /dev/null +++ b/clang/test/Sema/format-strings-nonnull.cpp @@ -0,0 +1,49 @@ +// RUN: %clang_cc1 -fsyntax-only -verify -Wnonnull %s + +#ifdef __cplusplus +# define EXTERN_C extern "C" +#else +# define EXTERN_C extern +#endif + +typedef struct _FILE FILE; +typedef __SIZE_TYPE__ size_t; +typedef __builtin_va_list va_list; + +EXTERN_C int printf(const char *, ...); +EXTERN_C int fprintf(FILE *, const char *restrict, ...); +EXTERN_C int sprintf(char* restrict, char const* res, ...); +EXTERN_C int vfprintf(FILE* restrict, char const* res, __builtin_va_list); + +EXTERN_C int scanf(char const *restrict, ...); +EXTERN_C int fscanf(FILE* restrict, char const* res, ...); +EXTERN_C int sscanf(char const* restrict, char const* res, ...); + +void test(FILE *fp, va_list ap) { + char buf[256]; + int num; + + __builtin_printf(__null, "x"); + // expected-warning@-1 {{null passed to a callee that requires a non-null argument}} + + printf(__null, "xxd"); + // expected-warning@-1 {{null passed to a callee that requires a non-null argument}} + + fprintf(fp, __null, 42); + // expected-warning@-1 {{null passed to a callee that requires a non-null argument}} + + sprintf(buf, __null); + // expected-warning@-1 {{null passed to a callee that requires a non-null argument}} + + scanf(__null); + // expected-warning@-1 {{null passed to a callee that requires a non-null argument}} + + fscanf(fp, __null); + // expected-warning@-1 {{null passed to a callee that requires a non-null argument}} + + vfprintf(__null, "xxd", ap); + // expected-warning@-1 {{null passed to a callee that requires a non-null argument}} + + sscanf(__null, "%d", &num); + // expected-warning@-1 {{null passed to a callee that requires a non-null argument}} +} diff --git a/clang/test/Sema/format-strings-scanf.c b/clang/test/Sema/format-strings-scanf.c index 22c1cce..941e3f7 100644 --- a/clang/test/Sema/format-strings-scanf.c +++ b/clang/test/Sema/format-strings-scanf.c @@ -36,8 +36,9 @@ int vscanf(const char * restrict, va_list); int vfscanf(FILE * restrict, const char * restrict, va_list); int vsscanf(const char * restrict, const char * restrict, va_list); -void test(const char *s, int *i) { - scanf(s, i); // expected-warning{{format string is not a string literal}} +void test(const char *s, int *i) { // #test + scanf(s, i); // expected-warning{{diagnostic behavior may be improved by adding the 'format(scanf, 1, 2)' attribute to the declaration of 'test'}} + // expected-note@#test {{'test' declared here}} scanf("%0d", i); // expected-warning{{zero field width in scanf format string is unused}} scanf("%00d", i); // expected-warning{{zero field width in scanf format string is unused}} scanf("%d%[asdfasdfd", i, s); // expected-warning{{no closing ']' for '%[' in scanf format string}} diff --git a/clang/test/Sema/format-strings.c b/clang/test/Sema/format-strings.c index 103dd8a..5280549 100644 --- a/clang/test/Sema/format-strings.c +++ b/clang/test/Sema/format-strings.c @@ -24,37 +24,115 @@ int vscanf(const char *restrict format, va_list arg); char * global_fmt; -void check_string_literal( FILE* fp, const char* s, char *buf, ... ) { - - char * b; +void check_string_literal1( const char* s, ... ) { va_list ap; - va_start(ap,buf); - + va_start(ap,s); printf(s); // expected-warning {{format string is not a string literal}} // expected-note@-1{{treat the string as an argument to avoid this}} - vprintf(s,ap); // expected-warning {{format string is not a string literal}} +} + +void check_string_literal2( const char* s, ... ) { // #check_string_literal2 + va_list ap; + va_start(ap,s); + vprintf(s,ap); // expected-warning {{diagnostic behavior may be improved by adding the 'format(printf, 1, 2)' attribute to the declaration of 'check_string_literal2'}} + // expected-note@#check_string_literal2 {{'check_string_literal2' declared here}} +} + +void check_string_literal3( FILE* fp, const char* s, ... ) { + va_list ap; + va_start(ap,s); fprintf(fp,s); // expected-warning {{format string is not a string literal}} // expected-note@-1{{treat the string as an argument to avoid this}} - vfprintf(fp,s,ap); // expected-warning {{format string is not a string literal}} +} + +void check_string_literal4( FILE* fp, const char* s, ... ) { // #check_string_literal4 + va_list ap; + va_start(ap,s); + vfprintf(fp,s,ap); // expected-warning {{diagnostic behavior may be improved by adding the 'format(printf, 2, 3)' attribute to the declaration of 'check_string_literal4'}} + // expected-note@#check_string_literal4 {{'check_string_literal4' declared here}} +} + +void check_string_literal5( const char* s, ... ) { + char * b; + va_list ap; + va_start(ap,s); asprintf(&b,s); // expected-warning {{format string is not a string lit}} // expected-note@-1{{treat the string as an argument to avoid this}} - vasprintf(&b,s,ap); // expected-warning {{format string is not a string literal}} +} + +void check_string_literal6( const char* s, ... ) { // #check_string_literal6 + char * b; + va_list ap; + va_start(ap,s); + vasprintf(&b,s,ap); // expected-warning {{diagnostic behavior may be improved by adding the 'format(printf, 1, 2)' attribute to the declaration of 'check_string_literal6'}} + // expected-note@#check_string_literal6 {{'check_string_literal6' declared here}} +} + +void check_string_literal7( const char* s, char *buf ) { sprintf(buf,s); // expected-warning {{format string is not a string literal}} // expected-note@-1{{treat the string as an argument to avoid this}} +} + +void check_string_literal8( const char* s, char *buf ) { snprintf(buf,2,s); // expected-warning {{format string is not a string lit}} // expected-note@-1{{treat the string as an argument to avoid this}} +} + +void check_string_literal9( const char* s, char *buf, ... ) { + va_list ap; + va_start(ap,buf); __builtin___sprintf_chk(buf,0,-1,s); // expected-warning {{format string is not a string literal}} // expected-note@-1{{treat the string as an argument to avoid this}} +} + +void check_string_literal10( const char* s, char *buf, ... ) { + va_list ap; + va_start(ap,buf); __builtin___snprintf_chk(buf,2,0,-1,s); // expected-warning {{format string is not a string lit}} // expected-note@-1{{treat the string as an argument to avoid this}} - vsprintf(buf,s,ap); // expected-warning {{format string is not a string lit}} - vsnprintf(buf,2,s,ap); // expected-warning {{format string is not a string lit}} +} + +void check_string_literal11( const char* s, char *buf, ... ) { // #check_string_literal11 + va_list ap; + va_start(ap,buf); + vsprintf(buf,s,ap); // expected-warning {{diagnostic behavior may be improved by adding the 'format(printf, 1, 3)' attribute to the declaration of 'check_string_literal11'}} + // expected-note@#check_string_literal11 {{'check_string_literal11' declared here}} +} + +void check_string_literal12( const char* s, char *buf, ... ) { // #check_string_literal12 + va_list ap; + va_start(ap,buf); + vsnprintf(buf,2,s,ap); // expected-warning {{diagnostic behavior may be improved by adding the 'format(printf, 1, 3)' attribute to the declaration of 'check_string_literal12'}} + // expected-note@#check_string_literal12 {{'check_string_literal12' declared here}} +} + +void check_string_literal13( char *buf, ... ) { + va_list ap; + va_start(ap,buf); vsnprintf(buf,2,global_fmt,ap); // expected-warning {{format string is not a string literal}} - __builtin___vsnprintf_chk(buf,2,0,-1,s,ap); // expected-warning {{format string is not a string lit}} +} + +void check_string_literal14( FILE* fp, const char* s, char *buf, ... ) { // #check_string_literal14 + va_list ap; + va_start(ap,buf); + __builtin___vsnprintf_chk(buf,2,0,-1,s,ap); // expected-warning {{diagnostic behavior may be improved by adding the 'format(printf, 2, 4)' attribute to the declaration of 'check_string_literal14'}} + // expected-note@#check_string_literal14 {{'check_string_literal14' declared here}} +} + +void check_string_literal15( FILE* fp, const char* s, char *buf, ... ) { + va_list ap; + va_start(ap,buf); __builtin___vsnprintf_chk(buf,2,0,-1,global_fmt,ap); // expected-warning {{format string is not a string literal}} +} - vscanf(s, ap); // expected-warning {{format string is not a string literal}} +void check_string_literal16(const char* s, ... ) { // #check_string_literal16 + va_list ap; + va_start(ap,s); + vscanf(s, ap); // expected-warning {{diagnostic behavior may be improved by adding the 'format(scanf, 1, 2)' attribute to the declaration of 'check_string_literal16'}} + // expected-note@#check_string_literal16 {{'check_string_literal16' declared here}} +} +void check_string_literal17() { const char *const fmt = "%d"; // FIXME -- defined here printf(fmt, 1, 2); // expected-warning{{data argument not used}} @@ -74,7 +152,7 @@ def" // warn only if the format string argument is a parameter that is not itself // declared as a format string with compatible format. __attribute__((__format__ (__printf__, 2, 4))) -void check_string_literal2( FILE* fp, const char* s, char *buf, ... ) { +void check_string_literal18( FILE* fp, const char* s, char *buf, ... ) { char * b; va_list ap; va_start(ap,buf); @@ -480,11 +558,9 @@ void pr7981(wint_t c, wchar_t c2) { #endif } -// -Wformat-security says NULL is not a string literal void rdar8269537(void) { - // This is likely to crash in most cases, but -Wformat-nonliteral technically - // doesn't warn in this case. - printf(0); // no-warning + printf(0); + // expected-warning@-1{{null passed to a callee that requires a non-null argument}} } // Handle functions with multiple format attributes. @@ -840,12 +916,13 @@ void test_block(void) { void __attribute__((__format__(__printf__, 2, 3))) (^printf_arg2)( const char *, const char *, ...) = - ^(const char *not_fmt, const char *fmt, ...) + ^(const char *not_fmt, const char *fmt, ...) // #printf_arg2 __attribute__((__format__(__printf__, 2, 3))) { va_list ap; va_start(ap, fmt); vprintf(fmt, ap); - vprintf(not_fmt, ap); // expected-warning{{format string is not a string literal}} + vprintf(not_fmt, ap); // expected-warning{{diagnostic behavior may be improved by adding the 'format(printf, 1, 3)' attribute to the declaration of block}} + // expected-note@#printf_arg2 {{block declared here}} va_end(ap); }; diff --git a/clang/test/Sema/scoped-atomic-ops.c b/clang/test/Sema/scoped-atomic-ops.c index 33044aa..23512a9 100644 --- a/clang/test/Sema/scoped-atomic-ops.c +++ b/clang/test/Sema/scoped-atomic-ops.c @@ -31,7 +31,7 @@ void fi2b(int *i) { __scoped_atomic_store_n(i, 1, __ATOMIC_RELAXED, __MEMORY_SCOPE_SYSTEM); } -void fi3a(int *a, int *b, int *c, int *d, int *e, int *f, int *g, int *h) { +void fi3a(int *a, int *b, int *c, int *d, int *e, int *f, int *g, int *h, unsigned *i, unsigned *j) { *a = __scoped_atomic_fetch_add(a, 1, __ATOMIC_RELAXED, __MEMORY_SCOPE_SYSTEM); *b = __scoped_atomic_fetch_sub(b, 1, __ATOMIC_RELAXED, __MEMORY_SCOPE_SYSTEM); *c = __scoped_atomic_fetch_and(c, 1, __ATOMIC_RELAXED, __MEMORY_SCOPE_SYSTEM); @@ -40,9 +40,11 @@ void fi3a(int *a, int *b, int *c, int *d, int *e, int *f, int *g, int *h) { *f = __scoped_atomic_fetch_nand(f, 1, __ATOMIC_RELAXED, __MEMORY_SCOPE_SYSTEM); *g = __scoped_atomic_fetch_min(g, 1, __ATOMIC_RELAXED, __MEMORY_SCOPE_SYSTEM); *h = __scoped_atomic_fetch_max(h, 1, __ATOMIC_RELAXED, __MEMORY_SCOPE_SYSTEM); + *i = __scoped_atomic_uinc_wrap(i, 1u, __ATOMIC_RELAXED, __MEMORY_SCOPE_SYSTEM); + *j = __scoped_atomic_udec_wrap(j, 1u, __ATOMIC_RELAXED, __MEMORY_SCOPE_SYSTEM); } -void fi3b(int *a, int *b, int *c, int *d, int *e, int *f, int *g, int *h) { +void fi3b(int *a, int *b, int *c, int *d, int *e, int *f, int *g, int *h, unsigned *i, unsigned *j) { *a = __scoped_atomic_fetch_add(1, 1, __ATOMIC_RELAXED, __MEMORY_SCOPE_SYSTEM); // expected-error {{address argument to atomic builtin must be a pointer ('int' invalid)}} *b = __scoped_atomic_fetch_sub(1, 1, __ATOMIC_RELAXED, __MEMORY_SCOPE_SYSTEM); // expected-error {{address argument to atomic builtin must be a pointer ('int' invalid)}} *c = __scoped_atomic_fetch_and(1, 1, __ATOMIC_RELAXED, __MEMORY_SCOPE_SYSTEM); // expected-error {{address argument to atomic builtin must be a pointer ('int' invalid)}} @@ -51,9 +53,11 @@ void fi3b(int *a, int *b, int *c, int *d, int *e, int *f, int *g, int *h) { *f = __scoped_atomic_fetch_nand(1, 1, __ATOMIC_RELAXED, __MEMORY_SCOPE_SYSTEM); // expected-error {{address argument to atomic builtin must be a pointer ('int' invalid)}} *g = __scoped_atomic_fetch_min(1, 1, __ATOMIC_RELAXED, __MEMORY_SCOPE_SYSTEM); // expected-error {{address argument to atomic builtin must be a pointer ('int' invalid)}} *h = __scoped_atomic_fetch_max(1, 1, __ATOMIC_RELAXED, __MEMORY_SCOPE_SYSTEM); // expected-error {{address argument to atomic builtin must be a pointer ('int' invalid)}} + *i = __scoped_atomic_uinc_wrap(1, 1u, __ATOMIC_RELAXED, __MEMORY_SCOPE_SYSTEM); // expected-error {{address argument to atomic builtin must be a pointer ('int' invalid)}} + *g = __scoped_atomic_udec_wrap(1, 1u, __ATOMIC_RELAXED, __MEMORY_SCOPE_SYSTEM); // expected-error {{address argument to atomic builtin must be a pointer ('int' invalid)}} } -void fi3c(int *a, int *b, int *c, int *d, int *e, int *f, int *g, int *h) { +void fi3c(int *a, int *b, int *c, int *d, int *e, int *f, int *g, int *h, unsigned *i, unsigned *j) { *a = __scoped_atomic_fetch_add(a, 1, __ATOMIC_RELAXED); // expected-error {{too few arguments to function call, expected 4, have 3}} *b = __scoped_atomic_fetch_sub(b, 1, __ATOMIC_RELAXED); // expected-error {{too few arguments to function call, expected 4, have 3}} *c = __scoped_atomic_fetch_and(c, 1, __ATOMIC_RELAXED); // expected-error {{too few arguments to function call, expected 4, have 3}} @@ -62,9 +66,11 @@ void fi3c(int *a, int *b, int *c, int *d, int *e, int *f, int *g, int *h) { *f = __scoped_atomic_fetch_nand(f, 1, __ATOMIC_RELAXED); // expected-error {{too few arguments to function call, expected 4, have 3}} *g = __scoped_atomic_fetch_min(g, 1, __ATOMIC_RELAXED); // expected-error {{too few arguments to function call, expected 4, have 3}} *h = __scoped_atomic_fetch_max(h, 1, __ATOMIC_RELAXED); // expected-error {{too few arguments to function call, expected 4, have 3}} + *i = __scoped_atomic_uinc_wrap(i, 1u, __ATOMIC_RELAXED); // expected-error {{too few arguments to function call, expected 4, have 3}} + *j = __scoped_atomic_udec_wrap(j, 1u, __ATOMIC_RELAXED); // expected-error {{too few arguments to function call, expected 4, have 3}} } -void fi3d(int *a, int *b, int *c, int *d, int *e, int *f, int *g, int *h) { +void fi3d(int *a, int *b, int *c, int *d, int *e, int *f, int *g, int *h, unsigned *i, unsigned *j) { *a = __scoped_atomic_fetch_add(a, 1, __ATOMIC_RELAXED, 42); // expected-error {{synchronization scope argument to atomic operation is invalid}} *b = __scoped_atomic_fetch_sub(b, 1, __ATOMIC_RELAXED, 42); // expected-error {{synchronization scope argument to atomic operation is invalid}} *c = __scoped_atomic_fetch_and(c, 1, __ATOMIC_RELAXED, 42); // expected-error {{synchronization scope argument to atomic operation is invalid}} @@ -73,6 +79,17 @@ void fi3d(int *a, int *b, int *c, int *d, int *e, int *f, int *g, int *h) { *f = __scoped_atomic_fetch_nand(f, 1, __ATOMIC_RELAXED, 42); // expected-error {{synchronization scope argument to atomic operation is invalid}} *g = __scoped_atomic_fetch_min(g, 1, __ATOMIC_RELAXED, 42); // expected-error {{synchronization scope argument to atomic operation is invalid}} *h = __scoped_atomic_fetch_max(h, 1, __ATOMIC_RELAXED, 42); // expected-error {{synchronization scope argument to atomic operation is invalid}} + *i = __scoped_atomic_uinc_wrap(i, 1u, __ATOMIC_RELAXED, 42); // expected-error {{synchronization scope argument to atomic operation is invalid}} + *j = __scoped_atomic_udec_wrap(j, 1u, __ATOMIC_RELAXED, 42); // expected-error {{synchronization scope argument to atomic operation is invalid}} +} + +void fi3e(float *a, float *b, float *c, float *d, float *e, float *f) { + *a = __scoped_atomic_fetch_and(a, 1, __ATOMIC_RELAXED, 42); // expected-error {{address argument to atomic operation must be a pointer to integer ('float *' invalid)}} + *b = __scoped_atomic_fetch_or(b, 1, __ATOMIC_RELAXED, 42); // expected-error {{address argument to atomic operation must be a pointer to integer ('float *' invalid)}} + *c = __scoped_atomic_fetch_xor(c, 1, __ATOMIC_RELAXED, 42); // expected-error {{address argument to atomic operation must be a pointer to integer ('float *' invalid)}} + *d = __scoped_atomic_fetch_nand(d, 1, __ATOMIC_RELAXED, 42); // expected-error {{address argument to atomic operation must be a pointer to integer ('float *' invalid)}} + *f = __scoped_atomic_uinc_wrap(f, 1u, __ATOMIC_RELAXED, 42); // expected-error {{address argument to atomic operation must be a pointer to integer ('float *' invalid)}} + *e = __scoped_atomic_udec_wrap(e, 1u, __ATOMIC_RELAXED, 42); // expected-error {{address argument to atomic operation must be a pointer to integer ('float *' invalid)}} } int fi4a(int *i) { diff --git a/clang/test/Sema/type-dependent-attrs.c b/clang/test/Sema/type-dependent-attrs.c new file mode 100644 index 0000000..13068b3 --- /dev/null +++ b/clang/test/Sema/type-dependent-attrs.c @@ -0,0 +1,10 @@ +// RUN: %clang_cc1 -std=c23 -fsyntax-only -verify %s + +int open() { return 0; } +void close(typeof(open()) *) {} + +void cleanup_attr() { + int fd_int [[gnu::cleanup(close)]] = open(); + auto fd_auto [[gnu::cleanup(close)]] = open(); + float fd_invalid [[gnu::cleanup(close)]] = open(); // expected-error {{'cleanup' function 'close' parameter has type 'typeof (open()) *' (aka 'int *') which is incompatible with type 'float *'}} +} diff --git a/clang/test/Sema/warn-enum-compare-typo.c b/clang/test/Sema/warn-enum-compare-typo.c new file mode 100644 index 0000000..f937df1 --- /dev/null +++ b/clang/test/Sema/warn-enum-compare-typo.c @@ -0,0 +1,98 @@ +// RUN: %clang_cc1 -fsyntax-only -Wenum-compare-typo -verify %s +// RUN: %clang_cc1 -fsyntax-only -Wenum-compare-typo -fdiagnostics-parseable-fixits %s 2>&1 | FileCheck %s + + +enum PossibleTypoLeft { + Val1 = 1 << 0, + // expected-warning@+3 {{comparison operator '<' is potentially a typo for a shift operator '<<'}} + // expected-note@+2 {{use '<<' to perform a bitwise shift}} + // CHECK: fix-it:"{{.*}}":{[[@LINE+1]]:12-[[@LINE+1]]:13}:"<<" + Bad1 = 1 < 2, + // expected-warning@+3 {{comparison operator '>' is potentially a typo for a shift operator '>>'}} + // expected-note@+2 {{use '>>' to perform a bitwise shift}} + // CHECK: fix-it:"{{.*}}":{[[@LINE+1]]:12-[[@LINE+1]]:13}:">>" + Bad2 = 1 > 3, + // expected-warning@+3 {{comparison operator '>' is potentially a typo for a shift operator '>>'}} + // expected-note@+2 {{use '>>' to perform a bitwise shift}} + // CHECK: fix-it:"{{.*}}":{[[@LINE+1]]:13-[[@LINE+1]]:14}:">>" + Bad3 = (1 > 3) +}; + +enum PossibleTypoRight { + Val2 = 1 >> 0, + // expected-warning@+3 {{comparison operator '<' is potentially a typo for a shift operator '<<'}} + // expected-note@+2 {{use '<<' to perform a bitwise shift}} + // CHECK: fix-it:"{{.*}}":{[[@LINE+1]]:12-[[@LINE+1]]:13}:"<<" + Bad4 = 1 < 2, + // expected-warning@+3 {{comparison operator '>' is potentially a typo for a shift operator '>>'}} + // expected-note@+2 {{use '>>' to perform a bitwise shift}} + // CHECK: fix-it:"{{.*}}":{[[@LINE+1]]:12-[[@LINE+1]]:13}:">>" + Bad5 = 1 > 3, + // expected-warning@+3 {{comparison operator '<' is potentially a typo for a shift operator '<<'}} + // expected-note@+2 {{use '<<' to perform a bitwise shift}} + // CHECK: fix-it:"{{.*}}":{[[@LINE+1]]:13-[[@LINE+1]]:14}:"<<" + Bad6 = (1 < 3) +}; + +// Case 3: Context provided by other bitwise operators (&, |) +// Even though there are no shifts, the presence of '|' implies flags. +enum PossibleTypoBitwiseOr { + FlagA = 0x1, + FlagB = 0x2, + FlagCombo = FlagA | FlagB, + // expected-warning@+3 {{comparison operator '<' is potentially a typo for a shift operator '<<'}} + // expected-note@+2 {{use '<<' to perform a bitwise shift}} + // CHECK: fix-it:"{{.*}}":{[[@LINE+1]]:17-[[@LINE+1]]:18}:"<<" + FlagTypo1 = 1 < FlagCombo, + // expected-warning@+3 {{comparison operator '>' is potentially a typo for a shift operator '>>'}} + // expected-note@+2 {{use '>>' to perform a bitwise shift}} + // CHECK: fix-it:"{{.*}}":{[[@LINE+1]]:17-[[@LINE+1]]:18}:">>" + FlagTypo2 = 1 > FlagCombo +}; + +enum PossibleTypoBitwiseAnd { + FlagAnd = FlagA & FlagB, + // expected-warning@+3 {{comparison operator '<' is potentially a typo for a shift operator '<<'}} + // expected-note@+2 {{use '<<' to perform a bitwise shift}} + // CHECK: fix-it:"{{.*}}":{[[@LINE+1]]:17-[[@LINE+1]]:18}:"<<" + FlagTypo3 = 1 < FlagAnd, + // expected-warning@+3 {{comparison operator '>' is potentially a typo for a shift operator '>>'}} + // expected-note@+2 {{use '>>' to perform a bitwise shift}} + // CHECK: fix-it:"{{.*}}":{[[@LINE+1]]:17-[[@LINE+1]]:18}:">>" + FlagTypo4 = 1 > FlagAnd +}; + +enum NoWarningOnDirectInit { + A = 0, + B = 1, + Ok1 = 1 < 2, // No warning expected + Ok2 = 1 > 2 // No warning expected +}; + +enum NoWarningOnArith { + D = 0 + 1, + E = D * 10, + F = E - D, + G = F / D, + Ok3 = 1 < E, // No warning expected + Ok4 = 1 > E // No warning expected +}; + +enum NoWarningOnExplicitCast { + Bit1 = 1 << 0, + Ok5 = (int)(1 < 2) // No warning expected +}; + +enum NoWarningOnNoneBitShift { + Bit2 = 1 << 0, + Ok6 = (3 < 2) // No warning expected +}; + +// Ensure the diagnostic group works +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wenum-compare-typo" +enum IGNORED { + Ok7 = 1 << 1, + Ignored3 = 1 < 10 // No warning +}; +#pragma clang diagnostic pop diff --git a/clang/test/Sema/warn-fortify-scanf.c b/clang/test/Sema/warn-fortify-scanf.c index 16cbfa2..7b8f4b7 100644 --- a/clang/test/Sema/warn-fortify-scanf.c +++ b/clang/test/Sema/warn-fortify-scanf.c @@ -59,10 +59,17 @@ void call_fscanf(void) { char buf20[20]; char buf30[30]; fscanf(0, "%4s %5s %10s", buf20, buf30, buf10); // expected-warning {{'fscanf' may overflow; destination buffer in argument 5 has size 10, but the corresponding specifier may require size 11}} + // expected-warning@-1 {{null passed to a callee that requires a non-null argument}} fscanf(0, "%4s %5s %11s", buf20, buf30, buf10); // expected-warning {{'fscanf' may overflow; destination buffer in argument 5 has size 10, but the corresponding specifier may require size 12}} + // expected-warning@-1 {{null passed to a callee that requires a non-null argument}} fscanf(0, "%4s %5s %9s", buf20, buf30, buf10); + // expected-warning@-1 {{null passed to a callee that requires a non-null argument}} fscanf(0, "%20s %5s %9s", buf20, buf30, buf10); // expected-warning {{'fscanf' may overflow; destination buffer in argument 3 has size 20, but the corresponding specifier may require size 21}} + // expected-warning@-1 {{null passed to a callee that requires a non-null argument}} fscanf(0, "%21s %5s %9s", buf20, buf30, buf10); // expected-warning {{'fscanf' may overflow; destination buffer in argument 3 has size 20, but the corresponding specifier may require size 22}} + // expected-warning@-1 {{null passed to a callee that requires a non-null argument}} fscanf(0, "%19s %5s %9s", buf20, buf30, buf10); + // expected-warning@-1 {{null passed to a callee that requires a non-null argument}} fscanf(0, "%19s %29s %9s", buf20, buf30, buf10); + // expected-warning@-1 {{null passed to a callee that requires a non-null argument}} } diff --git a/clang/test/Sema/warn-fortify-source.c b/clang/test/Sema/warn-fortify-source.c index 216878c..750bd53 100644 --- a/clang/test/Sema/warn-fortify-source.c +++ b/clang/test/Sema/warn-fortify-source.c @@ -76,6 +76,14 @@ void call_strcpy_nowarn(void) { __builtin_strcpy(dst, src); } +void call_strcat(void) { + const char *const src = "abcd"; + char dst1[5]; + char dst2[4]; + __builtin_strcat(dst1, src); + __builtin_strcat(dst2, src); // expected-warning {{'strcat' will always overflow; destination buffer has size 4, but the source string has length 5 (including NUL byte)}} +} + void call_stpcpy(void) { const char *const src = "abcd"; char dst1[5]; diff --git a/clang/test/Sema/warn-lifetime-safety-dataflow.cpp b/clang/test/Sema/warn-lifetime-safety-dataflow.cpp index 31148b9..7b6fc92 100644 --- a/clang/test/Sema/warn-lifetime-safety-dataflow.cpp +++ b/clang/test/Sema/warn-lifetime-safety-dataflow.cpp @@ -18,8 +18,8 @@ MyObj* return_local_addr() { return p; // CHECK: Use ([[O_P]] (Decl: p), Read) // CHECK: OriginFlow (Dest: [[O_RET_VAL:[0-9]+]] (Expr: ImplicitCastExpr), Src: [[O_P]] (Decl: p)) -// CHECK: ReturnOfOrigin ([[O_RET_VAL]] (Expr: ImplicitCastExpr)) // CHECK: Expire ([[L_X]] (Path: x)) +// CHECK: OriginEscapes ([[O_RET_VAL]] (Expr: ImplicitCastExpr)) } @@ -49,8 +49,8 @@ MyObj* assign_and_return_local_addr() { return ptr2; // CHECK: Use ([[O_PTR2]] (Decl: ptr2), Read) // CHECK: OriginFlow (Dest: [[O_RET_VAL:[0-9]+]] (Expr: ImplicitCastExpr), Src: [[O_PTR2]] (Decl: ptr2)) -// CHECK: ReturnOfOrigin ([[O_RET_VAL]] (Expr: ImplicitCastExpr)) // CHECK: Expire ([[L_Y]] (Path: y)) +// CHECK: OriginEscapes ([[O_RET_VAL]] (Expr: ImplicitCastExpr)) } // Return of Non-Pointer Type @@ -59,6 +59,7 @@ int return_int_val() { int x = 10; // CHECK: Block B{{[0-9]+}}: // CHECK: Issue ([[L_X:[0-9]+]] (Path: x), ToOrigin: {{[0-9]+}} (Expr: DeclRefExpr)) +// CHECK: Expire ([[L_X:[0-9]+]] (Path: x)) return x; } // CHECK-NEXT: End of Block @@ -77,7 +78,6 @@ void loan_expires_cpp() { } -// FIXME: No expire for Trivial Destructors // CHECK-LABEL: Function: loan_expires_trivial void loan_expires_trivial() { int trivial_obj = 1; @@ -86,11 +86,29 @@ void loan_expires_trivial() { // CHECK: OriginFlow (Dest: [[O_ADDR_TRIVIAL_OBJ:[0-9]+]] (Expr: UnaryOperator), Src: [[O_DRE_TRIVIAL]] (Expr: DeclRefExpr)) int* pTrivialObj = &trivial_obj; // CHECK: OriginFlow (Dest: {{[0-9]+}} (Decl: pTrivialObj), Src: [[O_ADDR_TRIVIAL_OBJ]] (Expr: UnaryOperator)) -// CHECK-NOT: Expire +// CHECK: Expire ([[L_TRIVIAL_OBJ:[0-9]+]] (Path: trivial_obj)) // CHECK-NEXT: End of Block - // FIXME: Add check for Expire once trivial destructors are handled for expiration. } +// Trivial Destructors +// CHECK-LABEL: Function: return_int_pointer +int* return_int_pointer() { + int* ptr; +// CHECK: Block B{{[0-9]+}}: + int x = 1; +// CHECK: Issue ([[L_X:[0-9]+]] (Path: x), ToOrigin: [[O_DRE_X:[0-9]+]] (Expr: DeclRefExpr)) +// CHECK: OriginFlow (Dest: [[O_ADDR_X:[0-9]+]] (Expr: UnaryOperator), Src: [[O_DRE_X]] (Expr: DeclRefExpr)) + ptr = &x; +// CHECK: Use ([[O_PTR:[0-9]+]] (Decl: ptr), Write) +// CHECK: OriginFlow (Dest: [[O_PTR]] (Decl: ptr), Src: [[O_ADDR_X]] (Expr: UnaryOperator)) +// CHECK: Use ([[O_PTR]] (Decl: ptr), Read) +// CHECK: OriginFlow (Dest: [[O_RET_VAL:[0-9]+]] (Expr: ImplicitCastExpr), Src: [[O_PTR]] (Decl: ptr)) +// CHECK: Expire ([[L_X]] (Path: x)) +// CHECK: OriginEscapes ([[O_RET_VAL]] (Expr: ImplicitCastExpr)) + return ptr; +} +// CHECK-NEXT: End of Block + // CHECK-LABEL: Function: conditional void conditional(bool condition) { int a = 5; @@ -414,3 +432,20 @@ void test_use_lifetimebound_call() { // CHECK: Expire ([[L_Y]] (Path: y)) // CHECK: Expire ([[L_X]] (Path: x)) } +// CHECK-LABEL: Function: test_conditional_operator +void test_conditional_operator(bool cond) { + MyObj x, y; + MyObj *p = cond ? &x : &y; +// CHECK: Block B{{[0-9]+}}: +// CHECK: Issue ([[L_X:[0-9]+]] (Path: x), ToOrigin: [[O_DRE_X:[0-9]+]] (Expr: DeclRefExpr)) +// CHECK: OriginFlow (Dest: [[O_ADDR_X:[0-9]+]] (Expr: UnaryOperator), Src: [[O_DRE_X]] (Expr: DeclRefExpr)) +// CHECK: Block B{{[0-9]+}}: +// CHECK: Issue ([[L_Y:[0-9]+]] (Path: y), ToOrigin: [[O_DRE_Y:[0-9]+]] (Expr: DeclRefExpr)) +// CHECK: OriginFlow (Dest: [[O_ADDR_Y:[0-9]+]] (Expr: UnaryOperator), Src: [[O_DRE_Y]] (Expr: DeclRefExpr)) +// CHECK: Block B{{[0-9]+}}: +// CHECK: OriginFlow (Dest: [[O_COND_OP:[0-9]+]] (Expr: ConditionalOperator), Src: [[O_ADDR_X]] (Expr: UnaryOperator)) +// CHECK: OriginFlow (Dest: [[O_COND_OP]] (Expr: ConditionalOperator), Src: [[O_ADDR_Y]] (Expr: UnaryOperator), Merge) +// CHECK: OriginFlow (Dest: [[O_P:[0-9]+]] (Decl: p), Src: [[O_COND_OP]] (Expr: ConditionalOperator)) +// CHECK: Expire ([[L_Y]] (Path: y)) +// CHECK: Expire ([[L_X]] (Path: x)) +} diff --git a/clang/test/Sema/warn-lifetime-safety-suggestions.cpp b/clang/test/Sema/warn-lifetime-safety-suggestions.cpp new file mode 100644 index 0000000..c0f675a --- /dev/null +++ b/clang/test/Sema/warn-lifetime-safety-suggestions.cpp @@ -0,0 +1,109 @@ +// RUN: %clang_cc1 -fsyntax-only -fexperimental-lifetime-safety -Wexperimental-lifetime-safety-suggestions -verify %s + +struct MyObj { + int id; + ~MyObj() {} // Non-trivial destructor + MyObj operator+(MyObj); +}; + +struct [[gsl::Pointer()]] View { + View(const MyObj&); // Borrows from MyObj + View(); + void use() const; +}; + +View return_view_directly (View a) { // expected-warning {{param should be marked [[clang::lifetimebound]]}}. + return a; // expected-note {{param returned here}} +} + +View conditional_return_view ( + View a, // expected-warning {{param should be marked [[clang::lifetimebound]]}}. + View b, // expected-warning {{param should be marked [[clang::lifetimebound]]}}. + bool c) { + View res; + if (c) + res = a; + else + res = b; + return res; // expected-note 2 {{param returned here}} +} + +// FIXME: Fails to generate lifetime suggestion for reference types as these are not handled currently. +MyObj& return_reference (MyObj& a, MyObj& b, bool c) { + if(c) { + return a; + } + return b; +} + +// FIXME: Fails to generate lifetime suggestion for reference types as these are not handled currently. +View return_view_from_reference (MyObj& p) { + return p; +} + +int* return_pointer_directly (int* a) { // expected-warning {{param should be marked [[clang::lifetimebound]]}}. + return a; // expected-note {{param returned here}} +} + +MyObj* return_pointer_object (MyObj* a) { // expected-warning {{param should be marked [[clang::lifetimebound]]}}. + return a; // expected-note {{param returned here}} +} + +View only_one_paramter_annotated (View a [[clang::lifetimebound]], + View b, // expected-warning {{param should be marked [[clang::lifetimebound]]}}. + bool c) { + if(c) + return a; + return b; // expected-note {{param returned here}} +} + +View reassigned_to_another_parameter ( + View a, + View b) { // expected-warning {{param should be marked [[clang::lifetimebound]]}}. + a = b; + return a; // expected-note {{param returned here}} +} + +struct ReturnsSelf { + const ReturnsSelf& get() const { + return *this; + } +}; + +struct ViewProvider { + MyObj data; + View getView() const { + return data; + } +}; + +// FIXME: Fails to generate lifetime suggestions for the implicit 'this' parameter, as this feature is not yet implemented. +void test_get_on_temporary() { + const ReturnsSelf& s_ref = ReturnsSelf().get(); + (void)s_ref; +} + +// FIXME: Fails to generate lifetime suggestions for the implicit 'this' parameter, as this feature is not yet implemented. +void test_getView_on_temporary() { + View sv = ViewProvider{1}.getView(); + (void)sv; +} + +//===----------------------------------------------------------------------===// +// Negative Test Cases +//===----------------------------------------------------------------------===// + +View already_annotated(View a [[clang::lifetimebound]]) { + return a; +} + +MyObj return_obj_by_value(MyObj& p) { + return p; +} + +MyObj GlobalMyObj; +View Global = GlobalMyObj; +View Reassigned(View a) { + a = Global; + return a; +} diff --git a/clang/test/Sema/warn-lifetime-safety.cpp b/clang/test/Sema/warn-lifetime-safety.cpp index 4f234f0..1191469e 100644 --- a/clang/test/Sema/warn-lifetime-safety.cpp +++ b/clang/test/Sema/warn-lifetime-safety.cpp @@ -1,4 +1,4 @@ -// RUN: %clang_cc1 -fsyntax-only -fexperimental-lifetime-safety -Wexperimental-lifetime-safety -verify %s +// RUN: %clang_cc1 -fsyntax-only -fexperimental-lifetime-safety -Wexperimental-lifetime-safety -Wno-dangling -verify %s struct MyObj { int id; @@ -12,6 +12,10 @@ struct [[gsl::Pointer()]] View { void use() const; }; +class TriviallyDestructedClass { + View a, b; +}; + //===----------------------------------------------------------------------===// // Basic Definite Use-After-Free (-W...permissive) // These are cases where the pointer is guaranteed to be dangling at the use site. @@ -396,6 +400,186 @@ void loan_from_previous_iteration(MyObj safe, bool condition) { } // expected-note {{destroyed here}} } +void trivial_int_uaf() { + int * a; + { + int b = 1; + a = &b; // expected-warning {{object whose reference is captured does not live long enough}} + } // expected-note {{destroyed here}} + (void)*a; // expected-note {{later used here}} +} + +void trivial_class_uaf() { + TriviallyDestructedClass* ptr; + { + TriviallyDestructedClass s; + ptr = &s; // expected-warning {{object whose reference is captured does not live long enough}} + } // expected-note {{destroyed here}} + (void)ptr; // expected-note {{later used here}} +} + +//===----------------------------------------------------------------------===// +// Basic Definite Use-After-Return (Return-Stack-Address) (-W...permissive) +// These are cases where the pointer is guaranteed to be dangling at the use site. +//===----------------------------------------------------------------------===// + +MyObj* simple_return_stack_address() { + MyObj s; + MyObj* p = &s; // expected-warning {{address of stack memory is returned later}} + return p; // expected-note {{returned here}} +} + +MyObj* direct_return() { + MyObj s; + return &s; // expected-warning {{address of stack memory is returned later}} + // expected-note@-1 {{returned here}} +} + +const MyObj* conditional_assign_unconditional_return(const MyObj& safe, bool c) { + MyObj s; + const MyObj* p = &safe; + if (c) { + p = &s; // expected-warning {{address of stack memory is returned later}} + } + return p; // expected-note {{returned here}} +} + +View conditional_assign_both_branches(const MyObj& safe, bool c) { + MyObj s; + View p; + if (c) { + p = s; // expected-warning {{address of stack memory is returned later}} + } + else { + p = safe; + } + return p; // expected-note {{returned here}} + +} + +View reassign_safe_to_local(const MyObj& safe) { + MyObj local; + View p = safe; + p = local; // expected-warning {{address of stack memory is returned later}} + return p; // expected-note {{returned here}} +} + +View pointer_chain_to_local() { + MyObj local; + View p1 = local; // expected-warning {{address of stack memory is returned later}} + View p2 = p1; + return p2; // expected-note {{returned here}} +} + +View multiple_assign_multiple_return(const MyObj& safe, bool c1, bool c2) { + MyObj local1; + MyObj local2; + View p; + if (c1) { + p = local1; // expected-warning {{address of stack memory is returned later}} + return p; // expected-note {{returned here}} + } + else if (c2) { + p = local2; // expected-warning {{address of stack memory is returned later}} + return p; // expected-note {{returned here}} + } + p = safe; + return p; +} + +View multiple_assign_single_return(const MyObj& safe, bool c1, bool c2) { + MyObj local1; + MyObj local2; + View p; + if (c1) { + p = local1; // expected-warning {{address of stack memory is returned later}} + } + else if (c2) { + p = local2; // expected-warning {{address of stack memory is returned later}} + } + else { + p = safe; + } + return p; // expected-note 2 {{returned here}} +} + +View direct_return_of_local() { + MyObj stack; + return stack; // expected-warning {{address of stack memory is returned later}} + // expected-note@-1 {{returned here}} +} + +MyObj& reference_return_of_local() { + MyObj stack; + return stack; // expected-warning {{address of stack memory is returned later}} + // expected-note@-1 {{returned here}} +} + +int* trivial_int_uar() { + int *a; + int b = 1; + a = &b; // expected-warning {{address of stack memory is returned later}} + return a; // expected-note {{returned here}} +} + +TriviallyDestructedClass* trivial_class_uar () { + TriviallyDestructedClass *ptr; + TriviallyDestructedClass s; + ptr = &s; // expected-warning {{address of stack memory is returned later}} + return ptr; // expected-note {{returned here}} +} + +const int& return_parameter(int a) { + return a; // expected-warning {{address of stack memory is returned later}} + // expected-note@-1 {{returned here}} +} + +int* return_pointer_to_parameter(int a) { + return &a; // expected-warning {{address of stack memory is returned later}} + // expected-note@-1 {{returned here}} +} + +const int& return_reference_to_parameter(int a) +{ + const int &b = a; + return b; // expected-warning {{address of stack memory is returned later}} + // expected-note@-1 {{returned here}} +} + +const int& get_ref_to_local() { + int a = 42; + return a; // expected-warning {{address of stack memory is returned later}} + // expected-note@-1 {{returned here}} +} + +//===----------------------------------------------------------------------===// +// Use-After-Scope & Use-After-Return (Return-Stack-Address) Combined +// These are cases where the diagnostic kind is determined by location +//===----------------------------------------------------------------------===// + +MyObj* uaf_before_uar() { + MyObj* p; + { + MyObj local_obj; + p = &local_obj; // expected-warning {{object whose reference is captured does not live long enough}} + } // expected-note {{destroyed here}} + return p; // expected-note {{later used here}} +} + +View uar_before_uaf(const MyObj& safe, bool c) { + View p; + { + MyObj local_obj; + p = local_obj; // expected-warning {{address of stack memory is returned later}} + if (c) { + return p; // expected-note {{returned here}} + } + } + p.use(); + p = safe; + return p; +} + //===----------------------------------------------------------------------===// // No-Error Cases //===----------------------------------------------------------------------===// @@ -434,12 +618,20 @@ void no_error_loan_from_current_iteration(bool cond) { } } +View safe_return(const MyObj& safe) { + MyObj local; + View p = local; + p = safe; // p has been reassigned + return p; // This is safe +} //===----------------------------------------------------------------------===// // Lifetimebound Attribute Tests //===----------------------------------------------------------------------===// View Identity(View v [[clang::lifetimebound]]); +const MyObj& IdentityRef(const MyObj& obj [[clang::lifetimebound]]); +MyObj* Identity(MyObj* v [[clang::lifetimebound]]); View Choose(bool cond, View a [[clang::lifetimebound]], View b [[clang::lifetimebound]]); MyObj* GetPointer(const MyObj& obj [[clang::lifetimebound]]); @@ -555,7 +747,8 @@ void lifetimebound_partial_safety(bool cond) { v.use(); // expected-note {{later used here}} } -// FIXME: Creating reference from lifetimebound call doesn't propagate loans. +// FIXME: Warning should be on the 'GetObject' call, not the assignment to 'ptr'. +// The loan from the lifetimebound argument is not propagated to the call expression itself. const MyObj& GetObject(View v [[clang::lifetimebound]]); void lifetimebound_return_reference() { View v; @@ -564,9 +757,9 @@ void lifetimebound_return_reference() { MyObj obj; View temp_v = obj; const MyObj& ref = GetObject(temp_v); - ptr = &ref; - } - (void)*ptr; + ptr = &ref; // expected-warning {{object whose reference is captured does not live long enough}} + } // expected-note {{destroyed here}} + (void)*ptr; // expected-note {{later used here}} } // FIXME: No warning for non gsl::Pointer types. Origin tracking is only supported for pointer types. @@ -582,3 +775,171 @@ void lifetimebound_ctor() { } (void)v; } + +View lifetimebound_return_of_local() { + MyObj stack; + return Identity(stack); // expected-warning {{address of stack memory is returned later}} + // expected-note@-1 {{returned here}} +} + +const MyObj& lifetimebound_return_ref_to_local() { + MyObj stack; + return IdentityRef(stack); // expected-warning {{address of stack memory is returned later}} + // expected-note@-1 {{returned here}} +} + +View lifetimebound_return_by_value_param(MyObj stack_param) { + return Identity(stack_param); // expected-warning {{address of stack memory is returned later}} + // expected-note@-1 {{returned here}} +} + +View lifetimebound_return_by_value_multiple_param(int cond, MyObj a, MyObj b, MyObj c) { + if (cond == 1) + return Identity(a); // expected-warning {{address of stack memory is returned later}} + // expected-note@-1 {{returned here}} + if (cond == 2) + return Identity(b); // expected-warning {{address of stack memory is returned later}} + // expected-note@-1 {{returned here}} + return Identity(c); // expected-warning {{address of stack memory is returned later}} + // expected-note@-1 {{returned here}} +} + +template<class T> +View lifetimebound_return_by_value_param_template(T t) { + return Identity(t); // expected-warning {{address of stack memory is returned later}} + // expected-note@-1 {{returned here}} +} +void use_lifetimebound_return_by_value_param_template() { + lifetimebound_return_by_value_param_template(MyObj{}); // expected-note {{in instantiation of}} +} + +void lambda_uar_param() { + auto lambda = [](MyObj stack_param) { + return Identity(stack_param); // expected-warning {{address of stack memory is returned later}} + // expected-note@-1 {{returned here}} + }; + lambda(MyObj{}); +} + +// FIXME: This should be detected. We see correct destructors but origin flow breaks somewhere. +namespace VariadicTemplatedParamsUAR{ + +template<typename... Args> +View Max(Args... args [[clang::lifetimebound]]); + +template<typename... Args> +View lifetimebound_return_of_variadic_param(Args... args) { + return Max(args...); +} +void test_variadic() { + lifetimebound_return_of_variadic_param(MyObj{1}, MyObj{2}, MyObj{3}); +} +} + +// FIXME: Fails to diagnose UAF when a reference to a by-value param escapes via an out-param. +void uaf_from_by_value_param_failing(MyObj param, View* out_p) { + *out_p = Identity(param); +} + +// Conditional operator. +void conditional_operator_one_unsafe_branch(bool cond) { + MyObj safe; + MyObj* p = &safe; + { + MyObj temp; + p = cond ? &temp // expected-warning {{object whose reference is captured may not live long enough}} + : &safe; + } // expected-note {{destroyed here}} + + // This is not a use-after-free for any value of `cond` but the analysis + // cannot reason this and marks the above as a false positive. This + // ensures safety regardless of cond's value. + if (cond) + p = &safe; + (void)*p; // expected-note {{later used here}} +} + +void conditional_operator_two_unsafe_branches(bool cond) { + MyObj* p; + { + MyObj a, b; + p = cond ? &a // expected-warning {{object whose reference is captured does not live long enough}} + : &b; // expected-warning {{object whose reference is captured does not live long enough}} + } // expected-note 2 {{destroyed here}} + (void)*p; // expected-note 2 {{later used here}} +} + +void conditional_operator_nested(bool cond) { + MyObj* p; + { + MyObj a, b, c, d; + p = cond ? cond ? &a // expected-warning {{object whose reference is captured does not live long enough}}. + : &b // expected-warning {{object whose reference is captured does not live long enough}}. + : cond ? &c // expected-warning {{object whose reference is captured does not live long enough}}. + : &d; // expected-warning {{object whose reference is captured does not live long enough}}. + } // expected-note 4 {{destroyed here}} + (void)*p; // expected-note 4 {{later used here}} +} + +void conditional_operator_lifetimebound(bool cond) { + MyObj* p; + { + MyObj a, b; + p = Identity(cond ? &a // expected-warning {{object whose reference is captured does not live long enough}} + : &b); // expected-warning {{object whose reference is captured does not live long enough}} + } // expected-note 2 {{destroyed here}} + (void)*p; // expected-note 2 {{later used here}} +} + +void conditional_operator_lifetimebound_nested(bool cond) { + MyObj* p; + { + MyObj a, b; + p = Identity(cond ? Identity(&a) // expected-warning {{object whose reference is captured does not live long enough}} + : Identity(&b)); // expected-warning {{object whose reference is captured does not live long enough}} + } // expected-note 2 {{destroyed here}} + (void)*p; // expected-note 2 {{later used here}} +} + +void conditional_operator_lifetimebound_nested_deep(bool cond) { + MyObj* p; + { + MyObj a, b, c, d; + p = Identity(cond ? Identity(cond ? &a // expected-warning {{object whose reference is captured does not live long enough}} + : &b) // expected-warning {{object whose reference is captured does not live long enough}} + : Identity(cond ? &c // expected-warning {{object whose reference is captured does not live long enough}} + : &d)); // expected-warning {{object whose reference is captured does not live long enough}} + } // expected-note 4 {{destroyed here}} + (void)*p; // expected-note 4 {{later used here}} +} + +void parentheses(bool cond) { + MyObj* p; + { + MyObj a; + p = &((((a)))); // expected-warning {{object whose reference is captured does not live long enough}} + } // expected-note {{destroyed here}} + (void)*p; // expected-note {{later used here}} + + { + MyObj a; + p = ((GetPointer((a)))); // expected-warning {{object whose reference is captured does not live long enough}} + } // expected-note {{destroyed here}} + (void)*p; // expected-note {{later used here}} + + { + MyObj a, b, c, d; + p = &(cond ? (cond ? a // expected-warning {{object whose reference is captured does not live long enough}}. + : b) // expected-warning {{object whose reference is captured does not live long enough}}. + : (cond ? c // expected-warning {{object whose reference is captured does not live long enough}}. + : d)); // expected-warning {{object whose reference is captured does not live long enough}}. + } // expected-note 4 {{destroyed here}} + (void)*p; // expected-note 4 {{later used here}} + + { + MyObj a, b, c, d; + p = ((cond ? (((cond ? &a : &b))) // expected-warning 2 {{object whose reference is captured does not live long enough}}. + : &(((cond ? c : d))))); // expected-warning 2 {{object whose reference is captured does not live long enough}}. + } // expected-note 4 {{destroyed here}} + (void)*p; // expected-note 4 {{later used here}} +} diff --git a/clang/test/Sema/warn-unreachable-file-scope.c b/clang/test/Sema/warn-unreachable-file-scope.c new file mode 100644 index 0000000..64a6918 --- /dev/null +++ b/clang/test/Sema/warn-unreachable-file-scope.c @@ -0,0 +1,37 @@ +// RUN: %clang_cc1 -fsyntax-only -verify %s + +typedef unsigned char u8; + +u8 a1 = (0 ? 0xffff : 0xff); +u8 a2 = (1 ? 0xffff : 0xff); // expected-warning {{implicit conversion from 'int' to 'u8' (aka 'unsigned char') changes value from 65535 to 255}} +u8 a3 = (1 ? 0xff : 0xffff); +u8 a4 = (0 ? 0xff : 0xffff); // expected-warning {{implicit conversion from 'int' to 'u8' (aka 'unsigned char') changes value from 65535 to 255}} + +unsigned long long b1 = 1 ? 0 : 1ULL << 64; +unsigned long long b2 = 0 ? 0 : 1ULL << 64; // expected-warning {{shift count >= width of type}} +unsigned long long b3 = 1 ? 1ULL << 64 : 0; // expected-warning {{shift count >= width of type}} + +#define M(n) (((n) == 64) ? ~0ULL : ((1ULL << (n)) - 1)) +unsigned long long c1 = M(64); +unsigned long long c2 = M(32); + +static u8 d1 = (0 ? 0xffff : 0xff); +static u8 d2 = (1 ? 0xffff : 0xff); // expected-warning {{implicit conversion from 'int' to 'u8' (aka 'unsigned char') changes value from 65535 to 255}} + +int a = 1 ? 6 : (1,2); +int b = 0 ? 6 : (1,2); // expected-warning {{left operand of comma operator has no effect}} + +void f(void) { + u8 e1 = (0 ? 0xffff : 0xff); + u8 e2 = (1 ? 0xffff : 0xff); // expected-warning {{implicit conversion from 'int' to 'u8' (aka 'unsigned char') changes value from 65535 to 255}} + + unsigned long long e3 = 1 ? 0 : 1ULL << 64; + unsigned long long e4 = 0 ? 0 : 1ULL << 64; // expected-warning {{shift count >= width of type}} +} + +void statics(void) { + static u8 f1 = (0 ? 0xffff : 0xff); + static u8 f2 = (1 ? 0xffff : 0xff); // expected-warning {{implicit conversion from 'int' to 'u8' (aka 'unsigned char') changes value from 65535 to 255}} + static u8 f3 = (1 ? 0xff : 0xffff); + static u8 f4 = (0 ? 0xff : 0xffff); // expected-warning {{implicit conversion from 'int' to 'u8' (aka 'unsigned char') changes value from 65535 to 255}} +} |
