From 67569659de101e005c876dbf18ae1a54a97c917e Mon Sep 17 00:00:00 2001 From: Michael Beck Date: Tue, 18 Mar 2008 15:35:04 +0000 Subject: [PATCH] Mostly finished parsing of GNU attributes. [r18981] --- parser.c | 306 ++++++++++++++++++++++++++++------------- parsetest/attributes.c | 18 ++- warning.c | 3 + warning.h | 1 + 4 files changed, 228 insertions(+), 100 deletions(-) diff --git a/parser.c b/parser.c index df444bd..b027d96 100644 --- a/parser.c +++ b/parser.c @@ -237,39 +237,6 @@ static size_t get_statement_struct_size(statement_kind_t kind) return sizes[kind]; } -/** - * Allocate a statement node of given kind and initialize all - * fields with zero. - */ -static statement_t *allocate_statement_zero(statement_kind_t kind) -{ - size_t size = get_statement_struct_size(kind); - statement_t *res = allocate_ast_zero(size); - - res->base.kind = kind; - return res; -} - -/** - * Creates a new invalid statement. - */ -static statement_t *create_invalid_statement(void) -{ - statement_t *statement = allocate_statement_zero(STATEMENT_INVALID); - statement->base.source_position = token.source_position; - return statement; -} - -/** - * Allocate a new empty statement. - */ -static statement_t *create_empty_statement(void) -{ - statement_t *statement = allocate_statement_zero(STATEMENT_EMPTY); - statement->base.source_position = token.source_position; - return statement; -} - /** * Returns the size of an expression node. * @@ -315,6 +282,19 @@ static size_t get_expression_struct_size(expression_kind_t kind) return sizes[kind]; } +/** + * Allocate a statement node of given kind and initialize all + * fields with zero. + */ +static statement_t *allocate_statement_zero(statement_kind_t kind) +{ + size_t size = get_statement_struct_size(kind); + statement_t *res = allocate_ast_zero(size); + + res->base.kind = kind; + return res; +} + /** * Allocate an expression node of given kind and initialize all * fields with zero. @@ -329,6 +309,36 @@ static expression_t *allocate_expression_zero(expression_kind_t kind) return res; } +/** + * Creates a new invalid expression. + */ +static expression_t *create_invalid_expression(void) +{ + expression_t *expression = allocate_expression_zero(EXPR_INVALID); + expression->base.source_position = token.source_position; + return expression; +} + +/** + * Creates a new invalid statement. + */ +static statement_t *create_invalid_statement(void) +{ + statement_t *statement = allocate_statement_zero(STATEMENT_INVALID); + statement->base.source_position = token.source_position; + return statement; +} + +/** + * Allocate a new empty statement. + */ +static statement_t *create_empty_statement(void) +{ + statement_t *statement = allocate_statement_zero(STATEMENT_EMPTY); + statement->base.source_position = token.source_position; + return statement; +} + /** * Returns the size of a type node. * @@ -953,6 +963,11 @@ typedef enum gnu_attribute_kind_t { GNU_AK_ALWAYS_INLINE, GNU_AK_MALLOC, GNU_AK_WEAK, + GNU_AK_ALIGNED, + GNU_AK_ALIAS, + GNU_AK_SECTION, + GNU_AK_FORMAT, + GNU_AK_FORMAT_ARG, GNU_AK_LAST } gnu_attribute_kind_t; @@ -970,6 +985,11 @@ static const char *gnu_attribute_names[GNU_AK_LAST] = { [GNU_AK_ALWAYS_INLINE] = "always_inline", [GNU_AK_MALLOC] = "malloc", [GNU_AK_WEAK] = "weak", + [GNU_AK_ALIGNED] = "aligned", + [GNU_AK_ALIAS] = "alias", + [GNU_AK_SECTION] = "section", + [GNU_AK_FORMAT] = "format", + [GNU_AK_FORMAT_ARG] = "format_arg" }; /** @@ -988,6 +1008,78 @@ static int strcmp_underscore(const char *s1, const char *s2) { return strcmp(s1, s2); } +static expression_t *parse_gnu_attribute_const_arg(void) { + expression_t *expression; + expect('('); + add_anchor_token('('); + expression = parse_constant_expression(); + rem_anchor_token('('); + expect(')'); + return expression; +end_error: + return create_invalid_expression(); +} + +static string_t parse_gnu_attribute_string_arg(void) { + string_t string = { NULL, 0 }; + expect('('); + add_anchor_token('('); + if(token.type != T_STRING_LITERAL) { + parse_error_expected("while parsing attribute directive", T_STRING_LITERAL); + goto end_error; + } + string = parse_string_literals(); + rem_anchor_token('('); + expect(')'); +end_error: + return string; +} + +static const char *format_names[] = { + "printf", + "scanf", + "strftime", + "strfmon" +}; + +/** + * parse ( identifier, const expression, const expression ) + */ +static void parse_gnu_attribute_format_args(void) { + int i; + expect('('); + if(token.type != T_IDENTIFIER) { + parse_error_expected("while parsing format attribute directive", T_IDENTIFIER); + goto end_error; + } + const char *name = token.v.symbol->string; + for(i = 0; i < 4; ++i) { + if(strcmp_underscore(format_names[i], name) == 0) + break; + } + if(i >= 4) { + if(warning.attribute) + warningf(HERE, "'%s' is an unrecognized format function type", name); + } + next_token(); + + expect(','); + add_anchor_token(')'); + add_anchor_token(','); + parse_constant_expression(); + rem_anchor_token(','); + rem_anchor_token('('); + + expect(','); + add_anchor_token(')'); + parse_constant_expression(); + rem_anchor_token('('); + expect(')'); + return; +end_error: + return; +} + /** * Parse one GNU attribute. * @@ -1008,72 +1100,98 @@ static int strcmp_underscore(const char *s1, const char *s2) { * always_inline * malloc * weak + * + * The following attributes are parsed with arguments + * aligned( const expression ) + * alias( string literal ) + * section( string literal ) + * format( identifier, const expression, const expression ) + * format_arg( const expression ) */ static void parse_gnu_attribute(void) { eat(T___attribute__); expect('('); expect('('); - while(true) { - const char *name; - if(token.type == T_const) { - name = "const"; - } else if(token.type == T_volatile) { - name = "volatile"; - } else if(token.type == T_cdecl) { - /* __attribute__((cdecl)), WITH ms mode */ - name = "cdecl"; - } else if(token.type != T_IDENTIFIER) { - parse_error_expected("while parsing GNU attribute", T_IDENTIFIER); - break; - } - const symbol_t *sym = token.v.symbol; - name = sym->string; - next_token(); + if(token.type != ')') { + /* non-empty attribute list */ + while(true) { + const char *name; + if(token.type == T_const) { + name = "const"; + } else if(token.type == T_volatile) { + name = "volatile"; + } else if(token.type == T_cdecl) { + /* __attribute__((cdecl)), WITH ms mode */ + name = "cdecl"; + } else if(token.type != T_IDENTIFIER) { + parse_error_expected("while parsing GNU attribute", T_IDENTIFIER); + break; + } + const symbol_t *sym = token.v.symbol; + name = sym->string; + next_token(); - gnu_attribute_kind_t kind; - for(kind = 0; kind < GNU_AK_LAST; ++kind) { - if(strcmp_underscore(gnu_attribute_names[kind], name) == 0) + gnu_attribute_kind_t kind; + for(kind = 0; kind < GNU_AK_LAST; ++kind) { + if(strcmp_underscore(gnu_attribute_names[kind], name) == 0) + break; + } + switch(kind) { + case GNU_AK_CONST: break; - } - switch(kind) { - case GNU_AK_CONST: - break; - case GNU_AK_VOLATILE: - break; - case GNU_AK_CDECL: - break; - case GNU_AK_STDCALL: - break; - case GNU_AK_FASTCALL: - break; - case GNU_AK_DEPRECATED: - break; - case GNU_AK_NOINLINE: - break; - case GNU_AK_NORETURN: - break; - case GNU_AK_NAKED: - break; - case GNU_AK_PURE: - break; - case GNU_AK_ALWAYS_INLINE: - break; - case GNU_AK_MALLOC: - break; - case GNU_AK_WEAK: - break; - case GNU_AK_LAST: - warningf(HERE, "unrecognized attribute '%s'", name); + case GNU_AK_VOLATILE: + break; + case GNU_AK_CDECL: + break; + case GNU_AK_STDCALL: + break; + case GNU_AK_FASTCALL: + break; + case GNU_AK_DEPRECATED: + break; + case GNU_AK_NOINLINE: + break; + case GNU_AK_NORETURN: + break; + case GNU_AK_NAKED: + break; + case GNU_AK_PURE: + break; + case GNU_AK_ALWAYS_INLINE: + break; + case GNU_AK_MALLOC: + break; + case GNU_AK_WEAK: + break; + case GNU_AK_ALIGNED: + parse_gnu_attribute_const_arg(); + break; + case GNU_AK_ALIAS: + parse_gnu_attribute_string_arg(); + break; + case GNU_AK_SECTION: + parse_gnu_attribute_string_arg(); + break; + case GNU_AK_FORMAT: + parse_gnu_attribute_format_args(); + break; + case GNU_AK_FORMAT_ARG: + parse_gnu_attribute_const_arg(); + break; + case GNU_AK_LAST: + if(warning.attribute) + warningf(HERE, "'%s' attribute directive ignored", name); - /* skip possible arguments */ - if(token.type == '(') - eat_until_matching_token('('); - break; + /* skip possible arguments */ + if(token.type == '(') + eat_until_matching_token('('); + break; + } + if(token.type != ',') + break; + next_token(); } - if(token.type != ',') - break; - next_token(); } expect(')'); expect(')'); @@ -4069,16 +4187,6 @@ struct expression_parser_function_t { expression_parser_function_t expression_parsers[T_LAST_TOKEN]; -/** - * Creates a new invalid expression. - */ -static expression_t *create_invalid_expression(void) -{ - expression_t *expression = allocate_expression_zero(EXPR_INVALID); - expression->base.source_position = token.source_position; - return expression; -} - /** * Prints an error message if an expression was expected but not read */ diff --git a/parsetest/attributes.c b/parsetest/attributes.c index a436268..b0de881 100644 --- a/parsetest/attributes.c +++ b/parsetest/attributes.c @@ -1,4 +1,20 @@ int test1(int a) __attribute__((noreturn)); int test2(int a) __attribute__((const)); int test3(int a) __attribute__((weak)); -int test4(int a) __attribute__((unknown_error(bla, bla))); +int test4(int a) __attribute__((unknown_error("bla", 3))); +int test5(int a) __attribute__((alias("test2"))); +int test6(int a) __attribute__((section("extra"))); +int test7(int a, const char *fmt, ...) __attribute__((format(printf,2,3))); + +struct X { + char y; + int x __attribute__((aligned(4+4))); +}; + +int test2(int a) { + return a; +} + +int main(void) { + return test5(0); +} diff --git a/warning.c b/warning.c index 05195e6..ebf5f6a 100644 --- a/warning.c +++ b/warning.c @@ -22,6 +22,7 @@ #include "warning.h" warning_t warning = { + .attribute = true, .char_subscripts = true, .check_format = true, .empty_statement = false, @@ -65,6 +66,7 @@ void set_warning_opt(const char *const opt) #define OPT(x, y) OPTX(x) SET(y) OPTX("all") { /* Note: this switched on a lot of more warnings than gcc's -Wall */ + SET(attribute) SET(char_subscripts) SET(check_format) SET(empty_statement) @@ -82,6 +84,7 @@ void set_warning_opt(const char *const opt) SET(unused_value) SET(unused_variable) } + OPT("attribute", attribute) OPT("char-subscripts", char_subscripts) OPT("empty-statement", empty_statement) OPT("error", s_are_errors) diff --git a/warning.h b/warning.h index a71aaec..743e420 100644 --- a/warning.h +++ b/warning.h @@ -29,6 +29,7 @@ typedef struct warning_t { bool aggregate_return:1; /**< Warn if any functions that return structures or unions are defined or called */ bool bad_function_cast:1; /**< Warn whenever a function call is cast to a non-matching type */ #endif + bool attribute:1; /**< Warn if an unexpected `__attribute__' is used or function attributes applied to variables, etc. */ bool char_subscripts:1; /**< Warn if an array subscript has the type 'char' */ bool check_format:1; /**< Check printf-style format strings */ #if 0 // TODO -- 2.20.1