Mostly finished parsing of GNU attributes.
authorMichael Beck <beck@ipd.info.uni-karlsruhe.de>
Tue, 18 Mar 2008 15:35:04 +0000 (15:35 +0000)
committerMichael Beck <beck@ipd.info.uni-karlsruhe.de>
Tue, 18 Mar 2008 15:35:04 +0000 (15:35 +0000)
[r18981]

parser.c
parsetest/attributes.c
warning.c
warning.h

index df444bd..b027d96 100644 (file)
--- 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
  */
index a436268..b0de881 100644 (file)
@@ -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);
+}
index 05195e6..ebf5f6a 100644 (file)
--- 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)
index a71aaec..743e420 100644 (file)
--- 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