PR target/88638 - FAIL: fsf-nsstring-format-1.s on darwin gcc/c-family/ChangeLog: PR target/88638 * c-attribs.c (positional_argument): Call valid_format_string_type_p and issue errors if it fails. * c-common.h (valid_format_string_type_p): Declare. * c-format.c (valid_stringptr_type_p): Rename... (valid_format_string_type_p): ...to this and make extern. (handle_format_arg_attribute): Adjust to new name. (check_format_string): Same. gcc/testsuite/ChangeLog: PR target/88638 * gcc.dg/format/attr-8.c: New test. * gcc.dg/darwin-cfstring-format-1.c: Adjust diagnostics. * gcc.dg/format/attr-3.c: Same. * obj-c++.dg/fsf-nsstring-format-1.mm: Same. * objc.dg/fsf-nsstring-format-1.m: Same. gcc/ChangeLog: PR target/88638 * doc/extend.texi (Darwin Format Checks): Clarify. Index: gcc/c-family/c-attribs.c =================================================================== --- gcc/c-family/c-attribs.c (revision 267616) +++ gcc/c-family/c-attribs.c (working copy) @@ -631,17 +631,13 @@ positional_argument (const_tree fntype, const_tree return NULL_TREE; } + /* Where the expected code is STRING_CST accept any pointer + expected by attribute format (this includes possibly qualified + char pointers and, for targets like Darwin, also pointers to + struct CFString). */ bool type_match; - if (code == STRING_CST && POINTER_TYPE_P (argtype)) - { - /* Where the expected code is STRING_CST accept any pointer - to a narrow character type, qualified or otherwise. */ - tree type = TREE_TYPE (argtype); - type = TYPE_MAIN_VARIANT (type); - type_match = (type == char_type_node - || type == signed_char_type_node - || type == unsigned_char_type_node); - } + if (code == STRING_CST) + type_match = valid_format_string_type_p (argtype); else if (code == INTEGER_TYPE) /* For integers, accept enums, wide characters and other types that match INTEGRAL_TYPE_P except for bool. */ @@ -652,6 +648,21 @@ positional_argument (const_tree fntype, const_tree if (!type_match) { + if (code == STRING_CST) + { + /* Reject invalid format strings with an error. */ + if (argno < 1) + error ("%qE attribute argument value %qE refers to " + "parameter type %qT", + atname, pos, argtype); + else + error ("%qE attribute argument %i value %qE refers to " + "parameter type %qT", + atname, argno, pos, argtype); + + return NULL_TREE; + } + if (argno < 1) warning (OPT_Wattributes, "%qE attribute argument value %qE refers to " Index: gcc/c-family/c-common.h =================================================================== --- gcc/c-family/c-common.h (revision 267616) +++ gcc/c-family/c-common.h (working copy) @@ -1335,6 +1335,9 @@ extern tree tm_mask_to_attr (int); extern tree find_tm_attribute (tree); extern const struct attribute_spec::exclusions attr_cold_hot_exclusions[]; +/* In c-format.c. */ +extern bool valid_format_string_type_p (tree); + /* A bitmap of flags to positional_argument. */ enum posargflags { /* Consider positional attribute argument value zero valid. */ Index: gcc/c-family/c-format.c =================================================================== --- gcc/c-family/c-format.c (revision 267616) +++ gcc/c-family/c-format.c (working copy) @@ -122,8 +122,8 @@ format_warning_at_char (location_t fmt_string_loc, The function returns true if strref points to any string type valid for the language dialect and target. */ -static bool -valid_stringptr_type_p (tree strref) +bool +valid_format_string_type_p (tree strref) { return (strref != NULL && TREE_CODE (strref) == POINTER_TYPE @@ -160,7 +160,7 @@ handle_format_arg_attribute (tree *node, tree atna return NULL_TREE; } - if (!valid_stringptr_type_p (TREE_TYPE (type))) + if (!valid_format_string_type_p (TREE_TYPE (type))) { if (!(flags & (int) ATTR_FLAG_BUILT_IN)) error ("function does not return string type"); @@ -194,7 +194,7 @@ check_format_string (const_tree fntype, unsigned H } if (!ref - || !valid_stringptr_type_p (ref)) + || !valid_format_string_type_p (ref)) { if (!(flags & (int) ATTR_FLAG_BUILT_IN)) error ("format string argument is not a string type"); @@ -267,13 +267,21 @@ check_format_string (const_tree fntype, unsigned H gcc_unreachable (); } -/* Verify EXPR is a constant, and store its value. - If validated_p is true there should be no errors. +/* Under the control of FLAGS, verify EXPR is a valid constant that + refers to a positional argument ARGNO having a string type (char* + or, for targets like Darwin, a pointer to struct CFString) to + a function type FNTYPE declared with attribute ATNAME. + If valid, store the constant's integer value in *VALUE and return + the value. + If VALIDATED_P is true assert the validation is successful. Returns the converted constant value on success, null otherwise. */ + static tree get_constant (const_tree fntype, const_tree atname, tree expr, int argno, unsigned HOST_WIDE_INT *value, int flags, bool validated_p) { + /* Require the referenced argument to have a string type. For targets + like Darwin, also accept pointers to struct CFString. */ if (tree val = positional_argument (fntype, atname, expr, STRING_CST, argno, flags)) { Index: gcc/doc/extend.texi =================================================================== --- gcc/doc/extend.texi (revision 267616) +++ gcc/doc/extend.texi (working copy) @@ -22370,10 +22370,14 @@ bit-fields. See the Solaris man page for @code{cm @node Darwin Format Checks @subsection Darwin Format Checks -Darwin targets support the @code{CFString} (or @code{__CFString__}) in the format -attribute context. Declarations made with such attribution are parsed for correct syntax -and format argument types. However, parsing of the format string itself is currently undefined -and is not carried out by this version of the compiler. +In addition to the full set of format archetypes (attribute format style +arguments such as @code{printf}, @code{scanf}, @code{strftime}, and +@code{strfmon}), Darwin targets also support the @code{CFString} (or +@code{__CFString__}) archetype in the @code{format} attribute. +Declarations with this archetype are parsed for correct syntax +and argument types. However, parsing of the format string itself and +validating arguments against it in calls to such functions is currently +not performed. Additionally, @code{CFStringRefs} (defined by the @code{CoreFoundation} headers) may also be used as format arguments. Note that the relevant headers are only likely to be Index: gcc/testsuite/ChangeLog =================================================================== Index: gcc/testsuite/gcc.dg/darwin-cfstring-format-1.c =================================================================== --- gcc/testsuite/gcc.dg/darwin-cfstring-format-1.c (revision 267616) +++ gcc/testsuite/gcc.dg/darwin-cfstring-format-1.c (working copy) @@ -15,7 +15,7 @@ typedef const struct __CFString * CFStringRef; int s1 (CFStringRef fmt, ...) __attribute__((format(CFString, 1, 2))) ; /* OK */ int s2 (int a, CFStringRef fmt, ... ) __attribute__((format(__CFString__, 2, 3))) ; /* OK */ -int s2a (int a, CFStringRef fmt, ... ) __attribute__((format(CFString, 2, 2))) ; /* { dg-error "format string argument follows the args to be formatted" } */ +int s2a (int a, CFStringRef fmt, ... ) __attribute__((format(CFString, 2, 2))) ; /* { dg-error ".format. attribute argument 3 value .2. does not refer to a variable argument list" } */ int s3 (const char *fmt, ... ) __attribute__((format(__CFString__, 1, 2))) ; /* { dg-error "format argument should be a .CFString. reference but a string was found" } */ int s4 (CFStringRef fmt, ... ) __attribute__((format(printf, 1, 2))) ; /* { dg-error "found a .CFStringRef.* but the format argument should be a string" } */ @@ -23,7 +23,7 @@ int s4 (CFStringRef fmt, ... ) __attribute__((form char *s5 (char dum, char *fmt1, ... ) __attribute__((format_arg(2))) ; /* OK */ CFStringRef s6 (CFStringRef dum, CFStringRef fmt1, ... ) __attribute__((format_arg(2))) ; /* OK */ -char *s7 (int dum, void *fmt1, ... ) __attribute__((format_arg(2))) ; /* { dg-error "format string argument is not a string type" } */ +char *s7 (int dum, void *fmt1, ... ) __attribute__((format_arg(2))) ; /* { dg-error ".format_arg. attribute argument value .2. refers to parameter type .void \\\*." } */ int s8 (CFStringRef dum, CFStringRef fmt1, ... ) __attribute__((format_arg(2))) ; /* { dg-error "function does not return string type" } */ void foo (void) Index: gcc/testsuite/gcc.dg/format/attr-3.c =================================================================== --- gcc/testsuite/gcc.dg/format/attr-3.c (revision 267616) +++ gcc/testsuite/gcc.dg/format/attr-3.c (working copy) @@ -56,16 +56,16 @@ extern void fg3 () __attribute__((format(gnu_attr_ /* The format string argument must be a string type, and the arguments to be formatted must be the "...". */ -extern void fh0 (int, ...) __attribute__((format(gnu_attr_printf, 1, 2))); /* { dg-warning ".format. attribute argument 2 value .1. refers to parameter type .int." "format int string" } */ -extern void fh1 (signed char *, ...) __attribute__((format(gnu_attr_printf, 1, 2))); /* { dg-error "not a string" "signed char string" } */ -extern void fh2 (unsigned char *, ...) __attribute__((format(gnu_attr_printf, 1, 2))); /* { dg-error "not a string" "unsigned char string" } */ +extern void fh0 (int, ...) __attribute__((format(gnu_attr_printf, 1, 2))); /* { dg-error ".format. attribute argument 2 value .1. refers to parameter type .int." "format int string" } */ +extern void fh1 (signed char *, ...) __attribute__((format(gnu_attr_printf, 1, 2))); /* { dg-error ".format. attribute argument 2 value .1. refers to parameter type .signed char \\\*." "signed char string" } */ +extern void fh2 (unsigned char *, ...) __attribute__((format(gnu_attr_printf, 1, 2))); /* { dg-error ".format. attribute argument 2 value .1. refers to parameter type .unsigned char \\\*." "unsigned char string" } */ extern void fh3 (const char *, ...) __attribute__((format(gnu_attr_printf, 1, 3))); /* { dg-error "is not" "not ..." } */ extern void fh4 (const char *, int, ...) __attribute__((format(gnu_attr_printf, 1, 2))); /* { dg-error ".format. attribute argument 3 value .2. does not refer to a variable argument list" "not ..." } */ /* format_arg formats must take and return a string. */ -extern char *fi0 (int) __attribute__((format_arg(1))); /* { dg-warning ".format_arg. attribute argument value .1. refers to parameter type .int." } */ -extern char *fi1 (signed char *) __attribute__((format_arg(1))); /* { dg-error "not a string" "format_arg signed char string" } */ -extern char *fi2 (unsigned char *) __attribute__((format_arg(1))); /* { dg-error "not a string" "format_arg unsigned char string" } */ +extern char *fi0 (int) __attribute__((format_arg(1))); /* { dg-error ".format_arg. attribute argument value .1. refers to parameter type .int." } */ +extern char *fi1 (signed char *) __attribute__((format_arg(1))); /* { dg-error ".format_arg. attribute argument value .1. refers to parameter type .signed char \\\*." "format_arg signed char string" } */ +extern char *fi2 (unsigned char *) __attribute__((format_arg(1))); /* { dg-error ".format_arg. attribute argument value .1. refers to parameter type .unsigned char \\\*." "format_arg unsigned char string" } */ extern int fi3 (const char *) __attribute__((format_arg(1))); /* { dg-error "not return string" "format_arg ret int string" } */ extern signed char *fi4 (const char *) __attribute__((format_arg(1))); /* { dg-error "not return string" "format_arg ret signed char string" } */ extern unsigned char *fi5 (const char *) __attribute__((format_arg(1))); /* { dg-error "not return string" "format_arg ret unsigned char string" } */ Index: gcc/testsuite/gcc.dg/format/attr-8.c =================================================================== --- gcc/testsuite/gcc.dg/format/attr-8.c (nonexistent) +++ gcc/testsuite/gcc.dg/format/attr-8.c (working copy) @@ -0,0 +1,34 @@ +/* Test to verify that parameters of qualified narrow char pointer type + are accepted for attribute format printf, but others are rejected. + { dg-do compile } + { dg-options "-std=gnu99 -Wformat" } */ + +#define DONT_GNU_PROTOTYPE +#include "format.h" + +#define FORMAT(archetype, arg1, arg2) \ + __attribute__ ((format (archetype, arg1, arg2))) + +FORMAT (gnu_attr_printf, 1, 2) + void fpc_1_2 (char *, ...); + +FORMAT (gnu_attr_printf, 1, 2) + void fpcc_1_2 (const char *, ...); + +FORMAT (gnu_attr_printf, 1, 2) + void frpc_1_2 (char * restrict, ...); + +FORMAT (gnu_attr_printf, 1, 2) + void fpvc_1_2 (volatile char *, ...); + +FORMAT (gnu_attr_printf, 1, 2) + void fpcvc_1_2 (const volatile char *, ...); + +FORMAT (gnu_attr_printf, 1, 2) + void fpv_1_2 (void *, ...); /* { dg-error ".format. attribute argument 2 value .1. refers to parameter type .void \\\*." } */ + +FORMAT (gnu_attr_printf, 1, 2) + void fppc_1_2 (char **, ...); /* { dg-error ".format. attribute argument 2 value .1. refers to parameter type .char \\\*\\\*." } */ + +FORMAT (gnu_attr_printf, 1, 2) + void fpwc_1_2 (wchar_t *, ...); /* { dg-error ".format. attribute argument 2 value .1. refers to parameter type .wchar_t \\\*." } */ Index: gcc/testsuite/obj-c++.dg/fsf-nsstring-format-1.mm =================================================================== --- gcc/testsuite/obj-c++.dg/fsf-nsstring-format-1.mm (revision 267616) +++ gcc/testsuite/obj-c++.dg/fsf-nsstring-format-1.mm (working copy) @@ -28,7 +28,7 @@ int s1b (NSString *fmt, ...) __attribute__((format int s2 (int a, NSString *fmt, ... ) __attribute__((format(__NSString__, 2, 3))) ; /* OK */ -int s2a (int a, NSString *fmt, ... ) __attribute__((format(NSString, 2, 2))) ; /* { dg-error "format string argument follows the args to be formatted" } */ +int s2a (int a, NSString *fmt, ... ) __attribute__((format(NSString, 2, 2))) ; /* { dg-error ".format. attribute argument 3 value .2. does not refer to a variable argument list" } */ int s3 (const char *fmt, ... ) __attribute__((format(__NSString__, 1, 2))) ; /* { dg-error "format argument should be a .NSString. reference but a string was found" } */ int s4 (NSString *fmt, ... ) __attribute__((format(printf, 1, 2))) ; /* { dg-error "found a .NSString. reference but the format argument should be a string" } */ @@ -36,7 +36,7 @@ int s4 (NSString *fmt, ... ) __attribute__((format char *s5 (char dum, char *fmt1, ... ) __attribute__((format_arg(2))) ; /* OK */ NSString *s6 (NSString *dum, NSString *fmt1, ... ) __attribute__((format_arg(2))) ; /* OK */ -char *s7 (int dum, void *fmt1, ... ) __attribute__((format_arg(2))) ; /* { dg-error "format string argument is not a string type" } */ +char *s7 (int dum, void *fmt1, ... ) __attribute__((format_arg(2))) ; /* { dg-error ".format_arg. attribute argument value .2. refers to parameter type .void\\\*." } */ int s8 (NSString *dum, NSString *fmt1, ... ) __attribute__((format_arg(2))) ; /* { dg-error "function does not return string type" } */ char *s9 (int dum, char *fmt1, ... ) __attribute__((format_arg(2))) ; /* OK */ Index: gcc/testsuite/objc.dg/fsf-nsstring-format-1.m =================================================================== --- gcc/testsuite/objc.dg/fsf-nsstring-format-1.m (revision 267616) +++ gcc/testsuite/objc.dg/fsf-nsstring-format-1.m (working copy) @@ -21,7 +21,7 @@ int s1b (NSString *fmt, ...) __attribute__((format int s2 (int a, NSString *fmt, ... ) __attribute__((format(__NSString__, 2, 3))) ; /* OK */ -int s2a (int a, NSString *fmt, ... ) __attribute__((format(NSString, 2, 2))) ; /* { dg-error "format string argument follows the args to be formatted" } */ +int s2a (int a, NSString *fmt, ... ) __attribute__((format(NSString, 2, 2))) ; /* { dg-error ".format. attribute argument 3 value .2. does not refer to a variable argument list" } */ int s3 (const char *fmt, ... ) __attribute__((format(__NSString__, 1, 2))) ; /* { dg-error "format argument should be a .NSString. reference but a string was found" } */ int s4 (NSString *fmt, ... ) __attribute__((format(printf, 1, 2))) ; /* { dg-error "found a .NSString. reference but the format argument should be a string" } */ @@ -29,7 +29,7 @@ int s4 (NSString *fmt, ... ) __attribute__((format char *s5 (char dum, char *fmt1, ... ) __attribute__((format_arg(2))) ; /* OK */ NSString *s6 (NSString *dum, NSString *fmt1, ... ) __attribute__((format_arg(2))) ; /* OK */ -char *s7 (int dum, void *fmt1, ... ) __attribute__((format_arg(2))) ; /* { dg-error "format string argument is not a string type" } */ +char *s7 (int dum, void *fmt1, ... ) __attribute__((format_arg(2))) ; /* { dg-error ".format_arg. attribute argument value .2. refers to parameter type .void \\\*." } */ int s8 (NSString *dum, NSString *fmt1, ... ) __attribute__((format_arg(2))) ; /* { dg-error "function does not return string type" } */ char *s9 (int dum, char *fmt1, ... ) __attribute__((format_arg(2))) ; /* OK */