@@ -977,6 +977,7 @@ extern void c_parse_error (const char *,
extern void objc_write_global_declarations (void);
extern tree objc_is_class_name (tree);
extern tree objc_is_object_ptr (tree);
+extern bool objc_is_string_type (tree);
extern void objc_check_decl (tree);
extern void objc_check_global_decl (tree);
extern tree objc_common_type (tree, tree);
@@ -1001,6 +1002,7 @@ extern tree objc_build_selector_expr (lo
extern tree objc_build_protocol_expr (tree);
extern tree objc_build_encode_expr (tree);
extern tree objc_build_string_object (tree);
+extern tree objc_maybe_extract_string_cst_from_objc_string (tree);
extern tree objc_get_protocol_qualified_type (tree, tree);
extern tree objc_get_class_reference (tree);
extern tree objc_get_class_ivars (tree);
@@ -63,6 +63,7 @@ enum format_type { printf_format_type, a
gcc_diag_format_type, gcc_tdiag_format_type,
gcc_cdiag_format_type,
gcc_cxxdiag_format_type, gcc_gfc_format_type,
+ nsstring_format_type,
format_type_error = -1};
typedef struct function_format_info
@@ -77,12 +78,20 @@ static int decode_format_type (const cha
static bool check_format_string (tree argument,
unsigned HOST_WIDE_INT format_num,
- int flags, bool *no_add_attrs);
+ int flags, bool *no_add_attrs, bool objc_string);
static bool get_constant (tree expr, unsigned HOST_WIDE_INT *value,
int validated_p);
static const char *convert_format_name_to_system_name (const char *attr_name);
static bool cmp_attribs (const char *tattr_name, const char *attr_name);
+/* Check that 'type' is a C string type. */
+static bool
+is_string_type (tree type)
+{
+ return (TREE_CODE (type) == POINTER_TYPE
+ && (TYPE_MAIN_VARIANT (TREE_TYPE (type)) == char_type_node));
+}
+
/* Handle a "format_arg" attribute; arguments as in
struct attribute_spec.handler. */
tree
@@ -101,16 +110,16 @@ handle_format_arg_attribute (tree *node,
return NULL_TREE;
}
+ /* TODO: Support for nsstring_format_type. */
+
argument = TYPE_ARG_TYPES (type);
if (argument)
{
- if (!check_format_string (argument, format_num, flags, no_add_attrs))
+ if (!check_format_string (argument, format_num, flags, no_add_attrs, false))
return NULL_TREE;
}
- if (TREE_CODE (TREE_TYPE (type)) != POINTER_TYPE
- || (TYPE_MAIN_VARIANT (TREE_TYPE (TREE_TYPE (type)))
- != char_type_node))
+ if (!is_string_type (TREE_TYPE (type)))
{
if (!(flags & (int) ATTR_FLAG_BUILT_IN))
error ("function does not return string type");
@@ -125,7 +134,7 @@ handle_format_arg_attribute (tree *node,
the format attribute is in error. */
static bool
check_format_string (tree argument, unsigned HOST_WIDE_INT format_num,
- int flags, bool *no_add_attrs)
+ int flags, bool *no_add_attrs, bool objc_string)
{
unsigned HOST_WIDE_INT i;
@@ -136,15 +145,25 @@ check_format_string (tree argument, unsi
argument = TREE_CHAIN (argument);
}
- if (!argument
- || TREE_CODE (TREE_VALUE (argument)) != POINTER_TYPE
- || (TYPE_MAIN_VARIANT (TREE_TYPE (TREE_VALUE (argument)))
- != char_type_node))
+ if (!objc_string)
{
- if (!(flags & (int) ATTR_FLAG_BUILT_IN))
- error ("format string argument not a string type");
- *no_add_attrs = true;
- return false;
+ if (!argument || !is_string_type (TREE_VALUE (argument)))
+ {
+ if (!(flags & (int) ATTR_FLAG_BUILT_IN))
+ error ("format string argument not a string type");
+ *no_add_attrs = true;
+ return false;
+ }
+ }
+ else
+ {
+ if (!argument || !objc_is_string_type (TREE_VALUE (argument)))
+ {
+ if (!(flags & (int) ATTR_FLAG_BUILT_IN))
+ error ("format string argument not an Objective-C string type");
+ *no_add_attrs = true;
+ return false;
+ }
}
return true;
@@ -273,6 +292,9 @@ typedef struct format_wanted_type
/* Whether the argument, dereferenced once, is read from and so
must not be a NULL pointer. */
int reading_from_flag;
+ /* Whether the argument must be an Objective-C object pointer type
+ and we need to use objc_is_object_ptr() to check it. */
+ int objc_object_flag;
/* If warnings should be of the form "field precision should have
type 'int'", the name to use (in this case "field precision"),
otherwise NULL, for "format expects type 'long'" type
@@ -358,6 +380,8 @@ static const format_length_info gcc_gfc_
{ NO_FMT, NO_FMT, 0 }
};
+/* 'NSString' format is identical to printf for these. */
+#define nsstring_length_specs printf_length_specs
static const format_flag_spec printf_flag_specs[] =
{
@@ -418,6 +442,8 @@ static const format_flag_pair gcc_gfc_fl
{ 0, 0, 0, 0 }
};
+#define nsstring_flag_pairs printf_flag_pairs
+
static const format_flag_spec gcc_diag_flag_specs[] =
{
{ '+', 0, 0, N_("'+' flag"), N_("the '+' printf flag"), STD_C89 },
@@ -432,6 +458,8 @@ static const format_flag_spec gcc_diag_f
#define gcc_cdiag_flag_specs gcc_diag_flag_specs
#define gcc_cxxdiag_flag_specs gcc_diag_flag_specs
+#define nsstring_flag_specs printf_flag_specs
+
static const format_flag_spec scanf_flag_specs[] =
{
{ '*', 0, 0, N_("assignment suppression"), N_("the assignment suppression scanf feature"), STD_C89 },
@@ -651,6 +679,32 @@ static const format_char_info gcc_gfc_ch
{ NULL, 0, STD_C89, NOLENGTHS, NULL, NULL, NULL }
};
+/* This is identical to the printf one, except for the addition of %@, used for Objective-C objects. */
+static const format_char_info nsstring_char_table[] =
+{
+ /* C89 conversion specifiers. */
+ { "di", 0, STD_C89, { T89_I, T99_SC, T89_S, T89_L, T9L_LL, TEX_LL, T99_SST, T99_PD, T99_IM, BADLEN, BADLEN, BADLEN }, "-wp0 +'I", "i", NULL },
+ { "oxX", 0, STD_C89, { T89_UI, T99_UC, T89_US, T89_UL, T9L_ULL, TEX_ULL, T99_ST, T99_UPD, T99_UIM, BADLEN, BADLEN, BADLEN }, "-wp0#", "i", NULL },
+ { "u", 0, STD_C89, { T89_UI, T99_UC, T89_US, T89_UL, T9L_ULL, TEX_ULL, T99_ST, T99_UPD, T99_UIM, BADLEN, BADLEN, BADLEN }, "-wp0'I", "i", NULL },
+ { "fgG", 0, STD_C89, { T89_D, BADLEN, BADLEN, T99_D, BADLEN, T89_LD, BADLEN, BADLEN, BADLEN, TEX_D32, TEX_D64, TEX_D128 }, "-wp0 +#'I", "", NULL },
+ { "eE", 0, STD_C89, { T89_D, BADLEN, BADLEN, T99_D, BADLEN, T89_LD, BADLEN, BADLEN, BADLEN, TEX_D32, TEX_D64, TEX_D128 }, "-wp0 +#I", "", NULL },
+ { "c", 0, STD_C89, { T89_I, BADLEN, BADLEN, T94_WI, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN }, "-w", "", NULL },
+ { "s", 1, STD_C89, { T89_C, BADLEN, BADLEN, T94_W, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN }, "-wp", "cR", NULL },
+ { "p", 1, STD_C89, { T89_V, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN }, "-w", "c", NULL },
+ { "n", 1, STD_C89, { T89_I, T99_SC, T89_S, T89_L, T9L_LL, BADLEN, T99_SST, T99_PD, T99_IM, BADLEN, BADLEN, BADLEN }, "", "W", NULL },
+ /* C99 conversion specifiers. */
+ { "F", 0, STD_C99, { T99_D, BADLEN, BADLEN, T99_D, BADLEN, T99_LD, BADLEN, BADLEN, BADLEN, TEX_D32, TEX_D64, TEX_D128 }, "-wp0 +#'I", "", NULL },
+ { "aA", 0, STD_C99, { T99_D, BADLEN, BADLEN, T99_D, BADLEN, T99_LD, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN }, "-wp0 +#", "", NULL },
+ /* X/Open conversion specifiers. */
+ { "C", 0, STD_EXT, { TEX_WI, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN }, "-w", "", NULL },
+ { "S", 1, STD_EXT, { TEX_W, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN }, "-wp", "R", NULL },
+ /* GNU conversion specifiers. */
+ { "m", 0, STD_EXT, { T89_V, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN }, "-wp", "", NULL },
+ /* Objective-C conversion specfier. */
+ { "@", 1, STD_C89, { T89_AT, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN }, "-wp", "@", NULL },
+ { NULL, 0, STD_C89, NOLENGTHS, NULL, NULL, NULL }
+};
+
static const format_char_info scan_char_table[] =
{
/* C89 conversion specifiers. */
@@ -750,6 +804,12 @@ static const format_kind_info format_typ
0, 0, 0, 0, 0, 0,
NULL, NULL
},
+ { "NSString", nsstring_length_specs, nsstring_char_table, "+#0-I", NULL,
+ nsstring_flag_specs, nsstring_flag_pairs,
+ FMT_FLAG_ARG_CONVERT|FMT_FLAG_DOLLAR_MULTIPLE|FMT_FLAG_USE_DOLLAR|FMT_FLAG_EMPTY_PREC_OK,
+ 'w', 0, 'p', 0, 'L', 0,
+ &integer_type_node, &integer_type_node
+ },
{ "gnu_scanf", scanf_length_specs, scan_char_table, "*'I", NULL,
scanf_flag_specs, scanf_flag_pairs,
FMT_FLAG_ARG_CONVERT|FMT_FLAG_SCANF_A_KLUDGE|FMT_FLAG_USE_DOLLAR|FMT_FLAG_ZERO_WIDTH_BAD|FMT_FLAG_DOLLAR_GAP_POINTER_OK,
@@ -1364,16 +1424,31 @@ check_format_arg (void *ctx, tree format
array_size = DECL_SIZE_UNIT (format_tree);
format_tree = array_init;
}
+
+ /* nsstring_format_type is special because the format string is an
+ Objective-C string. See if we can extract the constant
+ string. */
+ if (info->format_type == nsstring_format_type)
+ format_tree = objc_maybe_extract_string_cst_from_objc_string (format_tree);
+
if (TREE_CODE (format_tree) != STRING_CST)
{
res->number_non_literal++;
return;
}
- if (TYPE_MAIN_VARIANT (TREE_TYPE (TREE_TYPE (format_tree))) != char_type_node)
+ /* FIXME: The STRING_CST associated with Objective-C constant
+ strings is most definitely a traditional ASCII string, yet it
+ seems not to be marked with a char_type_node. Until that is
+ fixed, do not perform this test for Objective-C strings. */
+ if (info->format_type != nsstring_format_type)
{
- res->number_wide++;
- return;
+ if (TYPE_MAIN_VARIANT (TREE_TYPE (TREE_TYPE (format_tree))) != char_type_node)
+ {
+ res->number_wide++;
+ return;
+ }
}
+
format_chars = TREE_STRING_POINTER (format_tree);
format_length = TREE_STRING_LENGTH (format_tree);
if (array_size != 0)
@@ -2081,6 +2156,8 @@ check_format_info_main (format_check_res
wanted_type_ptr->char_lenient_flag = 0;
if (strchr (fci->flags2, 'c') != 0)
wanted_type_ptr->char_lenient_flag = 1;
+ if (strchr (fci->flags2, '@') != 0)
+ wanted_type_ptr->objc_object_flag = 1;
wanted_type_ptr->scalar_identity_flag = 0;
if (scalar_identity_flag)
wanted_type_ptr->scalar_identity_flag = 1;
@@ -2241,6 +2318,10 @@ check_format_types (format_wanted_type *
|| cur_type == signed_char_type_node
|| cur_type == unsigned_char_type_node);
+ /* Check for Objective-C objects. */
+ if (types->objc_object_flag && objc_is_object_ptr (cur_type))
+ continue;
+
/* Check the type of the "real" argument, if there's a type we want. */
if (lang_hooks.types_compatible_p (wanted_type, cur_type))
continue;
@@ -2798,8 +2879,16 @@ handle_format_attribute (tree *node, tre
argument = TYPE_ARG_TYPES (type);
if (argument)
{
+ bool objc_string = false;
+
+ /* In Objective-C, the special NSString format type is allowed,
+ where the format string is an Objective-C string instead of
+ a C string. */
+ if (c_dialect_objc () && info.format_type == nsstring_format_type)
+ objc_string = true;
+
if (!check_format_string (argument, info.format_num, flags,
- no_add_attrs))
+ no_add_attrs, objc_string))
return NULL_TREE;
if (info.first_arg_num != 0)
@@ -142,14 +142,16 @@ typedef struct format_char_info
modifiers. */
const char *flag_chars;
/* List of additional flags describing these conversion specifiers.
- "c" for generic character pointers being allowed, "2" for strftime
- two digit year formats, "3" for strftime formats giving two digit
- years in some locales, "4" for "2" which becomes "3" with an "E" modifier,
- "o" if use of strftime "O" is a GNU extension beyond C99,
- "W" if the argument is a pointer which is dereferenced and written into,
- "R" if the argument is a pointer which is dereferenced and read from,
- "i" for printf integer formats where the '0' flag is ignored with
- precision, and "[" for the starting character of a scanf scanset. */
+ "c" for generic character pointers being allowed, "2" for
+ strftime two digit year formats, "3" for strftime formats giving
+ two digit years in some locales, "4" for "2" which becomes "3"
+ with an "E" modifier, "o" if use of strftime "O" is a GNU
+ extension beyond C99, "W" if the argument is a pointer which is
+ dereferenced and written into, "R" if the argument is a pointer
+ which is dereferenced and read from, "i" for printf integer
+ formats where the '0' flag is ignored with precision, "[" for the
+ starting character of a scanf scanset and "@" for Objective-C
+ objects being allowed. */
const char *flags2;
/* If this format conversion character consumes more than one argument,
CHAIN points to information about the next argument. For later
@@ -286,6 +288,9 @@ typedef struct
#define T99_UC { STD_C99, NULL, T_UC }
#define T_V &void_type_node
#define T89_V { STD_C89, NULL, T_V }
+/* TODO: In the following, it would be better to print 'id' but we
+ need to avoid the '*' for the pointer. */
+#define T89_AT { STD_C89, "struct object", T_V }
#define T_W &wchar_type_node
#define T94_W { STD_C94, "wchar_t", T_W }
#define TEX_W { STD_EXT, "wchar_t", T_W }
@@ -1,5 +1,29 @@
2010-10-31 Nicola Pero <nicola.pero@meta-innovation.com>
+ * c-common.h (objc_is_string_type): New.
+ (objc_maybe_extract_string_cst_from_objc_string): New.
+ * stub-objc.c (objc_is_string_type): New.
+ (objc_maybe_extract_string_cst_from_objc_string): New.
+ * c-format.c (is_string_type): New.
+ (handle_format_arg_attribute): Updated call to
+ check_format_string. Use is_string_type.
+ (check_format_string): Added objc_string argument. If set, check
+ that the format string is an Objective-C string.
+ (handle_format_attribute): For nsstring_format_type, check that
+ format strings are Objective-C strings.
+ (struct format_wanted_type): Added objc_object_flag.
+ (nsstring_length_specs): New.
+ (nsstring_flag_pairs): New.
+ (nsstring_flag_specs): New.
+ (nsstring_char_table): New.
+ (check_format_arg): Implemented nsstring_format_type.
+ (check_format_info_main): Implemented '@' as valid flag2.
+ (check_format_types): Implemented objc_object_flag.
+ * c-format.h: Updated comments.
+ (T89_AT): New.
+
+2010-10-31 Nicola Pero <nicola.pero@meta-innovation.com>
+
Implemented format and noreturn attributes for Objective-C methods.
* c-common.c (handle_noreturn_attribute): Recognize 'noreturn'
attribute for Objective-C methods.
@@ -44,6 +44,12 @@ objc_is_object_ptr (tree ARG_UNUSED (arg
return 0;
}
+bool
+objc_is_string_type (tree ARG_UNUSED (arg))
+{
+ return 0;
+}
+
bool objc_diagnose_private_ivar (tree ARG_UNUSED (arg))
{
return false;
@@ -281,6 +287,12 @@ objc_build_string_object (tree ARG_UNUSE
}
tree
+objc_maybe_extract_string_cst_from_objc_string (tree ARG_UNUSED (str))
+{
+ return 0;
+}
+
+tree
objc_get_class_reference (tree ARG_UNUSED (name))
{
return 0;
@@ -1,5 +1,10 @@
2010-10-31 Nicola Pero <nicola.pero@meta-innovation.com>
+ * objc-act.c (objc_is_string_type): New.
+ (objc_maybe_extract_string_cst_from_objc_string): New.
+
+2010-10-31 Nicola Pero <nicola.pero@meta-innovation.com>
+
Implemented format and noreturn attributes for Objective-C methods.
* objc-act.c (objc_start_method_definition): If method attributes
are specified emit a warning and ignore them.
@@ -2611,6 +2611,61 @@ objc_build_string_object (tree string)
return addr;
}
+/* This is the inversion of objc_build_string_object: examine
+ 'objc_string', and if it is an Objective-C constant string created
+ by objc_build_string_object, extract the C string embedded into it
+ and return it as a STRING_CST node. Return 'objc_string' if
+ 'objc_string' is not an Objective-C constant string, or if we could
+ not extract the STRING_CST. */
+tree
+objc_maybe_extract_string_cst_from_objc_string (tree objc_string)
+{
+ tree constructor, expr;
+ VEC(constructor_elt,gc) *elts;
+
+ /* This is a simple implementation. */
+ /* TODO: Better error checking. */
+ if (TREE_CODE (objc_string) != VAR_DECL
+ || TREE_CODE (TREE_TYPE (objc_string)) != RECORD_TYPE)
+ return objc_string;
+
+ constructor = DECL_INITIAL (objc_string);
+
+ if (!TREE_READONLY (constructor)
+ || !TREE_CONSTANT (constructor)
+ || !TREE_STATIC (constructor))
+ return objc_string;
+
+ if (TREE_CODE (constructor) != CONSTRUCTOR)
+ return objc_string;
+
+ /* Get the second field (the first one is 'isa', the second one is
+ the constant string, the third one is the length. */
+ elts = CONSTRUCTOR_ELTS (constructor);
+ if (VEC_length (constructor_elt, elts) != 3)
+ return objc_string;
+
+ expr = VEC_index (constructor_elt, elts, 1)->value;
+
+ if (!expr || TREE_CODE (expr) != ADDR_EXPR)
+ {
+ printf ("expr not ADDR_EXPR\n");
+ debug_tree (expr);
+ return objc_string;
+ }
+
+ expr = TREE_OPERAND (expr, 0);
+
+ if (!expr || TREE_CODE (expr) != STRING_CST)
+ {
+ printf ("expr not STRING_CST\n");
+ debug_tree (expr);
+ return objc_string;
+ }
+
+ return expr;
+}
+
/* Declare a static instance of CLASS_DECL initialized by CONSTRUCTOR. */
static GTY(()) int num_static_inst;
@@ -3667,6 +3722,55 @@ objc_is_object_ptr (tree type)
return ret;
}
+/* Check whether TYPE is an Objective-C string class; at the moment,
+ this means NSString, NSMutableString or the constant string class.
+ This is used to check that a function or method marked with
+ __attribute__ ((format (NSString, 1, 2))) has the correct prototype
+ with an (NSString *) argument for the format string. */
+
+bool
+objc_is_string_type (tree type)
+{
+ tree objc_type_name;
+
+ /* First, determine that it looks roughly like an Objective-C object
+ type. */
+ type = TYPE_MAIN_VARIANT (type);
+ if (!POINTER_TYPE_P (type))
+ return false;
+
+ /* We determined it is a pointer; get what it points to. */
+ type = TREE_TYPE (type);
+
+ /* It should point to a RECORD_TYPE (the Class). */
+ if (TREE_CODE (type) != RECORD_TYPE)
+ return false;
+
+ /* Now, try to determine if it is an Objective-C string type. A
+ careful check (TODO) would look at the class hierarchy and accept
+ any Objective-C class that inherits from NSString. In practice,
+ this function is used to check the string format argument in an
+ NSString format type, which is practically always NSString, so we
+ can just check that the class name is NSString. To be a bit more
+ flexible, we also accept NSMutableString and the constant string
+ class name. */
+ objc_type_name = OBJC_TYPE_NAME (type);
+
+ if (objc_type_name && TREE_CODE (objc_type_name) == IDENTIFIER_NODE)
+ {
+ const char * name = IDENTIFIER_POINTER (objc_type_name);
+ if (TYPE_HAS_OBJC_INFO (type) && TYPE_OBJC_INTERFACE (type))
+ {
+ if (strcmp (name, constant_string_class_name) == 0
+ || strncmp (name, "NSString", 8) == 0
+ || strncmp (name, "NSMutableString", 15) == 0)
+ return true;
+ }
+ }
+
+ return false;
+}
+
static int
objc_is_gcable_type (tree type, int or_strong_p)
{
@@ -1,4 +1,8 @@
2010-10-31 Nicola Pero <nicola.pero@meta-innovation.com>
+
+ * objc.dg/attributes/method-format-2.m: New.
+
+2010-10-31 Nicola Pero <nicola.pero@meta-innovation.com>
Implemented format and noreturn attributes for Objective-C methods.
* objc.dg/attributes/method-attribute-2.m: Updated warnings.
@@ -0,0 +1,59 @@
+/* Contributed by Nicola Pero <nicola.pero@meta-innovation.com>, October 2010. */
+/* { dg-do compile } */
+/* { dg-options "-Wall -fconstant-string-class=MyString" } */
+
+#include <objc/objc.h>
+#include <stdlib.h>
+
+@interface MyString
+{
+@public
+ Class isa;
+ char *c_string;
+ unsigned int len;
+}
+@end
+
+@implementation MyString
+@end
+
+@interface LogObject
+{
+ Class isa;
+}
++ (void) log: (int)level message: (MyString *) my_format, ... __attribute__ ((format (NSString, 2, 3)));
+- (void) log: (int)level message: (MyString *) my_format, ... __attribute__ ((format (NSString, 2, 3)));
+
++ (void) debug: (MyString *) my_format, ... __attribute__ ((format (NSString, 1, 2)));
+- (void) debug: (MyString *) my_format, ... __attribute__ ((format (NSString, 1, 2)));
+
+/* Just make sure a missing or invalid attribute won't crash the compiler. */
+- (void) log2: (int)level message: (MyString *) my_format, ... __attribute__ ((format (NSString, 2))); /* { dg-error "wrong" } */
+- (void) log3: (int)level message: (const char *) my_format, ... __attribute__ ((format (NSString, 2, 3))); /* { dg-error "not an Objective.C string type" } */
+- (void) log4: (int)level message: (LogObject *) my_format, ... __attribute__ ((format (NSString, 2, 3))); /* { dg-error "not an Objective.C string type" } */
++ (void) debug2: (MyString *) my_format, ... __attribute__ ((format (NSString))); /* { dg-error "wrong" } */
+- (void) debug2: (MyString *) my_format, ... __attribute__ ((format (NSString))); /* { dg-error "wrong" } */
++ (void) alert: (MyString *) my_format __attribute__ ((format (NSString, 1, 2))); /* { dg-error "args to be formatted is not ..." } */
+- (void) alert: (MyString *) my_format __attribute__ ((format (NSString, 1, 2))); /* { dg-error "args to be formatted is not ..." } */
+@end
+
+void test (LogObject *object)
+{
+ [object log: 2 message: @"attribute only applies to variadic functions"];
+ [object log: 2 message: @"%d: attribute %@ only applies to variadic functions", 23, @"'format'"];
+ [object log: 2 message: @"%d: attribute %@ only applies to variadic functions", 23]; /* { dg-warning "too few arguments for format" } */
+ [object log: 2 message: @"%d: attribute %@ only applies to variadic functions", @"'format'", 23]; /* { dg-warning "format ..@. expects" } */
+
+ [object debug: @"attribute only applies to variadic functions"];
+ [object debug: @"%d:%d: attribute %@ only applies to variadic functions", 23, 11, @"'format'"];
+ [object debug: @"%d:%d: attribute %@ only applies to variadic functions", 23, @"'format'"]; /* { dg-warning "too few arguments for format" } */
+ [object debug: @"%d:%d: attribute %@ only applies to variadic functions", 23, @"'format'", 11]; /* { dg-warning "format ..@. expects" } */
+
+ [LogObject log: 2 message: @"attribute only applies to variadic functions"];
+ [LogObject log: 2 message: @"attribute %@ only applies to variadic functions", @"'format'"];
+ [LogObject log: 2 message: @"attribute %@ only applies to variadic functions"]; /* { dg-warning "too few arguments for format" } */
+
+ [LogObject debug: @"attribute only applies to variadic functions"];
+ [LogObject debug: @"attribute %@ only applies to variadic functions", @"'format'"];
+ [LogObject debug: @"attribute %@ only applies to variadic functions"]; /* { dg-warning "too few arguments for format" } */
+}