-/**
- * Parse one GNU attribute.
- *
- * Note that attribute names can be specified WITH or WITHOUT
- * double underscores, ie const or __const__.
- *
- * The following attributes are parsed without arguments
- * const
- * volatile
- * cdecl
- * stdcall
- * fastcall
- * deprecated
- * noinline
- * noreturn
- * naked
- * pure
- * always_inline
- * malloc
- * weak
- * constructor
- * destructor
- * nothrow
- * transparent_union
- * common
- * nocommon
- * packed
- * shared
- * notshared
- * used
- * unused
- * no_instrument_function
- * warn_unused_result
- * longcall
- * shortcall
- * long_call
- * short_call
- * function_vector
- * interrupt_handler
- * nmi_handler
- * nesting
- * near
- * far
- * signal
- * eightbit_data
- * tiny_data
- * saveall
- * flatten
- * sseregparm
- * externally_visible
- * return_twice
- * may_alias
- * ms_struct
- * gcc_struct
- * dllimport
- * dllexport
- *
- * 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 )
- * tls_model( string literal )
- * visibility( string literal )
- * regparm( const expression )
- * model( string leteral )
- * trap_exit( const expression )
- * sp_switch( string literal )
- *
- * The following attributes might have arguments
- * weak_ref( string literal )
- * non_null( const expression // ',' )
- * interrupt( string literal )
- * sentinel( constant expression )
- */
-static decl_modifiers_t parse_gnu_attribute(gnu_attribute_t **attributes)
-{
- gnu_attribute_t *head = *attributes;
- gnu_attribute_t *last = *attributes;
- decl_modifiers_t modifiers = 0;
- gnu_attribute_t *attribute;
-
- eat(T___attribute__);
- expect('(', end_error);
- expect('(', end_error);
-
- if (token.type != ')') {
- /* find the end of the list */
- if (last != NULL) {
- while (last->next != NULL)
- last = last->next;
- }
-
- /* 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) {
- const symbol_t *sym = token.v.symbol;
- name = sym->string;
- } else {
- parse_error_expected("while parsing GNU attribute", T_IDENTIFIER, NULL);
- break;
- }
-
- next_token();
-
- int i;
- for (i = 0; i < GNU_AK_LAST; ++i) {
- if (strcmp_underscore(gnu_attribute_names[i], name) == 0)
- break;
- }
- gnu_attribute_kind_t kind = (gnu_attribute_kind_t)i;
-
- attribute = NULL;
- if (kind == GNU_AK_LAST) {
- if (warning.attribute)
- warningf(HERE, "'%s' attribute directive ignored", name);
-
- /* skip possible arguments */
- if (token.type == '(') {
- eat_until_matching_token(')');
- }
- } else {
- /* check for arguments */
- attribute = allocate_gnu_attribute(kind);
- if (token.type == '(') {
- next_token();
- if (token.type == ')') {
- /* empty args are allowed */
- next_token();
- } else
- attribute->have_arguments = true;
- }
-
- switch (kind) {
- case GNU_AK_VOLATILE:
- case GNU_AK_NAKED:
- case GNU_AK_MALLOC:
- case GNU_AK_WEAK:
- case GNU_AK_COMMON:
- case GNU_AK_NOCOMMON:
- case GNU_AK_SHARED:
- case GNU_AK_NOTSHARED:
- case GNU_AK_NO_INSTRUMENT_FUNCTION:
- case GNU_AK_WARN_UNUSED_RESULT:
- case GNU_AK_LONGCALL:
- case GNU_AK_SHORTCALL:
- case GNU_AK_LONG_CALL:
- case GNU_AK_SHORT_CALL:
- case GNU_AK_FUNCTION_VECTOR:
- case GNU_AK_INTERRUPT_HANDLER:
- case GNU_AK_NMI_HANDLER:
- case GNU_AK_NESTING:
- case GNU_AK_NEAR:
- case GNU_AK_FAR:
- case GNU_AK_SIGNAL:
- case GNU_AK_EIGTHBIT_DATA:
- case GNU_AK_TINY_DATA:
- case GNU_AK_SAVEALL:
- case GNU_AK_FLATTEN:
- case GNU_AK_SSEREGPARM:
- case GNU_AK_EXTERNALLY_VISIBLE:
- case GNU_AK_RETURN_TWICE:
- case GNU_AK_MAY_ALIAS:
- case GNU_AK_MS_STRUCT:
- case GNU_AK_GCC_STRUCT:
- goto no_arg;
-
- case GNU_AK_CDECL: modifiers |= DM_CDECL; goto no_arg;
- case GNU_AK_FASTCALL: modifiers |= DM_FASTCALL; goto no_arg;
- case GNU_AK_STDCALL: modifiers |= DM_STDCALL; goto no_arg;
- case GNU_AK_UNUSED: modifiers |= DM_UNUSED; goto no_arg;
- case GNU_AK_USED: modifiers |= DM_USED; goto no_arg;
- case GNU_AK_PURE: modifiers |= DM_PURE; goto no_arg;
- case GNU_AK_CONST: modifiers |= DM_CONST; goto no_arg;
- case GNU_AK_ALWAYS_INLINE: modifiers |= DM_FORCEINLINE; goto no_arg;
- case GNU_AK_DLLIMPORT: modifiers |= DM_DLLIMPORT; goto no_arg;
- case GNU_AK_DLLEXPORT: modifiers |= DM_DLLEXPORT; goto no_arg;
- case GNU_AK_PACKED: modifiers |= DM_PACKED; goto no_arg;
- case GNU_AK_NOINLINE: modifiers |= DM_NOINLINE; goto no_arg;
- case GNU_AK_RETURNS_TWICE: modifiers |= DM_RETURNS_TWICE; goto no_arg;
- case GNU_AK_NORETURN: modifiers |= DM_NORETURN; goto no_arg;
- case GNU_AK_NOTHROW: modifiers |= DM_NOTHROW; goto no_arg;
- case GNU_AK_TRANSPARENT_UNION: modifiers |= DM_TRANSPARENT_UNION; goto no_arg;
- case GNU_AK_CONSTRUCTOR: modifiers |= DM_CONSTRUCTOR; goto no_arg;
- case GNU_AK_DESTRUCTOR: modifiers |= DM_DESTRUCTOR; goto no_arg;
- case GNU_AK_DEPRECATED: modifiers |= DM_DEPRECATED; goto no_arg;
-
- case GNU_AK_ALIGNED:
- /* __align__ may be used without an argument */
- if (attribute->have_arguments) {
- parse_gnu_attribute_const_arg(attribute);
- }
- break;
-
- case GNU_AK_FORMAT_ARG:
- case GNU_AK_REGPARM:
- case GNU_AK_TRAP_EXIT:
- if (!attribute->have_arguments) {
- /* should have arguments */
- errorf(HERE, "wrong number of arguments specified for '%s' attribute", name);
- attribute->invalid = true;
- } else
- parse_gnu_attribute_const_arg(attribute);
- break;
- case GNU_AK_ALIAS:
- case GNU_AK_SECTION:
- case GNU_AK_SP_SWITCH:
- if (!attribute->have_arguments) {
- /* should have arguments */
- errorf(HERE, "wrong number of arguments specified for '%s' attribute", name);
- attribute->invalid = true;
- } else
- parse_gnu_attribute_string_arg(attribute, &attribute->u.string);
- break;
- case GNU_AK_FORMAT:
- if (!attribute->have_arguments) {
- /* should have arguments */
- errorf(HERE, "wrong number of arguments specified for '%s' attribute", name);
- attribute->invalid = true;
- } else
- parse_gnu_attribute_format_args(attribute);
- break;
- case GNU_AK_WEAKREF:
- /* may have one string argument */
- if (attribute->have_arguments)
- parse_gnu_attribute_string_arg(attribute, &attribute->u.string);
- break;
- case GNU_AK_NONNULL:
- if (attribute->have_arguments)
- parse_gnu_attribute_const_arg_list(attribute);
- break;
- case GNU_AK_TLS_MODEL:
- if (!attribute->have_arguments) {
- /* should have arguments */
- errorf(HERE, "wrong number of arguments specified for '%s' attribute", name);
- } else
- parse_gnu_attribute_tls_model_arg(attribute);
- break;
- case GNU_AK_VISIBILITY:
- if (!attribute->have_arguments) {
- /* should have arguments */
- errorf(HERE, "wrong number of arguments specified for '%s' attribute", name);
- } else
- parse_gnu_attribute_visibility_arg(attribute);
- break;
- case GNU_AK_MODEL:
- if (!attribute->have_arguments) {
- /* should have arguments */
- errorf(HERE, "wrong number of arguments specified for '%s' attribute", name);
- } else {
- parse_gnu_attribute_model_arg(attribute);
- }
- break;
- case GNU_AK_MODE:
- if (!attribute->have_arguments) {
- /* should have arguments */
- errorf(HERE, "wrong number of arguments specified for '%s' attribute", name);
- } else {
- parse_gnu_attribute_mode_arg(attribute);
- }
- break;
- case GNU_AK_INTERRUPT:
- /* may have one string argument */
- if (attribute->have_arguments)
- parse_gnu_attribute_interrupt_arg(attribute);
- break;
- case GNU_AK_SENTINEL:
- /* may have one string argument */
- if (attribute->have_arguments)
- parse_gnu_attribute_const_arg(attribute);
- break;
- case GNU_AK_LAST:
- /* already handled */
- break;
-
-no_arg:
- check_no_argument(attribute, name);
- }
- }
- if (attribute != NULL) {
- if (last != NULL) {
- last->next = attribute;
- last = attribute;
- } else {
- head = last = attribute;
- }
- }
-
- if (token.type != ',')
- break;
- next_token();
- }
- }
- expect(')', end_error);
- expect(')', end_error);
-end_error:
- *attributes = head;
-
- return modifiers;
-}