From 0161e8dace9f35fba0a938a803fc4c55e675dcb9 Mon Sep 17 00:00:00 2001 From: Richard Kenner Date: Sat, 8 Jun 1996 13:35:57 -0400 Subject: [PATCH] (enum attrs): Add A_FORMAT_ARG. (init_attribute): Initialize it. (decl_attributes, case A_FORMAT): Clean up error messages. (decl_attributes, case A_FORMAT_ARG): New case. (struct international_format_info): New structure and typedef. (international_format_list): New variable. (record_international_format): New function. (init_format_info): Call it for gettext, dcgettext, and dcgettext. (check_format_info): See if format arg is call to internationalization function. From-SVN: r12241 --- gcc/c-common.c | 173 +++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 167 insertions(+), 6 deletions(-) diff --git a/gcc/c-common.c b/gcc/c-common.c index 3f6b28e36d8..04c1ef3522a 100644 --- a/gcc/c-common.c +++ b/gcc/c-common.c @@ -39,12 +39,13 @@ extern struct obstack permanent_obstack; enum attrs {A_PACKED, A_NOCOMMON, A_COMMON, A_NORETURN, A_CONST, A_T_UNION, A_CONSTRUCTOR, A_DESTRUCTOR, A_MODE, A_SECTION, A_ALIGNED, - A_UNUSED, A_FORMAT, A_WEAK, A_ALIAS}; + A_UNUSED, A_FORMAT, A_FORMAT_ARG, A_WEAK, A_ALIAS}; static void declare_hidden_char_array PROTO((char *, char *)); static void add_attribute PROTO((enum attrs, char *, int, int, int)); static void init_attributes PROTO((void)); +static void record_international_format PROTO((tree, tree, int)); /* Make bindings for __FUNCTION__ and __PRETTY_FUNCTION__. */ @@ -275,6 +276,7 @@ init_attributes () add_attribute (A_SECTION, "section", 1, 1, 1); add_attribute (A_ALIGNED, "aligned", 0, 1, 0); add_attribute (A_FORMAT, "format", 3, 3, 1); + add_attribute (A_FORMAT_ARG, "format_arg", 1, 1, 1); add_attribute (A_WEAK, "weak", 0, 0, 1); add_attribute (A_ALIAS, "alias", 1, 1, 1); } @@ -580,9 +582,15 @@ decl_attributes (node, attributes, prefix_attributes) || !strcmp (IDENTIFIER_POINTER (format_type), "__scanf__"))) is_scan = 1; + else if (TREE_CODE (format_type) == IDENTIFIER_NODE) + { + error ("`%s' is an unrecognized format function type", + IDENTIFIER_POINTER (format_type)); + continue; + } else { - error ("unrecognized format specifier for `%s'"); + error ("unrecognized format specifier"); continue; } @@ -653,6 +661,70 @@ decl_attributes (node, attributes, prefix_attributes) break; } + case A_FORMAT_ARG: + { + tree format_num_expr = TREE_VALUE (args); + int format_num, arg_num; + tree argument; + + if (TREE_CODE (decl) != FUNCTION_DECL) + { + error_with_decl (decl, + "argument format specified for non-function `%s'"); + continue; + } + + /* Strip any conversions from the first arg number and verify it + is a constant. */ + while (TREE_CODE (format_num_expr) == NOP_EXPR + || TREE_CODE (format_num_expr) == CONVERT_EXPR + || TREE_CODE (format_num_expr) == NON_LVALUE_EXPR) + format_num_expr = TREE_OPERAND (format_num_expr, 0); + + if (TREE_CODE (format_num_expr) != INTEGER_CST) + { + error ("format string has non-constant operand number"); + continue; + } + + format_num = TREE_INT_CST_LOW (format_num_expr); + + /* If a parameter list is specified, verify that the format_num + argument is actually a string, in case the format attribute + is in error. */ + argument = TYPE_ARG_TYPES (type); + if (argument) + { + for (arg_num = 1; ; ++arg_num) + { + if (argument == 0 || arg_num == format_num) + break; + argument = TREE_CHAIN (argument); + } + if (! argument + || TREE_CODE (TREE_VALUE (argument)) != POINTER_TYPE + || (TYPE_MAIN_VARIANT (TREE_TYPE (TREE_VALUE (argument))) + != char_type_node)) + { + error ("format string arg not a string type"); + continue; + } + } + + if (TREE_CODE (TREE_TYPE (TREE_TYPE (decl))) != POINTER_TYPE + || (TYPE_MAIN_VARIANT (TREE_TYPE (TREE_TYPE (TREE_TYPE (decl)))) + != char_type_node)) + { + error ("function does not return string type"); + continue; + } + + record_international_format (DECL_NAME (decl), + DECL_ASSEMBLER_NAME (decl), + format_num); + break; + } + case A_WEAK: declare_weak (decl); break; @@ -749,7 +821,8 @@ static format_char_info scan_char_table[] = { { NULL } }; -typedef struct function_format_info { +typedef struct function_format_info +{ struct function_format_info *next; /* next structure on the list */ tree name; /* identifier such as "printf" */ tree assembler_name; /* optional mangled identifier (for C++) */ @@ -760,14 +833,27 @@ typedef struct function_format_info { static function_format_info *function_format_list = NULL; -static void check_format_info PROTO((function_format_info *, tree)); +typedef struct international_format_info +{ + struct international_format_info *next; /* next structure on the list */ + tree name; /* identifier such as "gettext" */ + tree assembler_name; /* optional mangled identifier (for C++) */ + int format_num; /* number of format argument */ +} international_format_info; + +static international_format_info *international_format_list = NULL; + +static void check_format_info PROTO((function_format_info *, tree)); /* Initialize the table of functions to perform format checking on. The ANSI functions are always checked (whether is included or not), since it is common to call printf without including . There shouldn't be a problem with this, since ANSI reserves these function names whether you include the - header file or not. In any case, the checking is harmless. */ + header file or not. In any case, the checking is harmless. + + Also initialize the name of function that modify the format string for + internationalization purposes. */ void init_function_format_info () @@ -781,6 +867,10 @@ init_function_format_info () record_function_format (get_identifier ("vprintf"), NULL_TREE, 0, 1, 0); record_function_format (get_identifier ("vfprintf"), NULL_TREE, 0, 2, 0); record_function_format (get_identifier ("vsprintf"), NULL_TREE, 0, 2, 0); + + record_international_format (get_identifier ("gettext"), NULL_TREE, 1); + record_international_format (get_identifier ("dgettext"), NULL_TREE, 2); + record_international_format (get_identifier ("dcgettext"), NULL_TREE, 2); } /* Record information for argument format checking. FUNCTION_IDENT is @@ -789,7 +879,7 @@ init_function_format_info () false indicates printf-style format checking. FORMAT_NUM is the number of the argument which is the format control string (starting from 1). FIRST_ARG_NUM is the number of the first actual argument to check - against teh format string, or zero if no checking is not be done + against the format string, or zero if no checking is not be done (e.g. for varargs such as vfprintf). */ void @@ -825,6 +915,43 @@ record_function_format (name, assembler_name, is_scan, info->first_arg_num = first_arg_num; } +/* Record information for the names of function that modify the format + argument to format functions. FUNCTION_IDENT is the identifier node for + the name of the function (its decl need not exist yet) and FORMAT_NUM is + the number of the argument which is the format control string (starting + from 1). */ + +void +record_international_format (name, assembler_name, format_num) + tree name; + tree assembler_name; + int format_num; +{ + international_format_info *info; + + /* Re-use existing structure if it's there. */ + + for (info = international_format_list; info; info = info->next) + { + if (info->name == name && info->assembler_name == assembler_name) + break; + } + + if (! info) + { + info + = (international_format_info *) + xmalloc (sizeof (international_format_info)); + info->next = international_format_list; + international_format_list = info; + + info->name = name; + info->assembler_name = assembler_name; + } + + info->format_num = format_num; +} + static char tfaff[] = "too few arguments for format"; /* Check the argument list of a call to printf, scanf, etc. @@ -895,9 +1022,43 @@ check_format_info (info, params) params = TREE_CHAIN (params); if (format_tree == 0) return; + /* We can only check the format if it's a string constant. */ while (TREE_CODE (format_tree) == NOP_EXPR) format_tree = TREE_OPERAND (format_tree, 0); /* strip coercion */ + + if (TREE_CODE (format_tree) == CALL_EXPR + && TREE_CODE (TREE_OPERAND (format_tree, 0)) == ADDR_EXPR + && (TREE_CODE (TREE_OPERAND (TREE_OPERAND (format_tree, 0), 0)) + == FUNCTION_DECL)) + { + tree function = TREE_OPERAND (TREE_OPERAND (format_tree, 0), 0); + + /* See if this is a call to a known internationalization function + that modifies the format arg. */ + international_format_info *info; + + for (info = international_format_list; info; info = info->next) + if (info->assembler_name + ? (info->assembler_name == DECL_ASSEMBLER_NAME (function)) + : (info->name == DECL_NAME (function))) + { + tree inner_args; + int i; + + for (inner_args = TREE_OPERAND (format_tree, 1), i = 1; + inner_args != 0; + inner_args = TREE_CHAIN (inner_args), i++) + if (i == info->format_num) + { + format_tree = TREE_VALUE (inner_args); + + while (TREE_CODE (format_tree) == NOP_EXPR) + format_tree = TREE_OPERAND (format_tree, 0); + } + } + } + if (integer_zerop (format_tree)) { warning ("null format string");