give stray-semicolon warning a name
[cparser] / parser.c
index be04d23..e0e2503 100644 (file)
--- a/parser.c
+++ b/parser.c
@@ -36,7 +36,7 @@
 #include "entity_t.h"
 #include "attribute_t.h"
 #include "lang_features.h"
-#include "walk_statements.h"
+#include "walk.h"
 #include "warning.h"
 #include "printer.h"
 #include "adt/bitfiddle.h"
@@ -89,7 +89,6 @@ static scope_t             *current_scope     = NULL;
 /** Point to the current function declaration if inside a function. */
 static function_t          *current_function  = NULL;
 static entity_t            *current_entity    = NULL;
-static entity_t            *current_init_decl = NULL;
 static switch_statement_t  *current_switch    = NULL;
 static statement_t         *current_loop      = NULL;
 static statement_t         *current_parent    = NULL;
@@ -101,28 +100,44 @@ static label_statement_t   *label_first       = NULL;
 static label_statement_t  **label_anchor      = NULL;
 /** current translation unit. */
 static translation_unit_t  *unit              = NULL;
-/** true if we are in a type property context (evaluation only for type) */
-static bool                 in_type_prop      = false;
 /** true if we are in an __extension__ context. */
 static bool                 in_gcc_extension  = false;
 static struct obstack       temp_obst;
 static entity_t            *anonymous_entity;
 static declaration_t      **incomplete_arrays;
-
-
-#define PUSH_PARENT(stmt)                          \
-       statement_t *const prev_parent = current_parent; \
-       ((void)(current_parent = (stmt)))
-#define POP_PARENT ((void)(current_parent = prev_parent))
+static elf_visibility_tag_t default_visibility = ELF_VISIBILITY_DEFAULT;
+
+
+#define PUSH_PARENT(stmt) \
+       statement_t *const new_parent = (stmt); \
+       statement_t *const old_parent = current_parent; \
+       ((void)(current_parent = new_parent))
+#define POP_PARENT() (assert(current_parent == new_parent), (void)(current_parent = old_parent))
+
+#define PUSH_SCOPE(scope) \
+       size_t   const top       = environment_top(); \
+       scope_t *const new_scope = (scope); \
+       scope_t *const old_scope = scope_push(new_scope)
+#define POP_SCOPE() (assert(current_scope == new_scope), scope_pop(old_scope), environment_pop_to(top))
+
+#define PUSH_EXTENSION() \
+       (void)0; \
+       bool const old_gcc_extension = in_gcc_extension; \
+       while (next_if(T___extension__)) { \
+               in_gcc_extension = true; \
+       } \
+       do {} while (0)
+#define POP_EXTENSION() \
+       ((void)(in_gcc_extension = old_gcc_extension))
 
 /** special symbol used for anonymous entities. */
 static symbol_t *sym_anonymous = NULL;
 
 /** The token anchor set */
-static unsigned char token_anchor_set[T_LAST_TOKEN];
+static unsigned short token_anchor_set[T_LAST_TOKEN];
 
 /** The current source position. */
-#define HERE (&token.source_position)
+#define HERE (&token.base.source_position)
 
 /** true if we are in GCC mode. */
 #define GNU_MODE ((c_mode & _GNUC) || in_gcc_extension)
@@ -214,10 +229,6 @@ static void semantic_comparison(binary_expression_t *expression);
        TYPE_QUALIFIERS                 \
        TYPE_SPECIFIERS
 
-#define TYPENAME_START      \
-       TYPE_QUALIFIERS         \
-       TYPE_SPECIFIERS
-
 #define EXPRESSION_START              \
        case '!':                         \
        case '&':                         \
@@ -292,7 +303,7 @@ static size_t get_statement_struct_size(statement_kind_t kind)
                [STATEMENT_MS_TRY]      = sizeof(ms_try_statement_t),
                [STATEMENT_LEAVE]       = sizeof(leave_statement_t)
        };
-       assert(kind < lengthof(sizes));
+       assert((size_t)kind < lengthof(sizes));
        assert(sizes[kind] != 0);
        return sizes[kind];
 }
@@ -308,6 +319,7 @@ static size_t get_expression_struct_size(expression_kind_t kind)
                [EXPR_INVALID]                    = sizeof(expression_base_t),
                [EXPR_REFERENCE]                  = sizeof(reference_expression_t),
                [EXPR_REFERENCE_ENUM_VALUE]       = sizeof(reference_expression_t),
+               [EXPR_LITERAL_BOOLEAN]            = sizeof(literal_expression_t),
                [EXPR_LITERAL_INTEGER]            = sizeof(literal_expression_t),
                [EXPR_LITERAL_INTEGER_OCTAL]      = sizeof(literal_expression_t),
                [EXPR_LITERAL_INTEGER_HEXADECIMAL]= sizeof(literal_expression_t),
@@ -343,7 +355,7 @@ static size_t get_expression_struct_size(expression_kind_t kind)
        if (kind >= EXPR_BINARY_FIRST && kind <= EXPR_BINARY_LAST) {
                return sizes[EXPR_BINARY_FIRST];
        }
-       assert(kind < lengthof(sizes));
+       assert((size_t)kind < lengthof(sizes));
        assert(sizes[kind] != 0);
        return sizes[kind];
 }
@@ -360,7 +372,7 @@ static statement_t *allocate_statement_zero(statement_kind_t kind)
 
        res->base.kind            = kind;
        res->base.parent          = current_parent;
-       res->base.source_position = token.source_position;
+       res->base.source_position = token.base.source_position;
        return res;
 }
 
@@ -377,7 +389,7 @@ static expression_t *allocate_expression_zero(expression_kind_t kind)
 
        res->base.kind            = kind;
        res->base.type            = type_error_type;
-       res->base.source_position = token.source_position;
+       res->base.source_position = token.base.source_position;
        return res;
 }
 
@@ -406,15 +418,6 @@ static statement_t *create_empty_statement(void)
        return allocate_statement_zero(STATEMENT_EMPTY);
 }
 
-static function_parameter_t *allocate_parameter(type_t *const type)
-{
-       function_parameter_t *const param
-               = obstack_alloc(type_obst, sizeof(*param));
-       memset(param, 0, sizeof(*param));
-       param->type = type;
-       return param;
-}
-
 /**
  * Returns the size of an initializer node.
  *
@@ -429,7 +432,7 @@ static size_t get_initializer_size(initializer_kind_t kind)
                [INITIALIZER_LIST]        = sizeof(initializer_list_t),
                [INITIALIZER_DESIGNATOR]  = sizeof(initializer_designator_t)
        };
-       assert(kind < lengthof(sizes));
+       assert((size_t)kind < lengthof(sizes));
        assert(sizes[kind] != 0);
        return sizes[kind];
 }
@@ -481,7 +484,7 @@ static inline void next_token(void)
 
 static inline bool next_if(int const type)
 {
-       if (token.type == type) {
+       if (token.kind == type) {
                next_token();
                return true;
        } else {
@@ -502,41 +505,41 @@ static inline const token_t *look_ahead(size_t num)
 /**
  * Adds a token type to the token type anchor set (a multi-set).
  */
-static void add_anchor_token(int token_type)
+static void add_anchor_token(int token_kind)
 {
-       assert(0 <= token_type && token_type < T_LAST_TOKEN);
-       ++token_anchor_set[token_type];
+       assert(0 <= token_kind && token_kind < T_LAST_TOKEN);
+       ++token_anchor_set[token_kind];
 }
 
 /**
  * Set the number of tokens types of the given type
  * to zero and return the old count.
  */
-static int save_and_reset_anchor_state(int token_type)
+static int save_and_reset_anchor_state(int token_kind)
 {
-       assert(0 <= token_type && token_type < T_LAST_TOKEN);
-       int count = token_anchor_set[token_type];
-       token_anchor_set[token_type] = 0;
+       assert(0 <= token_kind && token_kind < T_LAST_TOKEN);
+       int count = token_anchor_set[token_kind];
+       token_anchor_set[token_kind] = 0;
        return count;
 }
 
 /**
  * Restore the number of token types to the given count.
  */
-static void restore_anchor_state(int token_type, int count)
+static void restore_anchor_state(int token_kind, int count)
 {
-       assert(0 <= token_type && token_type < T_LAST_TOKEN);
-       token_anchor_set[token_type] = count;
+       assert(0 <= token_kind && token_kind < T_LAST_TOKEN);
+       token_anchor_set[token_kind] = count;
 }
 
 /**
  * Remove a token type from the token type anchor set (a multi-set).
  */
-static void rem_anchor_token(int token_type)
+static void rem_anchor_token(int token_kind)
 {
-       assert(0 <= token_type && token_type < T_LAST_TOKEN);
-       assert(token_anchor_set[token_type] != 0);
-       --token_anchor_set[token_type];
+       assert(0 <= token_kind && token_kind < T_LAST_TOKEN);
+       assert(token_anchor_set[token_kind] != 0);
+       --token_anchor_set[token_kind];
 }
 
 /**
@@ -545,9 +548,9 @@ static void rem_anchor_token(int token_type)
  */
 static bool at_anchor(void)
 {
-       if (token.type < 0)
+       if (token.kind < 0)
                return false;
-       return token_anchor_set[token.type];
+       return token_anchor_set[token.kind];
 }
 
 /**
@@ -566,11 +569,11 @@ static void eat_until_matching_token(int type)
        unsigned parenthesis_count = 0;
        unsigned brace_count       = 0;
        unsigned bracket_count     = 0;
-       while (token.type        != end_token ||
+       while (token.kind        != end_token ||
               parenthesis_count != 0         ||
               brace_count       != 0         ||
               bracket_count     != 0) {
-               switch (token.type) {
+               switch (token.kind) {
                case T_EOF: return;
                case '(': ++parenthesis_count; break;
                case '{': ++brace_count;       break;
@@ -590,7 +593,7 @@ static void eat_until_matching_token(int type)
                        if (bracket_count > 0)
                                --bracket_count;
 check_stop:
-                       if (token.type        == end_token &&
+                       if (token.kind        == end_token &&
                            parenthesis_count == 0         &&
                            brace_count       == 0         &&
                            bracket_count     == 0)
@@ -609,9 +612,9 @@ check_stop:
  */
 static void eat_until_anchor(void)
 {
-       while (token_anchor_set[token.type] == 0) {
-               if (token.type == '(' || token.type == '{' || token.type == '[')
-                       eat_until_matching_token(token.type);
+       while (token_anchor_set[token.kind] == 0) {
+               if (token.kind == '(' || token.kind == '{' || token.kind == '[')
+                       eat_until_matching_token(token.kind);
                next_token();
        }
 }
@@ -625,7 +628,7 @@ static void eat_block(void)
        next_if('}');
 }
 
-#define eat(token_type) (assert(token.type == (token_type)), next_token())
+#define eat(token_kind) (assert(token.kind == (token_kind)), next_token())
 
 /**
  * Report a parse error because an expected token was not found.
@@ -662,13 +665,13 @@ static void type_error_incompatible(const char *msg,
  */
 #define expect(expected, error_label)                     \
        do {                                                  \
-               if (UNLIKELY(token.type != (expected))) {         \
+               if (UNLIKELY(token.kind != (expected))) {         \
                        parse_error_expected(NULL, (expected), NULL); \
                        add_anchor_token(expected);                   \
                        eat_until_anchor();                           \
-                       next_if((expected));                          \
                        rem_anchor_token(expected);                   \
-                       goto error_label;                             \
+                       if (token.kind != (expected))                 \
+                         goto error_label;                           \
                }                                                 \
                next_token();                                     \
        } while (0)
@@ -705,7 +708,7 @@ static entity_t *get_entity(const symbol_t *const symbol,
        assert(namespc != NAMESPACE_INVALID);
        entity_t *entity = symbol->entity;
        for (; entity != NULL; entity = entity->base.symbol_next) {
-               if (entity->base.namespc == namespc)
+               if ((namespace_tag_t)entity->base.namespc == namespc)
                        return entity;
        }
 
@@ -718,7 +721,7 @@ static entity_t *get_tag(symbol_t const *const symbol,
                          entity_kind_tag_t const kind)
 {
        entity_t *entity = get_entity(symbol, NAMESPACE_TAG);
-       if (entity != NULL && entity->kind != kind) {
+       if (entity != NULL && (entity_kind_tag_t)entity->kind != kind) {
                errorf(HERE,
                                "'%Y' defined as wrong kind of tag (previous definition %P)",
                                symbol, &entity->base.source_position);
@@ -875,32 +878,12 @@ static int get_rank(const type_t *type)
  */
 static type_t *promote_integer(type_t *type)
 {
-       if (type->kind == TYPE_BITFIELD)
-               type = type->bitfield.base_type;
-
        if (get_rank(type) < get_akind_rank(ATOMIC_TYPE_INT))
                type = type_int;
 
        return type;
 }
 
-/**
- * Create a cast expression.
- *
- * @param expression  the expression to cast
- * @param dest_type   the destination type
- */
-static expression_t *create_cast_expression(expression_t *expression,
-                                            type_t *dest_type)
-{
-       expression_t *cast = allocate_expression_zero(EXPR_UNARY_CAST_IMPLICIT);
-
-       cast->unary.value = expression;
-       cast->base.type   = dest_type;
-
-       return cast;
-}
-
 /**
  * Check if a given expression represents a null pointer constant.
  *
@@ -909,8 +892,7 @@ static expression_t *create_cast_expression(expression_t *expression,
 static bool is_null_pointer_constant(const expression_t *expression)
 {
        /* skip void* cast */
-       if (expression->kind == EXPR_UNARY_CAST ||
-                       expression->kind == EXPR_UNARY_CAST_IMPLICIT) {
+       if (expression->kind == EXPR_UNARY_CAST) {
                type_t *const type = skip_typeref(expression->base.type);
                if (types_compatible(type, type_void_ptr))
                        expression = expression->unary.value;
@@ -940,7 +922,12 @@ static expression_t *create_implicit_cast(expression_t *expression,
        if (source_type == dest_type)
                return expression;
 
-       return create_cast_expression(expression, dest_type);
+       expression_t *cast = allocate_expression_zero(EXPR_UNARY_CAST);
+       cast->unary.value   = expression;
+       cast->base.type     = dest_type;
+       cast->base.implicit = true;
+
+       return cast;
 }
 
 typedef enum assign_error_t {
@@ -952,10 +939,7 @@ typedef enum assign_error_t {
        ASSIGN_WARNING_INT_FROM_POINTER
 } assign_error_t;
 
-static void report_assign_error(assign_error_t error, type_t *orig_type_left,
-                                const expression_t *const right,
-                                const char *context,
-                                const source_position_t *source_position)
+static void report_assign_error(assign_error_t error, type_t *orig_type_left, expression_t const *const right, char const *const context, source_position_t const *const pos)
 {
        type_t *const orig_type_right = right->base.type;
        type_t *const type_left       = skip_typeref(orig_type_left);
@@ -965,48 +949,29 @@ static void report_assign_error(assign_error_t error, type_t *orig_type_left,
        case ASSIGN_SUCCESS:
                return;
        case ASSIGN_ERROR_INCOMPATIBLE:
-               errorf(source_position,
-                      "destination type '%T' in %s is incompatible with type '%T'",
-                      orig_type_left, context, orig_type_right);
+               errorf(pos, "destination type '%T' in %s is incompatible with type '%T'", orig_type_left, context, orig_type_right);
                return;
 
        case ASSIGN_ERROR_POINTER_QUALIFIER_MISSING: {
-               if (warning.other) {
-                       type_t *points_to_left  = skip_typeref(type_left->pointer.points_to);
-                       type_t *points_to_right = skip_typeref(type_right->pointer.points_to);
+               type_t *points_to_left  = skip_typeref(type_left->pointer.points_to);
+               type_t *points_to_right = skip_typeref(type_right->pointer.points_to);
 
-                       /* the left type has all qualifiers from the right type */
-                       unsigned missing_qualifiers
-                               = points_to_right->base.qualifiers & ~points_to_left->base.qualifiers;
-                       warningf(source_position,
-                                       "destination type '%T' in %s from type '%T' lacks qualifiers '%Q' in pointer target type",
-                                       orig_type_left, context, orig_type_right, missing_qualifiers);
-               }
+               /* the left type has all qualifiers from the right type */
+               unsigned missing_qualifiers = points_to_right->base.qualifiers & ~points_to_left->base.qualifiers;
+               warningf(WARN_OTHER, pos, "destination type '%T' in %s from type '%T' lacks qualifiers '%Q' in pointer target type", orig_type_left, context, orig_type_right, missing_qualifiers);
                return;
        }
 
        case ASSIGN_WARNING_POINTER_INCOMPATIBLE:
-               if (warning.other) {
-                       warningf(source_position,
-                                       "destination type '%T' in %s is incompatible with '%E' of type '%T'",
-                                       orig_type_left, context, right, orig_type_right);
-               }
+               warningf(WARN_OTHER, pos, "destination type '%T' in %s is incompatible with '%E' of type '%T'", orig_type_left, context, right, orig_type_right);
                return;
 
        case ASSIGN_WARNING_POINTER_FROM_INT:
-               if (warning.other) {
-                       warningf(source_position,
-                                       "%s makes pointer '%T' from integer '%T' without a cast",
-                                       context, orig_type_left, orig_type_right);
-               }
+               warningf(WARN_OTHER, pos, "%s makes pointer '%T' from integer '%T' without a cast", context, orig_type_left, orig_type_right);
                return;
 
        case ASSIGN_WARNING_INT_FROM_POINTER:
-               if (warning.other) {
-                       warningf(source_position,
-                                       "%s makes integer '%T' from pointer '%T' without a cast",
-                                       context, orig_type_left, orig_type_right);
-               }
+               warningf(WARN_OTHER, pos, "%s makes integer '%T' from pointer '%T' without a cast", context, orig_type_left, orig_type_right);
                return;
 
        default:
@@ -1097,21 +1062,19 @@ static expression_t *parse_assignment_expression(void)
 
 static void warn_string_concat(const source_position_t *pos)
 {
-       if (warning.traditional) {
-               warningf(pos, "traditional C rejects string constant concatenation");
-       }
+       warningf(WARN_TRADITIONAL, pos, "traditional C rejects string constant concatenation");
 }
 
 static string_t parse_string_literals(void)
 {
-       assert(token.type == T_STRING_LITERAL);
-       string_t result = token.literal;
+       assert(token.kind == T_STRING_LITERAL);
+       string_t result = token.string.string;
 
        next_token();
 
-       while (token.type == T_STRING_LITERAL) {
-               warn_string_concat(&token.source_position);
-               result = concat_strings(&result, &token.literal);
+       while (token.kind == T_STRING_LITERAL) {
+               warn_string_concat(&token.base.source_position);
+               result = concat_strings(&result, &token.string.string);
                next_token();
        }
 
@@ -1137,7 +1100,8 @@ static int strcmp_underscore(const char *s1, const char *s2)
 static attribute_t *allocate_attribute_zero(attribute_kind_t kind)
 {
        attribute_t *attribute = allocate_ast_zero(sizeof(*attribute));
-       attribute->kind        = kind;
+       attribute->kind            = kind;
+       attribute->source_position = *HERE;
        return attribute;
 }
 
@@ -1173,13 +1137,13 @@ static attribute_argument_t *parse_attribute_arguments(void)
 {
        attribute_argument_t  *first  = NULL;
        attribute_argument_t **anchor = &first;
-       if (token.type != ')') do {
+       if (token.kind != ')') do {
                attribute_argument_t *argument = allocate_ast_zero(sizeof(*argument));
 
                /* is it an identifier */
-               if (token.type == T_IDENTIFIER
-                               && (look_ahead(1)->type == ',' || look_ahead(1)->type == ')')) {
-                       symbol_t *symbol   = token.symbol;
+               if (token.kind == T_IDENTIFIER
+                               && (look_ahead(1)->kind == ',' || look_ahead(1)->kind == ')')) {
+                       symbol_t *symbol   = token.identifier.symbol;
                        argument->kind     = ATTRIBUTE_ARGUMENT_SYMBOL;
                        argument->v.symbol = symbol;
                        next_token();
@@ -1206,9 +1170,8 @@ end_error:
 
 static attribute_t *parse_attribute_asm(void)
 {
-       eat(T_asm);
-
        attribute_t *attribute = allocate_attribute_zero(ATTRIBUTE_GNU_ASM);
+       eat(T_asm);
 
        expect('(', end_error);
        attribute->a.arguments = parse_attribute_arguments();
@@ -1220,9 +1183,9 @@ end_error:
 
 static symbol_t *get_symbol_from_token(void)
 {
-       switch(token.type) {
+       switch(token.kind) {
        case T_IDENTIFIER:
-               return token.symbol;
+               return token.identifier.symbol;
        case T_auto:
        case T_char:
        case T_double:
@@ -1252,7 +1215,7 @@ static symbol_t *get_symbol_from_token(void)
        case T_volatile:
        case T_inline:
                /* maybe we need more tokens ... add them on demand */
-               return get_token_symbol(&token);
+               return get_token_kind_symbol(token.kind);
        default:
                return NULL;
        }
@@ -1267,26 +1230,24 @@ static attribute_t *parse_attribute_gnu_single(void)
                return NULL;
        }
 
-       const char *name = symbol->string;
-       next_token();
+       attribute_kind_t  kind;
+       char const *const name = symbol->string;
+       for (kind = ATTRIBUTE_GNU_FIRST;; ++kind) {
+               if (kind > ATTRIBUTE_GNU_LAST) {
+                       warningf(WARN_ATTRIBUTE, HERE, "unknown attribute '%s' ignored", name);
+                       /* TODO: we should still save the attribute in the list... */
+                       kind = ATTRIBUTE_UNKNOWN;
+                       break;
+               }
 
-       attribute_kind_t kind;
-       for (kind = ATTRIBUTE_GNU_FIRST; kind <= ATTRIBUTE_GNU_LAST; ++kind) {
                const char *attribute_name = get_attribute_name(kind);
                if (attribute_name != NULL
                                && strcmp_underscore(attribute_name, name) == 0)
                        break;
        }
 
-       if (kind >= ATTRIBUTE_GNU_LAST) {
-               if (warning.attribute) {
-                       warningf(HERE, "unknown attribute '%s' ignored", name);
-               }
-               /* TODO: we should still save the attribute in the list... */
-               kind = ATTRIBUTE_UNKNOWN;
-       }
-
        attribute_t *attribute = allocate_attribute_zero(kind);
+       next_token();
 
        /* parse arguments */
        if (next_if('('))
@@ -1304,7 +1265,7 @@ static attribute_t *parse_attribute_gnu(void)
        expect('(', end_error);
        expect('(', end_error);
 
-       if (token.type != ')') do {
+       if (token.kind != ')') do {
                attribute_t *attribute = parse_attribute_gnu_single();
                if (attribute == NULL)
                        goto end_error;
@@ -1328,9 +1289,11 @@ static attribute_t *parse_attributes(attribute_t *first)
                        anchor = &(*anchor)->next;
 
                attribute_t *attribute;
-               switch (token.type) {
+               switch (token.kind) {
                case T___attribute__:
                        attribute = parse_attribute_gnu();
+                       if (attribute == NULL)
+                               continue;
                        break;
 
                case T_asm:
@@ -1338,31 +1301,30 @@ static attribute_t *parse_attributes(attribute_t *first)
                        break;
 
                case T_cdecl:
-                       next_token();
                        attribute = allocate_attribute_zero(ATTRIBUTE_MS_CDECL);
+                       eat(T_cdecl);
                        break;
 
                case T__fastcall:
-                       next_token();
                        attribute = allocate_attribute_zero(ATTRIBUTE_MS_FASTCALL);
+                       eat(T__fastcall);
                        break;
 
                case T__forceinline:
-                       next_token();
                        attribute = allocate_attribute_zero(ATTRIBUTE_MS_FORCEINLINE);
+                       eat(T__forceinline);
                        break;
 
                case T__stdcall:
-                       next_token();
                        attribute = allocate_attribute_zero(ATTRIBUTE_MS_STDCALL);
+                       eat(T__stdcall);
                        break;
 
                case T___thiscall:
-                       next_token();
                        /* TODO record modifier */
-                       if (warning.other)
-                               warningf(HERE, "Ignoring declaration modifier %K", &token);
+                       warningf(WARN_OTHER, HERE, "Ignoring declaration modifier %K", &token);
                        attribute = allocate_attribute_zero(ATTRIBUTE_MS_THISCALL);
+                       eat(T___thiscall);
                        break;
 
                default:
@@ -1528,7 +1490,6 @@ static void mark_vars_read(expression_t *const expr, entity_t *lhs_ent)
                case EXPR_UNARY_POSTFIX_DECREMENT:
                case EXPR_UNARY_PREFIX_INCREMENT:
                case EXPR_UNARY_PREFIX_DECREMENT:
-               case EXPR_UNARY_CAST_IMPLICIT:
                case EXPR_UNARY_ASSUME:
 unary:
                        mark_vars_read(expr->unary.value, lhs_ent);
@@ -1586,7 +1547,6 @@ unary:
                        return;
 
                EXPR_LITERAL_CASES
-               case EXPR_UNKNOWN:
                case EXPR_INVALID:
                case EXPR_STRING_LITERAL:
                case EXPR_WIDE_STRING_LITERAL:
@@ -1614,10 +1574,10 @@ static designator_t *parse_designation(void)
 
        for (;;) {
                designator_t *designator;
-               switch (token.type) {
+               switch (token.kind) {
                case '[':
                        designator = allocate_ast_zero(sizeof(designator[0]));
-                       designator->source_position = token.source_position;
+                       designator->source_position = token.base.source_position;
                        next_token();
                        add_anchor_token(']');
                        designator->array_index = parse_constant_expression();
@@ -1626,14 +1586,14 @@ static designator_t *parse_designation(void)
                        break;
                case '.':
                        designator = allocate_ast_zero(sizeof(designator[0]));
-                       designator->source_position = token.source_position;
+                       designator->source_position = token.base.source_position;
                        next_token();
-                       if (token.type != T_IDENTIFIER) {
+                       if (token.kind != T_IDENTIFIER) {
                                parse_error_expected("while parsing designator",
                                                     T_IDENTIFIER, NULL);
                                return NULL;
                        }
-                       designator->symbol = token.symbol;
+                       designator->symbol = token.identifier.symbol;
                        next_token();
                        break;
                default:
@@ -1731,13 +1691,12 @@ static initializer_t *initializer_from_expression(type_t *orig_type,
 }
 
 /**
- * Checks if a given expression can be used as an constant initializer.
+ * Checks if a given expression can be used as a constant initializer.
  */
 static bool is_initializer_constant(const expression_t *expression)
 {
-       return
-               is_constant_expression(expression) != EXPR_CLASS_VARIABLE ||
-               is_address_constant(expression)    != EXPR_CLASS_VARIABLE;
+       return is_constant_expression(expression) != EXPR_CLASS_VARIABLE ||
+              is_linker_constant(expression)     != EXPR_CLASS_VARIABLE;
 }
 
 /**
@@ -1750,12 +1709,12 @@ static initializer_t *parse_scalar_initializer(type_t *type,
 {
        /* there might be extra {} hierarchies */
        int braces = 0;
-       if (next_if('{')) {
-               if (warning.other)
-                       warningf(HERE, "extra curly braces around scalar initializer");
+       if (token.kind == '{') {
+               warningf(WARN_OTHER, HERE, "extra curly braces around scalar initializer");
                do {
+                       eat('{');
                        ++braces;
-               } while (next_if('{'));
+               } while (token.kind == '{');
        }
 
        expression_t *expression = parse_assignment_expression();
@@ -1779,9 +1738,9 @@ static initializer_t *parse_scalar_initializer(type_t *type,
        bool additional_warning_displayed = false;
        while (braces > 0) {
                next_if(',');
-               if (token.type != '}') {
-                       if (!additional_warning_displayed && warning.other) {
-                               warningf(HERE, "additional elements in scalar initializer");
+               if (token.kind != '}') {
+                       if (!additional_warning_displayed) {
+                               warningf(WARN_OTHER, HERE, "additional elements in scalar initializer");
                                additional_warning_displayed = true;
                        }
                }
@@ -1961,17 +1920,14 @@ static bool walk_designator(type_path_t *path, const designator_t *designator,
                                if (iter == NULL) {
                                        errorf(&designator->source_position,
                                               "'%T' has no member named '%Y'", orig_type, symbol);
-                                       goto failed;
+                                       return false;
                                }
                                assert(iter->kind == ENTITY_COMPOUND_MEMBER);
-                               if (used_in_offsetof) {
-                                       type_t *real_type = skip_typeref(iter->declaration.type);
-                                       if (real_type->kind == TYPE_BITFIELD) {
-                                               errorf(&designator->source_position,
-                                                      "offsetof designator '%Y' must not specify bitfield",
-                                                      symbol);
-                                               goto failed;
-                                       }
+                               if (used_in_offsetof && iter->compound_member.bitfield) {
+                                       errorf(&designator->source_position,
+                                                  "offsetof designator '%Y' must not specify bitfield",
+                                                  symbol);
+                                       return false;
                                }
 
                                top->type             = orig_type;
@@ -1988,7 +1944,7 @@ static bool walk_designator(type_path_t *path, const designator_t *designator,
                                               "[%E] designator used for non-array type '%T'",
                                               array_index, orig_type);
                                }
-                               goto failed;
+                               return false;
                        }
 
                        long index = fold_constant_to_int(array_index);
@@ -2017,9 +1973,6 @@ static bool walk_designator(type_path_t *path, const designator_t *designator,
                }
        }
        return true;
-
-failed:
-       return false;
 }
 
 static void advance_current_object(type_path_t *path, size_t top_path_level)
@@ -2078,10 +2031,10 @@ static void skip_initializers(void)
 {
        next_if('{');
 
-       while (token.type != '}') {
-               if (token.type == T_EOF)
+       while (token.kind != '}') {
+               if (token.kind == T_EOF)
                        return;
-               if (token.type == '{') {
+               if (token.kind == '{') {
                        eat_block();
                        continue;
                }
@@ -2103,7 +2056,7 @@ static initializer_t *parse_sub_initializer(type_path_t *path,
                type_t *outer_type, size_t top_path_level,
                parse_initializer_env_t *env)
 {
-       if (token.type == '}') {
+       if (token.kind == '}') {
                /* empty initializer */
                return create_empty_initializer();
        }
@@ -2121,14 +2074,14 @@ static initializer_t *parse_sub_initializer(type_path_t *path,
 
        while (true) {
                designator_t *designator = NULL;
-               if (token.type == '.' || token.type == '[') {
+               if (token.kind == '.' || token.kind == '[') {
                        designator = parse_designation();
                        goto finish_designator;
-               } else if (token.type == T_IDENTIFIER && look_ahead(1)->type == ':') {
+               } else if (token.kind == T_IDENTIFIER && look_ahead(1)->kind == ':') {
                        /* GNU-style designator ("identifier: value") */
                        designator = allocate_ast_zero(sizeof(designator[0]));
-                       designator->source_position = token.source_position;
-                       designator->symbol          = token.symbol;
+                       designator->source_position = token.base.source_position;
+                       designator->symbol          = token.identifier.symbol;
                        eat(T_IDENTIFIER);
                        eat(':');
 
@@ -2151,11 +2104,10 @@ finish_designator:
 
                initializer_t *sub;
 
-               if (token.type == '{') {
+               if (token.kind == '{') {
                        if (type != NULL && is_type_scalar(type)) {
                                sub = parse_scalar_initializer(type, env->must_be_constant);
                        } else {
-                               eat('{');
                                if (type == NULL) {
                                        if (env->entity != NULL) {
                                                errorf(HERE,
@@ -2164,8 +2116,11 @@ finish_designator:
                                        } else {
                                                errorf(HERE, "extra brace group at end of initializer");
                                        }
-                               } else
+                                       eat('{');
+                               } else {
+                                       eat('{');
                                        descend_into_subtype(path);
+                               }
 
                                add_anchor_token('}');
                                sub = parse_sub_initializer(path, orig_type, top_path_level+1,
@@ -2197,10 +2152,17 @@ finish_designator:
                                        goto error_parse_next;
                                type_t *const outer_type_skip = skip_typeref(outer_type);
                                if (is_type_compound(outer_type_skip) &&
-                                   !outer_type_skip->compound.compound->complete) {
+                                               !outer_type_skip->compound.compound->complete) {
                                        goto error_parse_next;
                                }
-                               goto error_excess;
+
+                               source_position_t const* const pos = &expression->base.source_position;
+                               if (env->entity != NULL) {
+                                       warningf(WARN_OTHER, pos, "excess elements in initializer for '%Y'", env->entity->base.symbol);
+                               } else {
+                                       warningf(WARN_OTHER, pos, "excess elements in initializer");
+                               }
+                               goto error_parse_next;
                        }
 
                        /* handle { "string" } special case */
@@ -2210,9 +2172,8 @@ finish_designator:
                                sub = initializer_from_expression(outer_type, expression);
                                if (sub != NULL) {
                                        next_if(',');
-                                       if (token.type != '}' && warning.other) {
-                                               warningf(HERE, "excessive elements in initializer for type '%T'",
-                                                                orig_type);
+                                       if (token.kind != '}') {
+                                               warningf(WARN_OTHER, HERE, "excessive elements in initializer for type '%T'", orig_type);
                                        }
                                        /* TODO: eat , ... */
                                        return sub;
@@ -2252,27 +2213,15 @@ finish_designator:
                                path->max_index = index;
                }
 
-               if (type != NULL) {
-                       /* append to initializers list */
-                       ARR_APP1(initializer_t*, initializers, sub);
-               } else {
-error_excess:
-                       if (warning.other) {
-                               if (env->entity != NULL) {
-                                       warningf(HERE, "excess elements in initializer for '%Y'",
-                                                env->entity->base.symbol);
-                               } else {
-                                       warningf(HERE, "excess elements in initializer");
-                               }
-                       }
-               }
+               /* append to initializers list */
+               ARR_APP1(initializer_t*, initializers, sub);
 
 error_parse_next:
-               if (token.type == '}') {
+               if (token.kind == '}') {
                        break;
                }
                expect(',', end_error);
-               if (token.type == '}') {
+               if (token.kind == '}') {
                        break;
                }
 
@@ -2331,7 +2280,7 @@ static initializer_t *parse_initializer(parse_initializer_env_t *env)
 
        if (is_type_scalar(type)) {
                result = parse_scalar_initializer(type, env->must_be_constant);
-       } else if (token.type == '{') {
+       } else if (token.kind == '{') {
                eat('{');
 
                type_path_t path;
@@ -2349,6 +2298,7 @@ static initializer_t *parse_initializer(parse_initializer_env_t *env)
                DEL_ARR_F(path.path);
 
                expect('}', end_error);
+end_error:;
        } else {
                /* parse_scalar_initializer() also works in this case: we simply
                 * have an expression without {} around it */
@@ -2394,8 +2344,6 @@ static initializer_t *parse_initializer(parse_initializer_env_t *env)
        }
 
        return result;
-end_error:
-       return NULL;
 }
 
 static void append_entity(scope_t *scope, entity_t *entity)
@@ -2412,57 +2360,50 @@ static void append_entity(scope_t *scope, entity_t *entity)
 
 static compound_t *parse_compound_type_specifier(bool is_struct)
 {
+       source_position_t const pos = *HERE;
        eat(is_struct ? T_struct : T_union);
 
        symbol_t    *symbol     = NULL;
        entity_t    *entity     = NULL;
        attribute_t *attributes = NULL;
 
-       if (token.type == T___attribute__) {
+       if (token.kind == T___attribute__) {
                attributes = parse_attributes(NULL);
        }
 
        entity_kind_tag_t const kind = is_struct ? ENTITY_STRUCT : ENTITY_UNION;
-       if (token.type == T_IDENTIFIER) {
+       if (token.kind == T_IDENTIFIER) {
                /* the compound has a name, check if we have seen it already */
-               symbol = token.symbol;
+               symbol = token.identifier.symbol;
+               entity = get_tag(symbol, kind);
                next_token();
 
-               entity = get_tag(symbol, kind);
                if (entity != NULL) {
                        if (entity->base.parent_scope != current_scope &&
-                           (token.type == '{' || token.type == ';')) {
+                           (token.kind == '{' || token.kind == ';')) {
                                /* we're in an inner scope and have a definition. Shadow
                                 * existing definition in outer scope */
                                entity = NULL;
-                       } else if (entity->compound.complete && token.type == '{') {
-                               assert(symbol != NULL);
-                               errorf(HERE, "multiple definitions of '%s %Y' (previous definition %P)",
-                                      is_struct ? "struct" : "union", symbol,
-                                      &entity->base.source_position);
+                       } else if (entity->compound.complete && token.kind == '{') {
+                               source_position_t const *const ppos = &entity->base.source_position;
+                               errorf(&pos, "multiple definitions of '%N' (previous definition %P)", entity, ppos);
                                /* clear members in the hope to avoid further errors */
                                entity->compound.members.entities = NULL;
                        }
                }
-       } else if (token.type != '{') {
-               if (is_struct) {
-                       parse_error_expected("while parsing struct type specifier",
-                                            T_IDENTIFIER, '{', NULL);
-               } else {
-                       parse_error_expected("while parsing union type specifier",
-                                            T_IDENTIFIER, '{', NULL);
-               }
+       } else if (token.kind != '{') {
+               char const *const msg =
+                       is_struct ? "while parsing struct type specifier" :
+                                   "while parsing union type specifier";
+               parse_error_expected(msg, T_IDENTIFIER, '{', NULL);
 
                return NULL;
        }
 
        if (entity == NULL) {
-               entity = allocate_entity_zero(kind);
-
+               entity = allocate_entity_zero(kind, NAMESPACE_TAG, symbol);
                entity->compound.alignment   = 1;
-               entity->base.namespc         = NAMESPACE_TAG;
-               entity->base.source_position = token.source_position;
-               entity->base.symbol          = symbol;
+               entity->base.source_position = pos;
                entity->base.parent_scope    = current_scope;
                if (symbol != NULL) {
                        environment_push(entity);
@@ -2470,7 +2411,7 @@ static compound_t *parse_compound_type_specifier(bool is_struct)
                append_entity(current_scope, entity);
        }
 
-       if (token.type == '{') {
+       if (token.kind == '{') {
                parse_compound_type_entries(&entity->compound);
 
                /* ISO/IEC 14882:1998(E) Â§7.1.3:5 */
@@ -2491,7 +2432,7 @@ static void parse_enum_entries(type_t *const enum_type)
 {
        eat('{');
 
-       if (token.type == '}') {
+       if (token.kind == '}') {
                errorf(HERE, "empty enum not allowed");
                next_token();
                return;
@@ -2499,18 +2440,18 @@ static void parse_enum_entries(type_t *const enum_type)
 
        add_anchor_token('}');
        do {
-               if (token.type != T_IDENTIFIER) {
+               if (token.kind != T_IDENTIFIER) {
                        parse_error_expected("while parsing enum entry", T_IDENTIFIER, NULL);
                        eat_block();
                        rem_anchor_token('}');
                        return;
                }
 
-               entity_t *entity             = allocate_entity_zero(ENTITY_ENUM_VALUE);
+               symbol_t *symbol       = token.identifier.symbol;
+               entity_t *const entity
+                       = allocate_entity_zero(ENTITY_ENUM_VALUE, NAMESPACE_NORMAL, symbol);
                entity->enum_value.enum_type = enum_type;
-               entity->base.namespc         = NAMESPACE_NORMAL;
-               entity->base.symbol          = token.symbol;
-               entity->base.source_position = token.source_position;
+               entity->base.source_position = token.base.source_position;
                next_token();
 
                if (next_if('=')) {
@@ -2523,7 +2464,7 @@ static void parse_enum_entries(type_t *const enum_type)
                }
 
                record_entity(entity, false);
-       } while (next_if(',') && token.type != '}');
+       } while (next_if(',') && token.kind != '}');
        rem_anchor_token('}');
 
        expect('}', end_error);
@@ -2534,25 +2475,26 @@ end_error:
 
 static type_t *parse_enum_specifier(void)
 {
-       entity_t *entity;
-       symbol_t *symbol;
+       source_position_t const pos = *HERE;
+       entity_t               *entity;
+       symbol_t               *symbol;
 
        eat(T_enum);
-       switch (token.type) {
+       switch (token.kind) {
                case T_IDENTIFIER:
-                       symbol = token.symbol;
+                       symbol = token.identifier.symbol;
+                       entity = get_tag(symbol, ENTITY_ENUM);
                        next_token();
 
-                       entity = get_tag(symbol, ENTITY_ENUM);
                        if (entity != NULL) {
                                if (entity->base.parent_scope != current_scope &&
-                                               (token.type == '{' || token.type == ';')) {
+                                               (token.kind == '{' || token.kind == ';')) {
                                        /* we're in an inner scope and have a definition. Shadow
                                         * existing definition in outer scope */
                                        entity = NULL;
-                               } else if (entity->enume.complete && token.type == '{') {
-                                       errorf(HERE, "multiple definitions of 'enum %Y' (previous definition %P)",
-                                                       symbol, &entity->base.source_position);
+                               } else if (entity->enume.complete && token.kind == '{') {
+                                       source_position_t const *const ppos = &entity->base.source_position;
+                                       errorf(&pos, "multiple definitions of '%N' (previous definition %P)", entity, ppos);
                                }
                        }
                        break;
@@ -2569,10 +2511,8 @@ static type_t *parse_enum_specifier(void)
        }
 
        if (entity == NULL) {
-               entity                       = allocate_entity_zero(ENTITY_ENUM);
-               entity->base.namespc         = NAMESPACE_TAG;
-               entity->base.source_position = token.source_position;
-               entity->base.symbol          = symbol;
+               entity = allocate_entity_zero(ENTITY_ENUM, NAMESPACE_TAG, symbol);
+               entity->base.source_position = pos;
                entity->base.parent_scope    = current_scope;
        }
 
@@ -2580,7 +2520,7 @@ static type_t *parse_enum_specifier(void)
        type->enumt.enume  = &entity->enume;
        type->enumt.akind  = ATOMIC_TYPE_INT;
 
-       if (token.type == '{') {
+       if (token.kind == '{') {
                if (symbol != NULL) {
                        environment_push(entity);
                }
@@ -2596,8 +2536,7 @@ static type_t *parse_enum_specifier(void)
                        anonymous_entity = entity;
                }
        } else if (!entity->enume.complete && !(c_mode & _GNUC)) {
-               errorf(HERE, "'enum %Y' used before definition (incomplete enums are a GNU extension)",
-                      symbol);
+               errorf(HERE, "'%T' used before definition (incomplete enums are a GNU extension)", type);
        }
 
        return type;
@@ -2623,18 +2562,10 @@ static type_t *parse_typeof(void)
 
        expression_t *expression  = NULL;
 
-       bool old_type_prop     = in_type_prop;
-       bool old_gcc_extension = in_gcc_extension;
-       in_type_prop           = true;
-
-       while (next_if(T___extension__)) {
-               /* This can be a prefix to a typename or an expression. */
-               in_gcc_extension = true;
-       }
-       switch (token.type) {
+       switch (token.kind) {
        case T_IDENTIFIER:
-               if (is_typedef_symbol(token.symbol)) {
-       TYPENAME_START
+               if (is_typedef_symbol(token.identifier.symbol)) {
+       DECLARATION_START
                        type = parse_typename();
                } else {
        default:
@@ -2643,8 +2574,6 @@ static type_t *parse_typeof(void)
                }
                break;
        }
-       in_type_prop     = old_type_prop;
-       in_gcc_extension = old_gcc_extension;
 
        rem_anchor_token(')');
        expect(')', end_error);
@@ -2700,34 +2629,31 @@ static attribute_t *parse_attribute_ms_property(attribute_t *attribute)
                = allocate_ast_zero(sizeof(*property));
 
        do {
-               if (token.type != T_IDENTIFIER) {
+               if (token.kind != T_IDENTIFIER) {
                        parse_error_expected("while parsing property declspec",
                                             T_IDENTIFIER, NULL);
                        goto end_error;
                }
 
-               bool is_put;
-               symbol_t *symbol = token.symbol;
-               next_token();
+               symbol_t **prop;
+               symbol_t  *symbol = token.identifier.symbol;
                if (strcmp(symbol->string, "put") == 0) {
-                       is_put = true;
+                       prop = &property->put_symbol;
                } else if (strcmp(symbol->string, "get") == 0) {
-                       is_put = false;
+                       prop = &property->get_symbol;
                } else {
                        errorf(HERE, "expected put or get in property declspec");
-                       goto end_error;
+                       prop = NULL;
                }
+               eat(T_IDENTIFIER);
                expect('=', end_error);
-               if (token.type != T_IDENTIFIER) {
+               if (token.kind != T_IDENTIFIER) {
                        parse_error_expected("while parsing property declspec",
                                             T_IDENTIFIER, NULL);
                        goto end_error;
                }
-               if (is_put) {
-                       property->put_symbol = token.symbol;
-               } else {
-                       property->get_symbol = token.symbol;
-               }
+               if (prop != NULL)
+                       *prop = token.identifier.symbol;
                next_token();
        } while (next_if(','));
 
@@ -2744,9 +2670,8 @@ static attribute_t *parse_microsoft_extended_decl_modifier_single(void)
        attribute_kind_t kind = ATTRIBUTE_UNKNOWN;
        if (next_if(T_restrict)) {
                kind = ATTRIBUTE_MS_RESTRICT;
-       } else if (token.type == T_IDENTIFIER) {
-               const char *name = token.symbol->string;
-               next_token();
+       } else if (token.kind == T_IDENTIFIER) {
+               const char *name = token.identifier.symbol->string;
                for (attribute_kind_t k = ATTRIBUTE_MS_FIRST; k <= ATTRIBUTE_MS_LAST;
                     ++k) {
                        const char *attribute_name = get_attribute_name(k);
@@ -2756,8 +2681,8 @@ static attribute_t *parse_microsoft_extended_decl_modifier_single(void)
                        }
                }
 
-               if (kind == ATTRIBUTE_UNKNOWN && warning.attribute) {
-                       warningf(HERE, "unknown __declspec '%s' ignored", name);
+               if (kind == ATTRIBUTE_UNKNOWN) {
+                       warningf(WARN_ATTRIBUTE, HERE, "unknown __declspec '%s' ignored", name);
                }
        } else {
                parse_error_expected("while parsing __declspec", T_IDENTIFIER, NULL);
@@ -2765,6 +2690,7 @@ static attribute_t *parse_microsoft_extended_decl_modifier_single(void)
        }
 
        attribute_t *attribute = allocate_attribute_zero(kind);
+       eat(T_IDENTIFIER);
 
        if (kind == ATTRIBUTE_MS_PROPERTY) {
                return parse_attribute_ms_property(attribute);
@@ -2813,10 +2739,8 @@ end_error:
 
 static entity_t *create_error_entity(symbol_t *symbol, entity_kind_tag_t kind)
 {
-       entity_t *entity             = allocate_entity_zero(kind);
-       entity->base.namespc         = NAMESPACE_NORMAL;
+       entity_t *const entity = allocate_entity_zero(kind, NAMESPACE_NORMAL, symbol);
        entity->base.source_position = *HERE;
-       entity->base.symbol          = symbol;
        if (is_declaration(entity)) {
                entity->declaration.type     = type_error_type;
                entity->declaration.implicit = true;
@@ -2831,19 +2755,19 @@ static entity_t *create_error_entity(symbol_t *symbol, entity_kind_tag_t kind)
 
 static void parse_declaration_specifiers(declaration_specifiers_t *specifiers)
 {
-       type_t            *type              = NULL;
-       type_qualifiers_t  qualifiers        = TYPE_QUALIFIER_NONE;
-       unsigned           type_specifiers   = 0;
-       bool               newtype           = false;
-       bool               saw_error         = false;
-       bool               old_gcc_extension = in_gcc_extension;
+       type_t            *type            = NULL;
+       type_qualifiers_t  qualifiers      = TYPE_QUALIFIER_NONE;
+       unsigned           type_specifiers = 0;
+       bool               newtype         = false;
+       bool               saw_error       = false;
 
-       specifiers->source_position = token.source_position;
+       memset(specifiers, 0, sizeof(*specifiers));
+       specifiers->source_position = token.base.source_position;
 
        while (true) {
                specifiers->attributes = parse_attributes(specifiers->attributes);
 
-               switch (token.type) {
+               switch (token.kind) {
                /* storage class */
 #define MATCH_STORAGE_CLASS(token, class)                                  \
                case token:                                                        \
@@ -2907,11 +2831,6 @@ wrong_thread_storage_class:
                MATCH_TYPE_QUALIFIER(T___uptr,   TYPE_QUALIFIER_UPTR);
                MATCH_TYPE_QUALIFIER(T___sptr,   TYPE_QUALIFIER_SPTR);
 
-               case T___extension__:
-                       next_token();
-                       in_gcc_extension = true;
-                       break;
-
                /* type specifiers */
 #define MATCH_SPECIFIER(token, specifier, name)                         \
                case token:                                                     \
@@ -2956,7 +2875,7 @@ wrong_thread_storage_class:
 
                case T_long:
                        if (type_specifiers & SPECIFIER_LONG_LONG) {
-                               errorf(HERE, "multiple type specifiers given");
+                               errorf(HERE, "too many long type specifiers given");
                        } else if (type_specifiers & SPECIFIER_LONG) {
                                type_specifiers |= SPECIFIER_LONG_LONG;
                        } else {
@@ -2965,9 +2884,8 @@ wrong_thread_storage_class:
                        next_token();
                        break;
 
-#define CHECK_DOUBLE_TYPE()        \
-                       if ( type != NULL)     \
-                               errorf(HERE, "multiple data types in declaration specifiers");
+#define CHECK_DOUBLE_TYPE() \
+       (type != NULL ? errorf(HERE, "multiple types in declaration specifiers") : (void)0)
 
                case T_struct:
                        CHECK_DOUBLE_TYPE();
@@ -3000,7 +2918,7 @@ wrong_thread_storage_class:
                                /* Be somewhat resilient to typos like 'unsigned lng* f()' in a
                                 * declaration, so it doesn't generate errors about expecting '(' or
                                 * '{' later on. */
-                               switch (look_ahead(1)->type) {
+                               switch (look_ahead(1)->kind) {
                                        STORAGE_CLASSES
                                        TYPE_SPECIFIERS
                                        case T_const:
@@ -3020,12 +2938,12 @@ wrong_thread_storage_class:
                                }
                        }
 
-                       type_t *const typedef_type = get_typedef_type(token.symbol);
+                       type_t *const typedef_type = get_typedef_type(token.identifier.symbol);
                        if (typedef_type == NULL) {
                                /* Be somewhat resilient to typos like 'vodi f()' at the beginning of a
                                 * declaration, so it doesn't generate 'implicit int' followed by more
                                 * errors later on. */
-                               token_type_t const la1_type = (token_type_t)look_ahead(1)->type;
+                               token_kind_t const la1_type = (token_kind_t)look_ahead(1)->kind;
                                switch (la1_type) {
                                        DECLARATION_START
                                        case T_IDENTIFIER:
@@ -3033,16 +2951,15 @@ wrong_thread_storage_class:
                                        case '*': {
                                                errorf(HERE, "%K does not name a type", &token);
 
-                                               entity_t *entity =
-                                                       create_error_entity(token.symbol, ENTITY_TYPEDEF);
+                                               symbol_t *symbol = token.identifier.symbol;
+                                               entity_t *entity
+                                                       = create_error_entity(symbol, ENTITY_TYPEDEF);
 
                                                type = allocate_type_zero(TYPE_TYPEDEF);
                                                type->typedeft.typedefe = &entity->typedefe;
 
                                                next_token();
                                                saw_error = true;
-                                               if (la1_type == '&' || la1_type == '*')
-                                                       goto finish_specifiers;
                                                continue;
                                        }
 
@@ -3065,8 +2982,6 @@ wrong_thread_storage_class:
 finish_specifiers:
        specifiers->attributes = parse_attributes(specifiers->attributes);
 
-       in_gcc_extension = old_gcc_extension;
-
        if (type == NULL || (saw_error && type_specifiers != 0)) {
                atomic_type_kind_t atomic_type;
 
@@ -3130,10 +3045,7 @@ finish_specifiers:
                        | SPECIFIER_INT:
                        atomic_type = ATOMIC_TYPE_ULONGLONG;
 warn_about_long_long:
-                       if (warning.long_long) {
-                               warningf(&specifiers->source_position,
-                                        "ISO C90 does not support 'long long'");
-                       }
+                       warningf(WARN_LONG_LONG, &specifiers->source_position, "ISO C90 does not support 'long long'");
                        break;
 
                case SPECIFIER_UNSIGNED | SPECIFIER_INT8:
@@ -3205,32 +3117,31 @@ warn_about_long_long:
                case SPECIFIER_LONG | SPECIFIER_DOUBLE | SPECIFIER_IMAGINARY:
                        atomic_type = ATOMIC_TYPE_LONG_DOUBLE;
                        break;
-               default:
+               default: {
                        /* invalid specifier combination, give an error message */
+                       source_position_t const* const pos = &specifiers->source_position;
                        if (type_specifiers == 0) {
-                               if (saw_error)
-                                       goto end_error;
-
-                               /* ISO/IEC 14882:1998(E) Â§C.1.5:4 */
-                               if (!(c_mode & _CXX) && !strict_mode) {
-                                       if (warning.implicit_int) {
-                                               warningf(HERE, "no type specifiers in declaration, using 'int'");
+                               if (!saw_error) {
+                                       /* ISO/IEC 14882:1998(E) Â§C.1.5:4 */
+                                       if (!(c_mode & _CXX) && !strict_mode) {
+                                               warningf(WARN_IMPLICIT_INT, pos, "no type specifiers in declaration, using 'int'");
+                                               atomic_type = ATOMIC_TYPE_INT;
+                                               break;
+                                       } else {
+                                               errorf(pos, "no type specifiers given in declaration");
                                        }
-                                       atomic_type = ATOMIC_TYPE_INT;
-                                       break;
-                               } else {
-                                       errorf(HERE, "no type specifiers given in declaration");
                                }
                        } else if ((type_specifiers & SPECIFIER_SIGNED) &&
                                  (type_specifiers & SPECIFIER_UNSIGNED)) {
-                               errorf(HERE, "signed and unsigned specifiers given");
+                               errorf(pos, "signed and unsigned specifiers given");
                        } else if (type_specifiers & (SPECIFIER_SIGNED | SPECIFIER_UNSIGNED)) {
-                               errorf(HERE, "only integer types can be signed or unsigned");
+                               errorf(pos, "only integer types can be signed or unsigned");
                        } else {
-                               errorf(HERE, "multiple datatypes in declaration");
+                               errorf(pos, "multiple datatypes in declaration");
                        }
                        goto end_error;
                }
+               }
 
                if (type_specifiers & SPECIFIER_COMPLEX) {
                        type                = allocate_type_zero(TYPE_COMPLEX);
@@ -3244,7 +3155,7 @@ warn_about_long_long:
                }
                newtype = true;
        } else if (type_specifiers != 0) {
-               errorf(HERE, "multiple datatypes in declaration");
+               errorf(&specifiers->source_position, "multiple datatypes in declaration");
        }
 
        /* FIXME: check type qualifiers here */
@@ -3270,7 +3181,7 @@ static type_qualifiers_t parse_type_qualifiers(void)
        type_qualifiers_t qualifiers = TYPE_QUALIFIER_NONE;
 
        while (true) {
-               switch (token.type) {
+               switch (token.kind) {
                /* type qualifiers */
                MATCH_TYPE_QUALIFIER(T_const,    TYPE_QUALIFIER_CONST);
                MATCH_TYPE_QUALIFIER(T_restrict, TYPE_QUALIFIER_RESTRICT);
@@ -3293,24 +3204,21 @@ static type_qualifiers_t parse_type_qualifiers(void)
  */
 static void parse_identifier_list(scope_t *scope)
 {
+       assert(token.kind == T_IDENTIFIER);
        do {
-               entity_t *entity = allocate_entity_zero(ENTITY_PARAMETER);
-               entity->base.source_position = token.source_position;
-               entity->base.namespc         = NAMESPACE_NORMAL;
-               entity->base.symbol          = token.symbol;
+               entity_t *const entity = allocate_entity_zero(ENTITY_PARAMETER, NAMESPACE_NORMAL, token.identifier.symbol);
+               entity->base.source_position = token.base.source_position;
                /* a K&R parameter has no type, yet */
                next_token();
 
                if (scope != NULL)
                        append_entity(scope, entity);
-       } while (next_if(',') && token.type == T_IDENTIFIER);
+       } while (next_if(',') && token.kind == T_IDENTIFIER);
 }
 
 static entity_t *parse_parameter(void)
 {
        declaration_specifiers_t specifiers;
-       memset(&specifiers, 0, sizeof(specifiers));
-
        parse_declaration_specifiers(&specifiers);
 
        entity_t *entity = parse_declarator(&specifiers,
@@ -3329,27 +3237,26 @@ static void semantic_parameter_incomplete(const entity_t *entity)
         *             incomplete type. */
        type_t *type = skip_typeref(entity->declaration.type);
        if (is_type_incomplete(type)) {
-               errorf(&entity->base.source_position,
-                               "parameter '%#T' has incomplete type",
-                               entity->declaration.type, entity->base.symbol);
+               errorf(&entity->base.source_position, "'%N' has incomplete type", entity);
        }
 }
 
 static bool has_parameters(void)
 {
        /* func(void) is not a parameter */
-       if (token.type == T_IDENTIFIER) {
-               entity_t const *const entity = get_entity(token.symbol, NAMESPACE_NORMAL);
+       if (token.kind == T_IDENTIFIER) {
+               entity_t const *const entity
+                       = get_entity(token.identifier.symbol, NAMESPACE_NORMAL);
                if (entity == NULL)
                        return true;
                if (entity->kind != ENTITY_TYPEDEF)
                        return true;
                if (skip_typeref(entity->typedefe.type) != type_void)
                        return true;
-       } else if (token.type != T_void) {
+       } else if (token.kind != T_void) {
                return true;
        }
-       if (look_ahead(1)->type != ')')
+       if (look_ahead(1)->kind != ')')
                return true;
        next_token();
        return false;
@@ -3365,9 +3272,9 @@ static void parse_parameters(function_type_t *type, scope_t *scope)
        add_anchor_token(')');
        int saved_comma_state = save_and_reset_anchor_state(',');
 
-       if (token.type == T_IDENTIFIER &&
-           !is_typedef_symbol(token.symbol)) {
-               token_type_t la1_type = (token_type_t)look_ahead(1)->type;
+       if (token.kind == T_IDENTIFIER
+           && !is_typedef_symbol(token.identifier.symbol)) {
+               token_kind_t la1_type = (token_kind_t)look_ahead(1)->kind;
                if (la1_type == ',' || la1_type == ')') {
                        type->kr_style_parameters = true;
                        parse_identifier_list(scope);
@@ -3375,24 +3282,20 @@ static void parse_parameters(function_type_t *type, scope_t *scope)
                }
        }
 
-       if (token.type == ')') {
+       if (token.kind == ')') {
                /* ISO/IEC 14882:1998(E) Â§C.1.6:1 */
                if (!(c_mode & _CXX))
                        type->unspecified_parameters = true;
-               goto parameters_finished;
-       }
-
-       if (has_parameters()) {
+       } else if (has_parameters()) {
                function_parameter_t **anchor = &type->parameters;
                do {
-                       switch (token.type) {
+                       switch (token.kind) {
                        case T_DOTDOTDOT:
                                next_token();
                                type->variadic = true;
                                goto parameters_finished;
 
                        case T_IDENTIFIER:
-                       case T___extension__:
                        DECLARATION_START
                        {
                                entity_t *entity = parse_parameter();
@@ -3423,7 +3326,6 @@ static void parse_parameters(function_type_t *type, scope_t *scope)
                } while (next_if(','));
        }
 
-
 parameters_finished:
        rem_anchor_token(')');
        expect(')', end_error);
@@ -3532,10 +3434,10 @@ static construct_type_t *parse_array_declarator(void)
        array->is_static       = is_static;
 
        expression_t *size = NULL;
-       if (token.type == '*' && look_ahead(1)->type == ']') {
+       if (token.kind == '*' && look_ahead(1)->kind == ']') {
                array->is_variable = true;
                next_token();
-       } else if (token.type != ']') {
+       } else if (token.kind != ']') {
                size = parse_assignment_expression();
 
                /* Â§6.7.5.2:1  Array size must have integer type */
@@ -3552,7 +3454,7 @@ static construct_type_t *parse_array_declarator(void)
        }
 
        if (is_static && size == NULL)
-               errorf(HERE, "static array parameters require a size");
+               errorf(&array->base.pos, "static array parameters require a size");
 
        rem_anchor_token(']');
        expect(']', end_error);
@@ -3602,7 +3504,7 @@ static construct_type_t *parse_inner_declarator(parse_declarator_env_t *env)
        for (;;) {
                construct_type_t *type;
                //variable_t       *based = NULL; /* MS __based extension */
-               switch (token.type) {
+               switch (token.kind) {
                        case '&':
                                type = parse_reference_declarator();
                                break;
@@ -3630,40 +3532,58 @@ static construct_type_t *parse_inner_declarator(parse_declarator_env_t *env)
 ptr_operator_end: ;
        construct_type_t *inner_types = NULL;
 
-       switch (token.type) {
+       switch (token.kind) {
        case T_IDENTIFIER:
                if (env->must_be_abstract) {
                        errorf(HERE, "no identifier expected in typename");
                } else {
-                       env->symbol          = token.symbol;
-                       env->source_position = token.source_position;
+                       env->symbol          = token.identifier.symbol;
+                       env->source_position = token.base.source_position;
                }
                next_token();
                break;
-       case '(':
-               /* Â§6.7.6:2 footnote 126:  Empty parentheses in a type name are
-                * interpreted as ``function with no parameter specification'', rather
-                * than redundant parentheses around the omitted identifier. */
-               if (look_ahead(1)->type != ')') {
-                       next_token();
-                       add_anchor_token(')');
-                       inner_types = parse_inner_declarator(env);
-                       if (inner_types != NULL) {
-                               /* All later declarators only modify the return type */
-                               env->must_be_abstract = true;
-                       }
-                       rem_anchor_token(')');
-                       expect(')', end_error);
-               } else if (!env->may_be_abstract) {
-                       errorf(HERE, "declarator must have a name");
-                       goto error_out;
+
+       case '(': {
+               /* Parenthesized declarator or function declarator? */
+               token_t const *const la1 = look_ahead(1);
+               switch (la1->kind) {
+                       case T_IDENTIFIER:
+                               if (is_typedef_symbol(la1->identifier.symbol)) {
+                       case ')':
+                                       /* Â§6.7.6:2 footnote 126:  Empty parentheses in a type name are
+                                        * interpreted as ``function with no parameter specification'', rather
+                                        * than redundant parentheses around the omitted identifier. */
+                       default:
+                                       /* Function declarator. */
+                                       if (!env->may_be_abstract) {
+                                               errorf(HERE, "function declarator must have a name");
+                                       }
+                               } else {
+                       case '&':
+                       case '(':
+                       case '*':
+                       case '[':
+                       case T___attribute__: /* FIXME __attribute__ might also introduce a parameter of a function declarator. */
+                                       /* Paranthesized declarator. */
+                                       next_token();
+                                       add_anchor_token(')');
+                                       inner_types = parse_inner_declarator(env);
+                                       if (inner_types != NULL) {
+                                               /* All later declarators only modify the return type */
+                                               env->must_be_abstract = true;
+                                       }
+                                       rem_anchor_token(')');
+                                       expect(')', end_error);
+                               }
+                               break;
                }
                break;
+       }
+
        default:
                if (env->may_be_abstract)
                        break;
                parse_error_expected("while parsing declarator", T_IDENTIFIER, '(', NULL);
-error_out:
                eat_until_anchor();
                return NULL;
        }
@@ -3672,7 +3592,7 @@ error_out:
 
        for (;;) {
                construct_type_t *type;
-               switch (token.type) {
+               switch (token.kind) {
                case '(': {
                        scope_t *scope = NULL;
                        if (!env->must_be_abstract) {
@@ -3728,8 +3648,8 @@ static type_t *construct_declarator_type(construct_type_t *construct_list,
                        } else if (is_type_array(skipped_return_type)) {
                                errorf(pos, "function returning array is not allowed");
                        } else {
-                               if (skipped_return_type->base.qualifiers != 0 && warning.other) {
-                                       warningf(pos, "type qualifiers in return type of function type are meaningless");
+                               if (skipped_return_type->base.qualifiers != 0) {
+                                       warningf(WARN_IGNORED_QUALIFIERS, pos, "type qualifiers in return type of function type are meaningless");
                                }
                        }
 
@@ -3776,30 +3696,28 @@ static type_t *construct_declarator_type(construct_type_t *construct_list,
 
                        if (size_expression != NULL) {
                                switch (is_constant_expression(size_expression)) {
-                                       case EXPR_CLASS_CONSTANT: {
-                                               long const size = fold_constant_to_int(size_expression);
-                                               array_type->array.size          = size;
-                                               array_type->array.size_constant = true;
-                                               /* Â§6.7.5.2:1  If the expression is a constant expression, it shall
-                                                * have a value greater than zero. */
-                                               if (size <= 0) {
-                                                       if (size < 0 || !GNU_MODE) {
-                                                               errorf(&size_expression->base.source_position,
-                                                                               "size of array must be greater than zero");
-                                                       } else if (warning.other) {
-                                                               warningf(&size_expression->base.source_position,
-                                                                               "zero length arrays are a GCC extension");
-                                                       }
-                                               }
-                                               break;
+                               case EXPR_CLASS_CONSTANT: {
+                                       long const size = fold_constant_to_int(size_expression);
+                                       array_type->array.size          = size;
+                                       array_type->array.size_constant = true;
+                                       /* Â§6.7.5.2:1  If the expression is a constant expression,
+                                        * it shall have a value greater than zero. */
+                                       if (size < 0) {
+                                               errorf(&size_expression->base.source_position,
+                                                          "size of array must be greater than zero");
+                                       } else if (size == 0 && !GNU_MODE) {
+                                               errorf(&size_expression->base.source_position,
+                                                          "size of array must be greater than zero (zero length arrays are a GCC extension)");
                                        }
+                                       break;
+                               }
 
-                                       case EXPR_CLASS_VARIABLE:
-                                               array_type->array.is_vla = true;
-                                               break;
+                               case EXPR_CLASS_VARIABLE:
+                                       array_type->array.is_vla = true;
+                                       break;
 
-                                       case EXPR_CLASS_ERROR:
-                                               break;
+                               case EXPR_CLASS_ERROR:
+                                       break;
                                }
                        }
 
@@ -3825,7 +3743,7 @@ static type_t *automatic_type_conversion(type_t *orig_type);
 static type_t *semantic_parameter(const source_position_t *pos,
                                   type_t *type,
                                   const declaration_specifiers_t *specifiers,
-                                  symbol_t *symbol)
+                                  entity_t const *const param)
 {
        /* Â§6.7.5.3:7  A declaration of a parameter as ``array of type''
         *             shall be adjusted to ``qualified pointer to type'',
@@ -3836,7 +3754,7 @@ static type_t *semantic_parameter(const source_position_t *pos,
        type = automatic_type_conversion(type);
 
        if (specifiers->is_inline && is_type_valid(type)) {
-               errorf(pos, "parameter '%#T' declared 'inline'", type, symbol);
+               errorf(pos, "'%N' declared 'inline'", param);
        }
 
        /* Â§6.9.1:6  The declarations in the declaration list shall contain
@@ -3846,7 +3764,7 @@ static type_t *semantic_parameter(const source_position_t *pos,
                        specifiers->storage_class != STORAGE_CLASS_NONE   &&
                        specifiers->storage_class != STORAGE_CLASS_REGISTER)
           ) {
-               errorf(pos, "invalid storage class for parameter '%#T'", type, symbol);
+               errorf(pos, "invalid storage class for '%N'", param);
        }
 
        /* delay test for incomplete type, because we might have (void)
@@ -3881,9 +3799,7 @@ static entity_t *parse_declarator(const declaration_specifiers_t *specifiers,
 
        entity_t *entity;
        if (specifiers->storage_class == STORAGE_CLASS_TYPEDEF) {
-               entity                       = allocate_entity_zero(ENTITY_TYPEDEF);
-               entity->base.namespc         = NAMESPACE_NORMAL;
-               entity->base.symbol          = env.symbol;
+               entity = allocate_entity_zero(ENTITY_TYPEDEF, NAMESPACE_NORMAL, env.symbol);
                entity->base.source_position = env.source_position;
                entity->typedefe.type        = orig_type;
 
@@ -3904,7 +3820,7 @@ static entity_t *parse_declarator(const declaration_specifiers_t *specifiers,
        } else {
                /* create a declaration type entity */
                if (flags & DECL_CREATE_COMPOUND_MEMBER) {
-                       entity = allocate_entity_zero(ENTITY_COMPOUND_MEMBER);
+                       entity = allocate_entity_zero(ENTITY_COMPOUND_MEMBER, NAMESPACE_NORMAL, env.symbol);
 
                        if (env.symbol != NULL) {
                                if (specifiers->is_inline && is_type_valid(type)) {
@@ -3920,15 +3836,13 @@ static entity_t *parse_declarator(const declaration_specifiers_t *specifiers,
                                }
                        }
                } else if (flags & DECL_IS_PARAMETER) {
-                       orig_type = semantic_parameter(&env.source_position, orig_type,
-                                                      specifiers, env.symbol);
-
-                       entity = allocate_entity_zero(ENTITY_PARAMETER);
+                       entity    = allocate_entity_zero(ENTITY_PARAMETER, NAMESPACE_NORMAL, env.symbol);
+                       orig_type = semantic_parameter(&env.source_position, orig_type, specifiers, entity);
                } else if (is_type_function(type)) {
-                       entity = allocate_entity_zero(ENTITY_FUNCTION);
-
-                       entity->function.is_inline  = specifiers->is_inline;
-                       entity->function.parameters = env.parameters;
+                       entity = allocate_entity_zero(ENTITY_FUNCTION, NAMESPACE_NORMAL, env.symbol);
+                       entity->function.is_inline      = specifiers->is_inline;
+                       entity->function.elf_visibility = default_visibility;
+                       entity->function.parameters     = env.parameters;
 
                        if (env.symbol != NULL) {
                                /* this needs fixes for C++ */
@@ -3939,19 +3853,17 @@ static entity_t *parse_declarator(const declaration_specifiers_t *specifiers,
                                                        specifiers->storage_class != STORAGE_CLASS_NONE   &&
                                                        (in_function_scope || specifiers->storage_class != STORAGE_CLASS_STATIC)
                                                )) {
-                                       errorf(&env.source_position,
-                                                       "invalid storage class for function '%Y'", env.symbol);
+                                       errorf(&env.source_position, "invalid storage class for '%N'", entity);
                                }
                        }
                } else {
-                       entity = allocate_entity_zero(ENTITY_VARIABLE);
-
-                       entity->variable.thread_local = specifiers->thread_local;
+                       entity = allocate_entity_zero(ENTITY_VARIABLE, NAMESPACE_NORMAL, env.symbol);
+                       entity->variable.elf_visibility = default_visibility;
+                       entity->variable.thread_local   = specifiers->thread_local;
 
                        if (env.symbol != NULL) {
                                if (specifiers->is_inline && is_type_valid(type)) {
-                                       errorf(&env.source_position,
-                                                       "variable '%Y' declared 'inline'", env.symbol);
+                                       errorf(&env.source_position, "'%N' declared 'inline'", entity);
                                }
 
                                bool invalid_storage_class = false;
@@ -3968,19 +3880,12 @@ static entity_t *parse_declarator(const declaration_specifiers_t *specifiers,
                                        }
                                }
                                if (invalid_storage_class) {
-                                       errorf(&env.source_position,
-                                                       "invalid storage class for variable '%Y'", env.symbol);
+                                       errorf(&env.source_position, "invalid storage class for variable '%N'", entity);
                                }
                        }
                }
 
-               if (env.symbol != NULL) {
-                       entity->base.symbol          = env.symbol;
-                       entity->base.source_position = env.source_position;
-               } else {
-                       entity->base.source_position = specifiers->source_position;
-               }
-               entity->base.namespc           = NAMESPACE_NORMAL;
+               entity->base.source_position   = env.symbol != NULL ? env.source_position : specifiers->source_position;
                entity->declaration.type       = orig_type;
                entity->declaration.alignment  = get_type_alignment(orig_type);
                entity->declaration.modifiers  = env.modifiers;
@@ -4033,30 +3938,28 @@ static void check_main(const entity_t *entity)
 {
        const source_position_t *pos = &entity->base.source_position;
        if (entity->kind != ENTITY_FUNCTION) {
-               warningf(pos, "'main' is not a function");
+               warningf(WARN_MAIN, pos, "'main' is not a function");
                return;
        }
 
        if (entity->declaration.storage_class == STORAGE_CLASS_STATIC) {
-               warningf(pos, "'main' is normally a non-static function");
+               warningf(WARN_MAIN, pos, "'main' is normally a non-static function");
        }
 
        type_t *type = skip_typeref(entity->declaration.type);
        assert(is_type_function(type));
 
-       function_type_t *func_type = &type->function;
-       if (!types_compatible(skip_typeref(func_type->return_type), type_int)) {
-               warningf(pos, "return type of 'main' should be 'int', but is '%T'",
-                        func_type->return_type);
+       function_type_t const *const func_type = &type->function;
+       type_t                *const ret_type  = func_type->return_type;
+       if (!types_compatible(skip_typeref(ret_type), type_int)) {
+               warningf(WARN_MAIN, pos, "return type of 'main' should be 'int', but is '%T'", ret_type);
        }
        const function_parameter_t *parm = func_type->parameters;
        if (parm != NULL) {
                type_t *const first_type        = skip_typeref(parm->type);
                type_t *const first_type_unqual = get_unqualified_type(first_type);
                if (!types_compatible(first_type_unqual, type_int)) {
-                       warningf(pos,
-                                "first argument of 'main' should be 'int', but is '%T'",
-                                parm->type);
+                       warningf(WARN_MAIN, pos, "first argument of 'main' should be 'int', but is '%T'", parm->type);
                }
                parm = parm->next;
                if (parm != NULL) {
@@ -4064,8 +3967,7 @@ static void check_main(const entity_t *entity)
                        type_t *const second_type_unqual
                                = get_unqualified_type(second_type);
                        if (!types_compatible(second_type_unqual, type_char_ptr_ptr)) {
-                               warningf(pos, "second argument of 'main' should be 'char**', but is '%T'",
-                                        parm->type);
+                               warningf(WARN_MAIN, pos, "second argument of 'main' should be 'char**', but is '%T'", parm->type);
                        }
                        parm = parm->next;
                        if (parm != NULL) {
@@ -4073,8 +3975,7 @@ static void check_main(const entity_t *entity)
                                type_t *const third_type_unqual
                                        = get_unqualified_type(third_type);
                                if (!types_compatible(third_type_unqual, type_char_ptr_ptr)) {
-                                       warningf(pos, "third argument of 'main' should be 'char**', but is '%T'",
-                                                parm->type);
+                                       warningf(WARN_MAIN, pos, "third argument of 'main' should be 'char**', but is '%T'", parm->type);
                                }
                                parm = parm->next;
                                if (parm != NULL)
@@ -4082,7 +3983,7 @@ static void check_main(const entity_t *entity)
                        }
                } else {
 warn_arg_count:
-                       warningf(pos, "'main' takes only zero, two or three arguments");
+                       warningf(WARN_MAIN, pos, "'main' takes only zero, two or three arguments");
                }
        }
 }
@@ -4098,9 +3999,9 @@ static bool is_sym_main(const symbol_t *const sym)
 static void error_redefined_as_different_kind(const source_position_t *pos,
                const entity_t *old, entity_kind_t new_kind)
 {
-       errorf(pos, "redeclaration of %s '%Y' as %s (declared %P)",
-              get_entity_kind_name(old->kind), old->base.symbol,
-              get_entity_kind_name(new_kind), &old->base.source_position);
+       char              const *const what = get_entity_kind_name(new_kind);
+       source_position_t const *const ppos = &old->base.source_position;
+       errorf(pos, "redeclaration of '%N' as %s (declared %P)", old, what, ppos);
 }
 
 static bool is_entity_valid(entity_t *const ent)
@@ -4178,35 +4079,30 @@ entity_t *record_entity(entity_t *entity, const bool is_definition)
 
                assert(is_type_function(type));
                if (type->function.unspecified_parameters &&
-                               warning.strict_prototypes &&
-                               previous_entity == NULL) {
-                       warningf(pos, "function declaration '%#T' is not a prototype",
-                                        orig_type, symbol);
+                   previous_entity == NULL               &&
+                   !entity->declaration.implicit) {
+                       warningf(WARN_STRICT_PROTOTYPES, pos, "function declaration '%#N' is not a prototype", entity);
                }
 
-               if (warning.main && current_scope == file_scope
-                               && is_sym_main(symbol)) {
+               if (current_scope == file_scope && is_sym_main(symbol)) {
                        check_main(entity);
                }
        }
 
-       if (is_declaration(entity) &&
-                       warning.nested_externs &&
-                       entity->declaration.storage_class == STORAGE_CLASS_EXTERN &&
-                       current_scope != file_scope) {
-               warningf(pos, "nested extern declaration of '%#T'",
-                        entity->declaration.type, symbol);
+       if (is_declaration(entity)                                    &&
+           entity->declaration.storage_class == STORAGE_CLASS_EXTERN &&
+           current_scope != file_scope                               &&
+           !entity->declaration.implicit) {
+               warningf(WARN_NESTED_EXTERNS, pos, "nested extern declaration of '%#N'", entity);
        }
 
        if (previous_entity != NULL) {
+               source_position_t const *const ppos = &previous_entity->base.source_position;
+
                if (previous_entity->base.parent_scope == &current_function->parameters &&
                                previous_entity->base.parent_scope->depth + 1 == current_scope->depth) {
                        assert(previous_entity->kind == ENTITY_PARAMETER);
-                       errorf(pos,
-                                       "declaration '%#T' redeclares the parameter '%#T' (declared %P)",
-                                       entity->declaration.type, symbol,
-                                       previous_entity->declaration.type, symbol,
-                                       &previous_entity->base.source_position);
+                       errorf(pos, "declaration of '%N' redeclares the '%N' (declared %P)", entity, previous_entity, ppos);
                        goto finish;
                }
 
@@ -4219,14 +4115,12 @@ entity_t *record_entity(entity_t *entity, const bool is_definition)
                                goto finish;
                        }
                        if (previous_entity->kind == ENTITY_ENUM_VALUE) {
-                               errorf(pos, "redeclaration of enum entry '%Y' (declared %P)",
-                                               symbol, &previous_entity->base.source_position);
+                               errorf(pos, "redeclaration of '%N' (declared %P)", entity, ppos);
                                goto finish;
                        }
                        if (previous_entity->kind == ENTITY_TYPEDEF) {
                                /* TODO: C++ allows this for exactly the same type */
-                               errorf(pos, "redefinition of typedef '%Y' (declared %P)",
-                                               symbol, &previous_entity->base.source_position);
+                               errorf(pos, "redefinition of '%N' (declared %P)", entity, ppos);
                                goto finish;
                        }
 
@@ -4247,27 +4141,19 @@ entity_t *record_entity(entity_t *entity, const bool is_definition)
                                return previous_entity;
                        }
 
-                       type_t *const orig_type = decl->type;
-                       assert(orig_type != NULL);
-                       type_t *const type      = skip_typeref(orig_type);
+                       type_t *const type      = skip_typeref(decl->type);
                        type_t *const prev_type = skip_typeref(prev_decl->type);
 
                        if (!types_compatible(type, prev_type)) {
-                               errorf(pos,
-                                               "declaration '%#T' is incompatible with '%#T' (declared %P)",
-                                               orig_type, symbol, prev_decl->type, symbol,
-                                               &previous_entity->base.source_position);
+                               errorf(pos, "declaration '%#N' is incompatible with '%#N' (declared %P)", entity, previous_entity, ppos);
                        } else {
                                unsigned old_storage_class = prev_decl->storage_class;
 
-                               if (warning.redundant_decls               &&
-                                               is_definition                     &&
+                               if (is_definition                     &&
                                                !prev_decl->used                  &&
                                                !(prev_decl->modifiers & DM_USED) &&
                                                prev_decl->storage_class == STORAGE_CLASS_STATIC) {
-                                       warningf(&previous_entity->base.source_position,
-                                                       "unnecessary static forward declaration for '%#T'",
-                                                       prev_decl->type, symbol);
+                                       warningf(WARN_REDUNDANT_DECLS, ppos, "unnecessary static forward declaration for '%#N'", previous_entity);
                                }
 
                                storage_class_t new_storage_class = decl->storage_class;
@@ -4288,11 +4174,8 @@ entity_t *record_entity(entity_t *entity, const bool is_definition)
 
                                                case STORAGE_CLASS_EXTERN:
                                                        if (is_definition) {
-                                                               if (warning.missing_prototypes &&
-                                                                               prev_type->function.unspecified_parameters &&
-                                                                               !is_sym_main(symbol)) {
-                                                                       warningf(pos, "no previous prototype for '%#T'",
-                                                                                       orig_type, symbol);
+                                                               if (prev_type->function.unspecified_parameters && !is_sym_main(symbol)) {
+                                                                       warningf(WARN_MISSING_PROTOTYPES, pos, "no previous prototype for '%#N'", entity);
                                                                }
                                                        } else if (new_storage_class == STORAGE_CLASS_NONE) {
                                                                new_storage_class = STORAGE_CLASS_EXTERN;
@@ -4316,20 +4199,14 @@ warn_redundant_declaration: ;
                                        if (has_new_attrs) {
                                                merge_in_attributes(decl, prev_decl->attributes);
                                        } else if (!is_definition        &&
-                                                       warning.redundant_decls  &&
                                                        is_type_valid(prev_type) &&
-                                                       strcmp(previous_entity->base.source_position.input_name,
-                                                               "<builtin>") != 0) {
-                                               warningf(pos,
-                                                        "redundant declaration for '%Y' (declared %P)",
-                                                        symbol, &previous_entity->base.source_position);
+                                                       strcmp(ppos->input_name, "<builtin>") != 0) {
+                                               warningf(WARN_REDUNDANT_DECLS, pos, "redundant declaration for '%Y' (declared %P)", symbol, ppos);
                                        }
                                } else if (current_function == NULL) {
                                        if (old_storage_class != STORAGE_CLASS_STATIC &&
                                                        new_storage_class == STORAGE_CLASS_STATIC) {
-                                               errorf(pos,
-                                                      "static declaration of '%Y' follows non-static declaration (declared %P)",
-                                                      symbol, &previous_entity->base.source_position);
+                                               errorf(pos, "static declaration of '%Y' follows non-static declaration (declared %P)", symbol, ppos);
                                        } else if (old_storage_class == STORAGE_CLASS_EXTERN) {
                                                prev_decl->storage_class          = STORAGE_CLASS_NONE;
                                                prev_decl->declared_storage_class = STORAGE_CLASS_NONE;
@@ -4342,12 +4219,9 @@ warn_redundant_declaration: ;
                                } else if (is_type_valid(prev_type)) {
                                        if (old_storage_class == new_storage_class) {
 error_redeclaration:
-                                               errorf(pos, "redeclaration of '%Y' (declared %P)",
-                                                               symbol, &previous_entity->base.source_position);
+                                               errorf(pos, "redeclaration of '%Y' (declared %P)", symbol, ppos);
                                        } else {
-                                               errorf(pos,
-                                                               "redeclaration of '%Y' with different linkage (declared %P)",
-                                                               symbol, &previous_entity->base.source_position);
+                                               errorf(pos, "redeclaration of '%Y' with different linkage (declared %P)", symbol, ppos);
                                        }
                                }
                        }
@@ -4359,32 +4233,30 @@ error_redeclaration:
                        return previous_entity;
                }
 
-               if (warning.shadow) {
-                       warningf(pos, "%s '%Y' shadows %s (declared %P)",
-                                       get_entity_kind_name(entity->kind), symbol,
-                                       get_entity_kind_name(previous_entity->kind),
-                                       &previous_entity->base.source_position);
+               warning_t why;
+               if (is_warn_on(why = WARN_SHADOW) ||
+                   (is_warn_on(why = WARN_SHADOW_LOCAL) && previous_entity->base.parent_scope != file_scope)) {
+                       char const *const what = get_entity_kind_name(previous_entity->kind);
+                       warningf(why, pos, "'%N' shadows %s (declared %P)", entity, what, ppos);
                }
        }
 
        if (entity->kind == ENTITY_FUNCTION) {
                if (is_definition &&
-                               entity->declaration.storage_class != STORAGE_CLASS_STATIC) {
-                       if (warning.missing_prototypes && !is_sym_main(symbol)) {
-                               warningf(pos, "no previous prototype for '%#T'",
-                                        entity->declaration.type, symbol);
-                       } else if (warning.missing_declarations && !is_sym_main(symbol)) {
-                               warningf(pos, "no previous declaration for '%#T'",
-                                        entity->declaration.type, symbol);
+                               entity->declaration.storage_class != STORAGE_CLASS_STATIC &&
+                               !is_sym_main(symbol)) {
+                       if (is_warn_on(WARN_MISSING_PROTOTYPES)) {
+                               warningf(WARN_MISSING_PROTOTYPES, pos, "no previous prototype for '%#N'", entity);
+                       } else {
+                               goto warn_missing_declaration;
                        }
                }
-       } else if (warning.missing_declarations &&
-                       entity->kind == ENTITY_VARIABLE &&
-                       current_scope == file_scope) {
-               declaration_t *declaration = &entity->declaration;
-               if (declaration->storage_class == STORAGE_CLASS_NONE) {
-                       warningf(pos, "no previous declaration for '%#T'",
-                                declaration->type, symbol);
+       } else if (entity->kind == ENTITY_VARIABLE) {
+               if (current_scope                     == file_scope &&
+                               entity->declaration.storage_class == STORAGE_CLASS_NONE &&
+                               !entity->declaration.implicit) {
+warn_missing_declaration:
+                       warningf(WARN_MISSING_DECLARATIONS, pos, "no previous declaration for '%#N'", entity);
                }
        }
 
@@ -4393,7 +4265,6 @@ finish:
        assert(current_scope != NULL);
 
        entity->base.parent_scope = current_scope;
-       entity->base.namespc      = NAMESPACE_NORMAL;
        environment_push(entity);
        append_entity(current_scope, entity);
 
@@ -4407,19 +4278,13 @@ static void parser_error_multiple_definition(entity_t *entity,
               entity->base.symbol, &entity->base.source_position);
 }
 
-static bool is_declaration_specifier(const token_t *token,
-                                     bool only_specifiers_qualifiers)
+static bool is_declaration_specifier(const token_t *token)
 {
-       switch (token->type) {
-               TYPE_SPECIFIERS
-               TYPE_QUALIFIERS
+       switch (token->kind) {
+               DECLARATION_START
                        return true;
                case T_IDENTIFIER:
-                       return is_typedef_symbol(token->symbol);
-
-               case T___extension__:
-               STORAGE_CLASSES
-                       return !only_specifiers_qualifiers;
+                       return is_typedef_symbol(token->identifier.symbol);
 
                default:
                        return false;
@@ -4431,14 +4296,12 @@ static void parse_init_declarator_rest(entity_t *entity)
        type_t *orig_type = type_error_type;
 
        if (entity->base.kind == ENTITY_TYPEDEF) {
-               errorf(&entity->base.source_position,
-                      "typedef '%Y' is initialized (use __typeof__ instead)",
-                      entity->base.symbol);
+               source_position_t const *const pos = &entity->base.source_position;
+               errorf(pos, "'%N' is initialized (use __typeof__ instead)", entity);
        } else {
                assert(is_declaration(entity));
                orig_type = entity->declaration.type;
        }
-       eat('=');
 
        type_t *type = skip_typeref(orig_type);
 
@@ -4446,6 +4309,7 @@ static void parse_init_declarator_rest(entity_t *entity)
                        && entity->variable.initializer != NULL) {
                parser_error_multiple_definition(entity, HERE);
        }
+       eat('=');
 
        declaration_t *const declaration = &entity->declaration;
        bool must_be_constant = false;
@@ -4455,9 +4319,8 @@ static void parse_init_declarator_rest(entity_t *entity)
        }
 
        if (is_type_function(type)) {
-               errorf(&entity->base.source_position,
-                      "function '%#T' is initialized like a variable",
-                      orig_type, entity->base.symbol);
+               source_position_t const *const pos = &entity->base.source_position;
+               errorf(pos, "'%N' is initialized like a variable", entity);
                orig_type = type_error_type;
        }
 
@@ -4465,10 +4328,8 @@ static void parse_init_declarator_rest(entity_t *entity)
        env.type             = orig_type;
        env.must_be_constant = must_be_constant;
        env.entity           = entity;
-       current_init_decl    = entity;
 
        initializer_t *initializer = parse_initializer(&env);
-       current_init_decl = NULL;
 
        if (entity->kind == ENTITY_VARIABLE) {
                /* Â§6.7.5:22  array initializers for arrays with unknown size
@@ -4485,31 +4346,28 @@ static void parse_anonymous_declaration_rest(
        eat(';');
        anonymous_entity = NULL;
 
-       if (warning.other) {
-               if (specifiers->storage_class != STORAGE_CLASS_NONE ||
-                               specifiers->thread_local) {
-                       warningf(&specifiers->source_position,
-                                "useless storage class in empty declaration");
-               }
+       source_position_t const *const pos = &specifiers->source_position;
+       if (specifiers->storage_class != STORAGE_CLASS_NONE ||
+                       specifiers->thread_local) {
+               warningf(WARN_OTHER, pos, "useless storage class in empty declaration");
+       }
 
-               type_t *type = specifiers->type;
-               switch (type->kind) {
-                       case TYPE_COMPOUND_STRUCT:
-                       case TYPE_COMPOUND_UNION: {
-                               if (type->compound.compound->base.symbol == NULL) {
-                                       warningf(&specifiers->source_position,
-                                                "unnamed struct/union that defines no instances");
-                               }
-                               break;
+       type_t *type = specifiers->type;
+       switch (type->kind) {
+               case TYPE_COMPOUND_STRUCT:
+               case TYPE_COMPOUND_UNION: {
+                       if (type->compound.compound->base.symbol == NULL) {
+                               warningf(WARN_OTHER, pos, "unnamed struct/union that defines no instances");
                        }
+                       break;
+               }
 
-                       case TYPE_ENUM:
-                               break;
+               case TYPE_ENUM:
+                       break;
 
-                       default:
-                               warningf(&specifiers->source_position, "empty declaration");
-                               break;
-               }
+               default:
+                       warningf(WARN_OTHER, pos, "empty declaration");
+                       break;
        }
 }
 
@@ -4525,8 +4383,7 @@ static void check_variable_type_complete(entity_t *ent)
                        decl->storage_class == STORAGE_CLASS_STATIC)
                return;
 
-       type_t *const orig_type = decl->type;
-       type_t *const type      = skip_typeref(orig_type);
+       type_t *const type = skip_typeref(decl->type);
        if (!is_type_incomplete(type))
                return;
 
@@ -4537,8 +4394,7 @@ static void check_variable_type_complete(entity_t *ent)
                return;
        }
 
-       errorf(&ent->base.source_position, "variable '%#T' has incomplete type",
-                       orig_type, ent->base.symbol);
+       errorf(&ent->base.source_position, "variable '%#N' has incomplete type", ent);
 }
 
 
@@ -4550,9 +4406,9 @@ static void parse_declaration_rest(entity_t *ndeclaration,
        add_anchor_token(';');
        add_anchor_token(',');
        while (true) {
-               entity_t *entity = finished_declaration(ndeclaration, token.type == '=');
+               entity_t *entity = finished_declaration(ndeclaration, token.kind == '=');
 
-               if (token.type == '=') {
+               if (token.kind == '=') {
                        parse_init_declarator_rest(entity);
                } else if (entity->kind == ENTITY_VARIABLE) {
                        /* ISO/IEC 14882:1998(E) Â§8.5.3:3  The initializer can be omitted
@@ -4561,9 +4417,8 @@ static void parse_declaration_rest(entity_t *ndeclaration,
                        if (decl->storage_class != STORAGE_CLASS_EXTERN) {
                                type_t *type = decl->type;
                                if (is_type_reference(skip_typeref(type))) {
-                                       errorf(&entity->base.source_position,
-                                                       "reference '%#T' must be initialized",
-                                                       type, entity->base.symbol);
+                                       source_position_t const *const pos = &entity->base.source_position;
+                                       errorf(pos, "reference '%#N' must be initialized", entity);
                                }
                        }
                }
@@ -4588,22 +4443,20 @@ end_error:
 static entity_t *finished_kr_declaration(entity_t *entity, bool is_definition)
 {
        symbol_t *symbol = entity->base.symbol;
-       if (symbol == NULL) {
-               errorf(HERE, "anonymous declaration not valid as function parameter");
+       if (symbol == NULL)
                return entity;
-       }
 
        assert(entity->base.namespc == NAMESPACE_NORMAL);
        entity_t *previous_entity = get_entity(symbol, NAMESPACE_NORMAL);
        if (previous_entity == NULL
                        || previous_entity->base.parent_scope != current_scope) {
-               errorf(HERE, "expected declaration of a function parameter, found '%Y'",
+               errorf(&entity->base.source_position, "expected declaration of a function parameter, found '%Y'",
                       symbol);
                return entity;
        }
 
        if (is_definition) {
-               errorf(HERE, "parameter '%Y' is initialised", entity->base.symbol);
+               errorf(HERE, "'%N' is initialised", entity);
        }
 
        return record_entity(entity, false);
@@ -4612,14 +4465,12 @@ static entity_t *finished_kr_declaration(entity_t *entity, bool is_definition)
 static void parse_declaration(parsed_declaration_func finished_declaration,
                               declarator_flags_t      flags)
 {
-       declaration_specifiers_t specifiers;
-       memset(&specifiers, 0, sizeof(specifiers));
-
        add_anchor_token(';');
+       declaration_specifiers_t specifiers;
        parse_declaration_specifiers(&specifiers);
        rem_anchor_token(';');
 
-       if (token.type == ';') {
+       if (token.kind == ';') {
                parse_anonymous_declaration_rest(&specifiers);
        } else {
                entity_t *entity = parse_declarator(&specifiers, flags);
@@ -4654,9 +4505,7 @@ static void parse_kr_declaration_list(entity_t *entity)
 
        add_anchor_token('{');
 
-       /* push function parameters */
-       size_t const  top       = environment_top();
-       scope_t      *old_scope = scope_push(&entity->function.parameters);
+       PUSH_SCOPE(&entity->function.parameters);
 
        entity_t *parameter = entity->function.parameters.entities;
        for ( ; parameter != NULL; parameter = parameter->base.next) {
@@ -4667,9 +4516,8 @@ static void parse_kr_declaration_list(entity_t *entity)
 
        /* parse declaration list */
        for (;;) {
-               switch (token.type) {
+               switch (token.kind) {
                        DECLARATION_START
-                       case T___extension__:
                        /* This covers symbols, which are no type, too, and results in
                         * better error messages.  The typical cases are misspelled type
                         * names and missing includes. */
@@ -4682,10 +4530,7 @@ static void parse_kr_declaration_list(entity_t *entity)
        }
 decl_list_end:
 
-       /* pop function parameters */
-       assert(current_scope == &entity->function.parameters);
-       scope_pop(old_scope);
-       environment_pop_to(top);
+       POP_SCOPE();
 
        /* update function type */
        type_t *new_type = duplicate_type(type);
@@ -4722,15 +4567,12 @@ decl_list_end:
 
                type_t *parameter_type = parameter->declaration.type;
                if (parameter_type == NULL) {
+                       source_position_t const* const pos = &parameter->base.source_position;
                        if (strict_mode) {
-                               errorf(HERE, "no type specified for function parameter '%Y'",
-                                      parameter->base.symbol);
+                               errorf(pos, "no type specified for function '%N'", parameter);
                                parameter_type = type_error_type;
                        } else {
-                               if (warning.implicit_int) {
-                                       warningf(HERE, "no type specified for function parameter '%Y', using 'int'",
-                                                parameter->base.symbol);
-                               }
+                               warningf(WARN_IMPLICIT_INT, pos, "no type specified for function parameter '%N', using 'int'", parameter);
                                parameter_type = type_int;
                        }
                        parameter->declaration.type = parameter_type;
@@ -4755,25 +4597,22 @@ decl_list_end:
                                parameter_type = not_promoted;
                        }
                }
-               function_parameter_t *const parameter
+               function_parameter_t *const function_parameter
                        = allocate_parameter(parameter_type);
 
-               *anchor = parameter;
-               anchor  = &parameter->next;
+               *anchor = function_parameter;
+               anchor  = &function_parameter->next;
        }
 
        new_type->function.parameters = parameters;
        new_type = identify_new_type(new_type);
 
-       if (warning.other && need_incompatible_warning) {
-               type_t *proto_type_type = proto_type->declaration.type;
-               warningf(HERE,
-                        "declaration '%#T' is incompatible with '%#T' (declared %P)",
-                        proto_type_type, proto_type->base.symbol,
-                        new_type, entity->base.symbol,
-                        &proto_type->base.source_position);
+       if (need_incompatible_warning) {
+               symbol_t          const *const sym  = entity->base.symbol;
+               source_position_t const *const pos  = &entity->base.source_position;
+               source_position_t const *const ppos = &proto_type->base.source_position;
+               warningf(WARN_OTHER, pos, "declaration '%#N' is incompatible with '%#T' (declared %P)", proto_type, new_type, sym, ppos);
        }
-
        entity->declaration.type = new_type;
 
        rem_anchor_token('{');
@@ -4789,9 +4628,8 @@ static void print_in_function(void)
 {
        if (first_err) {
                first_err = false;
-               diagnosticf("%s: In function '%Y':\n",
-                           current_function->base.base.source_position.input_name,
-                           current_function->base.base.symbol);
+               char const *const file = current_function->base.base.source_position.input_name;
+               diagnosticf("%s: In '%N':\n", file, (entity_t const*)current_function);
        }
 }
 
@@ -4809,16 +4647,14 @@ static void check_labels(void)
                        continue;
 
                label_t *label = goto_statement->label;
-
-               label->used = true;
                if (label->base.source_position.input_name == NULL) {
                        print_in_function();
-                       errorf(&goto_statement->base.source_position,
-                              "label '%Y' used but not defined", label->base.symbol);
+                       source_position_t const *const pos = &goto_statement->base.source_position;
+                       errorf(pos, "'%N' used but not defined", (entity_t const*)label);
                 }
        }
 
-       if (warning.unused_label) {
+       if (is_warn_on(WARN_UNUSED_LABEL)) {
                for (const label_statement_t *label_statement = label_first;
                         label_statement != NULL;
                         label_statement = label_statement->next) {
@@ -4826,14 +4662,14 @@ static void check_labels(void)
 
                        if (! label->used) {
                                print_in_function();
-                               warningf(&label_statement->base.source_position,
-                                        "label '%Y' defined but not used", label->base.symbol);
+                               source_position_t const *const pos = &label_statement->base.source_position;
+                               warningf(WARN_UNUSED_LABEL, pos, "'%N' defined but not used", (entity_t const*)label);
                        }
                }
        }
 }
 
-static void warn_unused_entity(entity_t *entity, entity_t *last)
+static void warn_unused_entity(warning_t const why, entity_t *entity, entity_t *const last)
 {
        entity_t const *const end = last != NULL ? last->base.next : NULL;
        for (; entity != end; entity = entity->base.next) {
@@ -4846,14 +4682,10 @@ static void warn_unused_entity(entity_t *entity, entity_t *last)
 
                if (!declaration->used) {
                        print_in_function();
-                       const char *what = get_entity_kind_name(entity->kind);
-                       warningf(&entity->base.source_position, "%s '%Y' is unused",
-                                what, entity->base.symbol);
+                       warningf(why, &entity->base.source_position, "'%N' is unused", entity);
                } else if (entity->kind == ENTITY_VARIABLE && !entity->variable.read) {
                        print_in_function();
-                       const char *what = get_entity_kind_name(entity->kind);
-                       warningf(&entity->base.source_position, "%s '%Y' is never read",
-                                what, entity->base.symbol);
+                       warningf(why, &entity->base.source_position, "'%N' is never read", entity);
                }
        }
 }
@@ -4865,13 +4697,12 @@ static void check_unused_variables(statement_t *const stmt, void *const env)
        switch (stmt->kind) {
                case STATEMENT_DECLARATION: {
                        declaration_statement_t const *const decls = &stmt->declaration;
-                       warn_unused_entity(decls->declarations_begin,
-                                          decls->declarations_end);
+                       warn_unused_entity(WARN_UNUSED_VARIABLE, decls->declarations_begin, decls->declarations_end);
                        return;
                }
 
                case STATEMENT_FOR:
-                       warn_unused_entity(stmt->fors.scope.entities, NULL);
+                       warn_unused_entity(WARN_UNUSED_VARIABLE, stmt->fors.scope.entities, NULL);
                        return;
 
                default:
@@ -4884,15 +4715,15 @@ static void check_unused_variables(statement_t *const stmt, void *const env)
  */
 static void check_declarations(void)
 {
-       if (warning.unused_parameter) {
+       if (is_warn_on(WARN_UNUSED_PARAMETER)) {
                const scope_t *scope = &current_function->parameters;
 
                /* do not issue unused warnings for main */
                if (!is_sym_main(current_function->base.base.symbol)) {
-                       warn_unused_entity(scope->entities, NULL);
+                       warn_unused_entity(WARN_UNUSED_PARAMETER, scope->entities, NULL);
                }
        }
-       if (warning.unused_variable) {
+       if (is_warn_on(WARN_UNUSED_VARIABLE)) {
                walk_statements(current_function->statement, check_unused_variables,
                                NULL);
        }
@@ -4998,9 +4829,6 @@ static bool expression_returns(expression_t const *const expr)
                        return
                                expression_returns(expr->binary.left) &&
                                expression_returns(expr->binary.right);
-
-               case EXPR_UNKNOWN:
-                       break;
        }
 
        panic("unhandled expression");
@@ -5052,15 +4880,15 @@ static void check_reachable(statement_t *const stmt)
                case STATEMENT_DECLARATION: {
                        declaration_statement_t const *const decl = &stmt->declaration;
                        entity_t                const *      ent  = decl->declarations_begin;
-                       entity_t                const *const last = decl->declarations_end;
+                       entity_t                const *const last_decl = decl->declarations_end;
                        if (ent != NULL) {
                                for (;; ent = ent->base.next) {
                                        if (ent->kind                 == ENTITY_VARIABLE &&
-                                                       ent->variable.initializer != NULL            &&
-                                                       !initializer_returns(ent->variable.initializer)) {
+                                           ent->variable.initializer != NULL            &&
+                                           !initializer_returns(ent->variable.initializer)) {
                                                return;
                                        }
-                                       if (ent == last)
+                                       if (ent == last_decl)
                                                break;
                                }
                        }
@@ -5305,12 +5133,11 @@ found_break_parent:
                        type_t *const type = skip_typeref(current_function->base.type);
                        assert(is_type_function(type));
                        type_t *const ret  = skip_typeref(type->function.return_type);
-                       if (warning.return_type                    &&
-                           !is_type_atomic(ret, ATOMIC_TYPE_VOID) &&
+                       if (!is_type_atomic(ret, ATOMIC_TYPE_VOID) &&
                            is_type_valid(ret)                     &&
                            !is_sym_main(current_function->base.base.symbol)) {
-                               warningf(&stmt->base.source_position,
-                                        "control reaches end of non-void function");
+                               source_position_t const *const pos = &stmt->base.source_position;
+                               warningf(WARN_RETURN_TYPE, pos, "control reaches end of non-void function");
                        }
                        return;
                }
@@ -5443,8 +5270,8 @@ static void check_unreachable(statement_t* const stmt, void *const env)
                        if (!stmt->base.reachable) {
                                expression_t const *const cond = stmt->do_while.condition;
                                if (determine_truth(cond) >= 0) {
-                                       warningf(&cond->base.source_position,
-                                                "condition of do-while-loop is unreachable");
+                                       source_position_t const *const pos = &cond->base.source_position;
+                                       warningf(WARN_UNREACHABLE_CODE, pos, "condition of do-while-loop is unreachable");
                                }
                        }
                        return;
@@ -5454,21 +5281,21 @@ static void check_unreachable(statement_t* const stmt, void *const env)
 
                        // if init and step are unreachable, cond is unreachable, too
                        if (!stmt->base.reachable && !fors->step_reachable) {
-                               warningf(&stmt->base.source_position, "statement is unreachable");
+                               goto warn_unreachable;
                        } else {
                                if (!stmt->base.reachable && fors->initialisation != NULL) {
-                                       warningf(&fors->initialisation->base.source_position,
-                                                "initialisation of for-statement is unreachable");
+                                       source_position_t const *const pos = &fors->initialisation->base.source_position;
+                                       warningf(WARN_UNREACHABLE_CODE, pos, "initialisation of for-statement is unreachable");
                                }
 
                                if (!fors->condition_reachable && fors->condition != NULL) {
-                                       warningf(&fors->condition->base.source_position,
-                                                "condition of for-statement is unreachable");
+                                       source_position_t const *const pos = &fors->condition->base.source_position;
+                                       warningf(WARN_UNREACHABLE_CODE, pos, "condition of for-statement is unreachable");
                                }
 
                                if (!fors->step_reachable && fors->step != NULL) {
-                                       warningf(&fors->step->base.source_position,
-                                                "step of for-statement is unreachable");
+                                       source_position_t const *const pos = &fors->step->base.source_position;
+                                       warningf(WARN_UNREACHABLE_CODE, pos, "step of for-statement is unreachable");
                                }
                        }
                        return;
@@ -5499,8 +5326,10 @@ static void check_unreachable(statement_t* const stmt, void *const env)
 
                default:
 warn_unreachable:
-                       if (!stmt->base.reachable)
-                               warningf(&stmt->base.source_position, "statement is unreachable");
+                       if (!stmt->base.reachable) {
+                               source_position_t const *const pos = &stmt->base.source_position;
+                               warningf(WARN_UNREACHABLE_CODE, pos, "statement is unreachable");
+                       }
                        return;
        }
 }
@@ -5509,15 +5338,13 @@ static void parse_external_declaration(void)
 {
        /* function-definitions and declarations both start with declaration
         * specifiers */
-       declaration_specifiers_t specifiers;
-       memset(&specifiers, 0, sizeof(specifiers));
-
        add_anchor_token(';');
+       declaration_specifiers_t specifiers;
        parse_declaration_specifiers(&specifiers);
        rem_anchor_token(';');
 
        /* must be a declaration */
-       if (token.type == ';') {
+       if (token.kind == ';') {
                parse_anonymous_declaration_rest(&specifiers);
                return;
        }
@@ -5536,7 +5363,7 @@ static void parse_external_declaration(void)
        rem_anchor_token(',');
 
        /* must be a declaration */
-       switch (token.type) {
+       switch (token.kind) {
                case ',':
                case ';':
                case '=':
@@ -5548,7 +5375,7 @@ static void parse_external_declaration(void)
        /* must be a function definition */
        parse_kr_declaration_list(ndeclaration);
 
-       if (token.type != '{') {
+       if (token.kind != '{') {
                parse_error_expected("while parsing function definition", '{', NULL);
                eat_until_matching_token(';');
                return;
@@ -5560,30 +5387,25 @@ static void parse_external_declaration(void)
 
        if (!is_type_function(type)) {
                if (is_type_valid(type)) {
-                       errorf(HERE, "declarator '%#T' has a body but is not a function type",
-                              type, ndeclaration->base.symbol);
+                       errorf(HERE, "declarator '%#N' has a body but is not a function type", ndeclaration);
                }
                eat_block();
                return;
-       } else if (is_typeref(orig_type)) {
-               /* Â§6.9.1:2 */
-               errorf(&ndeclaration->base.source_position,
-                               "type of function definition '%#T' is a typedef",
-                               orig_type, ndeclaration->base.symbol);
        }
 
-       if (warning.aggregate_return &&
-           is_type_compound(skip_typeref(type->function.return_type))) {
-               warningf(HERE, "function '%Y' returns an aggregate",
-                        ndeclaration->base.symbol);
+       source_position_t const *const pos = &ndeclaration->base.source_position;
+       if (is_typeref(orig_type)) {
+               /* Â§6.9.1:2 */
+               errorf(pos, "type of function definition '%#N' is a typedef", ndeclaration);
        }
-       if (warning.traditional && !type->function.unspecified_parameters) {
-               warningf(HERE, "traditional C rejects ISO C style function definition of function '%Y'",
-                       ndeclaration->base.symbol);
+
+       if (is_type_compound(skip_typeref(type->function.return_type))) {
+               warningf(WARN_AGGREGATE_RETURN, pos, "'%N' returns an aggregate", ndeclaration);
        }
-       if (warning.old_style_definition && type->function.unspecified_parameters) {
-               warningf(HERE, "old-style function definition '%Y'",
-                       ndeclaration->base.symbol);
+       if (type->function.unspecified_parameters) {
+               warningf(WARN_OLD_STYLE_DEFINITION, pos, "old-style definition of '%N'", ndeclaration);
+       } else {
+               warningf(WARN_TRADITIONAL, pos, "traditional C rejects ISO C style definition of '%N'", ndeclaration);
        }
 
        /* Â§6.7.5.3:14 a function definition with () means no
@@ -5608,9 +5430,7 @@ static void parse_external_declaration(void)
        assert(is_declaration(entity));
        type = skip_typeref(entity->declaration.type);
 
-       /* push function parameters and switch scope */
-       size_t const  top       = environment_top();
-       scope_t      *old_scope = scope_push(&function->parameters);
+       PUSH_SCOPE(&function->parameters);
 
        entity_t *parameter = function->parameters.entities;
        for (; parameter != NULL; parameter = parameter->base.next) {
@@ -5637,7 +5457,7 @@ static void parse_external_declaration(void)
                entity_t   *old_current_entity   = current_entity;
                current_function                 = function;
                current_entity                   = entity;
-               current_parent                   = NULL;
+               PUSH_PARENT(NULL);
 
                goto_first   = NULL;
                goto_anchor  = &goto_first;
@@ -5649,24 +5469,21 @@ static void parse_external_declaration(void)
                first_err = true;
                check_labels();
                check_declarations();
-               if (warning.return_type      ||
-                   warning.unreachable_code ||
-                   (warning.missing_noreturn
-                    && !(function->base.modifiers & DM_NORETURN))) {
+               if (is_warn_on(WARN_RETURN_TYPE)      ||
+                   is_warn_on(WARN_UNREACHABLE_CODE) ||
+                   (is_warn_on(WARN_MISSING_NORETURN) && !(function->base.modifiers & DM_NORETURN))) {
                        noreturn_candidate = true;
                        check_reachable(body);
-                       if (warning.unreachable_code)
+                       if (is_warn_on(WARN_UNREACHABLE_CODE))
                                walk_statements(body, check_unreachable, NULL);
-                       if (warning.missing_noreturn &&
-                           noreturn_candidate       &&
+                       if (noreturn_candidate &&
                            !(function->base.modifiers & DM_NORETURN)) {
-                               warningf(&body->base.source_position,
-                                        "function '%#T' is candidate for attribute 'noreturn'",
-                                        type, entity->base.symbol);
+                               source_position_t const *const pos = &body->base.source_position;
+                               warningf(WARN_MISSING_NORETURN, pos, "function '%#N' is candidate for attribute 'noreturn'", entity);
                        }
                }
 
-               assert(current_parent   == NULL);
+               POP_PARENT();
                assert(current_function == function);
                assert(current_entity   == entity);
                current_entity   = old_current_entity;
@@ -5674,49 +5491,7 @@ static void parse_external_declaration(void)
                label_pop_to(label_stack_top);
        }
 
-       assert(current_scope == &function->parameters);
-       scope_pop(old_scope);
-       environment_pop_to(top);
-}
-
-static type_t *make_bitfield_type(type_t *base_type, expression_t *size,
-                                  source_position_t *source_position,
-                                  const symbol_t *symbol)
-{
-       type_t *type = allocate_type_zero(TYPE_BITFIELD);
-
-       type->bitfield.base_type       = base_type;
-       type->bitfield.size_expression = size;
-
-       il_size_t bit_size;
-       type_t *skipped_type = skip_typeref(base_type);
-       if (!is_type_integer(skipped_type)) {
-               errorf(HERE, "bitfield base type '%T' is not an integer type",
-                      base_type);
-               bit_size = 0;
-       } else {
-               bit_size = get_type_size(base_type) * 8;
-       }
-
-       if (is_constant_expression(size) == EXPR_CLASS_CONSTANT) {
-               long v = fold_constant_to_int(size);
-               const symbol_t *user_symbol = symbol == NULL ? sym_anonymous : symbol;
-
-               if (v < 0) {
-                       errorf(source_position, "negative width in bit-field '%Y'",
-                              user_symbol);
-               } else if (v == 0 && symbol != NULL) {
-                       errorf(source_position, "zero width for bit-field '%Y'",
-                              user_symbol);
-               } else if (bit_size > 0 && (il_size_t)v > bit_size) {
-                       errorf(source_position, "width of '%Y' exceeds its type",
-                              user_symbol);
-               } else {
-                       type->bitfield.bit_size = v;
-               }
-       }
-
-       return type;
+       POP_SCOPE();
 }
 
 static entity_t *find_compound_entry(compound_t *compound, symbol_t *symbol)
@@ -5746,23 +5521,17 @@ static entity_t *find_compound_entry(compound_t *compound, symbol_t *symbol)
 static void check_deprecated(const source_position_t *source_position,
                              const entity_t *entity)
 {
-       if (!warning.deprecated_declarations)
-               return;
        if (!is_declaration(entity))
                return;
        if ((entity->declaration.modifiers & DM_DEPRECATED) == 0)
                return;
 
-       char const *const prefix = get_entity_kind_name(entity->kind);
-       const char *deprecated_string
-                       = get_deprecated_string(entity->declaration.attributes);
-       if (deprecated_string != NULL) {
-               warningf(source_position, "%s '%Y' is deprecated (declared %P): \"%s\"",
-                                prefix, entity->base.symbol, &entity->base.source_position,
-                                deprecated_string);
+       source_position_t const *const epos = &entity->base.source_position;
+       char              const *const msg  = get_deprecated_string(entity->declaration.attributes);
+       if (msg != NULL) {
+               warningf(WARN_DEPRECATED_DECLARATIONS, source_position, "'%N' is deprecated (declared %P): \"%s\"", entity, epos, msg);
        } else {
-               warningf(source_position, "%s '%Y' is deprecated (declared %P)", prefix,
-                                entity->base.symbol, &entity->base.source_position);
+               warningf(WARN_DEPRECATED_DECLARATIONS, source_position, "'%N' is deprecated (declared %P)", entity, epos);
        }
 }
 
@@ -5783,12 +5552,19 @@ static expression_t *create_select(const source_position_t *pos,
        type_t *entry_type = entry->declaration.type;
        type_t *res_type   = get_qualified_type(entry_type, qualifiers);
 
+       /* bitfields need special treatment */
+       if (entry->compound_member.bitfield) {
+               unsigned bit_size = entry->compound_member.bit_size;
+               /* if fewer bits than an int, convert to int (see Â§6.3.1.1) */
+               if (bit_size < get_atomic_type_size(ATOMIC_TYPE_INT) * BITS_PER_BYTE) {
+                       res_type = type_int;
+               }
+       }
+
        /* we always do the auto-type conversions; the & and sizeof parser contains
         * code to revert this! */
        select->base.type = automatic_type_conversion(res_type);
-       if (res_type->kind == TYPE_BITFIELD) {
-               select->base.type = res_type->bitfield.base_type;
-       }
+
 
        return select;
 }
@@ -5822,7 +5598,7 @@ static expression_t *find_create_select(const source_position_t *pos,
 
                        expression_t *sub_addr = create_select(pos, addr, qualifiers, iter);
                        sub_addr->base.source_position = *pos;
-                       sub_addr->select.implicit      = true;
+                       sub_addr->base.implicit        = true;
                        return find_create_select(pos, sub_addr, qualifiers, sub_compound,
                                                  symbol);
                }
@@ -5835,46 +5611,84 @@ static expression_t *find_create_select(const source_position_t *pos,
        return NULL;
 }
 
+static void parse_bitfield_member(entity_t *entity)
+{
+       eat(':');
+
+       expression_t *size = parse_constant_expression();
+       long          size_long;
+
+       assert(entity->kind == ENTITY_COMPOUND_MEMBER);
+       type_t *type = entity->declaration.type;
+       if (!is_type_integer(skip_typeref(type))) {
+               errorf(HERE, "bitfield base type '%T' is not an integer type",
+                          type);
+       }
+
+       if (is_constant_expression(size) != EXPR_CLASS_CONSTANT) {
+               /* error already reported by parse_constant_expression */
+               size_long = get_type_size(type) * 8;
+       } else {
+               size_long = fold_constant_to_int(size);
+
+               const symbol_t *symbol = entity->base.symbol;
+               const symbol_t *user_symbol
+                       = symbol == NULL ? sym_anonymous : symbol;
+               unsigned bit_size = get_type_size(type) * 8;
+               if (size_long < 0) {
+                       errorf(HERE, "negative width in bit-field '%Y'", user_symbol);
+               } else if (size_long == 0 && symbol != NULL) {
+                       errorf(HERE, "zero width for bit-field '%Y'", user_symbol);
+               } else if (bit_size > 0 && (unsigned)size_long > bit_size) {
+                       errorf(HERE, "width of bitfield '%Y' exceeds its type",
+                                  user_symbol);
+               } else {
+                       /* hope that people don't invent crazy types with more bits
+                        * than our struct can hold */
+                       assert(size_long <
+                                  (1 << sizeof(entity->compound_member.bit_size)*8));
+               }
+       }
+
+       entity->compound_member.bitfield = true;
+       entity->compound_member.bit_size = (unsigned char)size_long;
+}
+
 static void parse_compound_declarators(compound_t *compound,
                const declaration_specifiers_t *specifiers)
 {
        do {
                entity_t *entity;
 
-               if (token.type == ':') {
-                       source_position_t source_position = *HERE;
-                       next_token();
-
-                       type_t *base_type = specifiers->type;
-                       expression_t *size = parse_constant_expression();
+               if (token.kind == ':') {
+                       /* anonymous bitfield */
+                       type_t *type = specifiers->type;
+                       entity_t *entity = allocate_entity_zero(ENTITY_COMPOUND_MEMBER,
+                                                               NAMESPACE_NORMAL, NULL);
+                       entity->base.source_position               = *HERE;
+                       entity->declaration.declared_storage_class = STORAGE_CLASS_NONE;
+                       entity->declaration.storage_class          = STORAGE_CLASS_NONE;
+                       entity->declaration.type                   = type;
 
-                       type_t *type = make_bitfield_type(base_type, size,
-                                       &source_position, NULL);
+                       parse_bitfield_member(entity);
 
                        attribute_t  *attributes = parse_attributes(NULL);
                        attribute_t **anchor     = &attributes;
                        while (*anchor != NULL)
                                anchor = &(*anchor)->next;
                        *anchor = specifiers->attributes;
-
-                       entity = allocate_entity_zero(ENTITY_COMPOUND_MEMBER);
-                       entity->base.namespc                       = NAMESPACE_NORMAL;
-                       entity->base.source_position               = source_position;
-                       entity->declaration.declared_storage_class = STORAGE_CLASS_NONE;
-                       entity->declaration.storage_class          = STORAGE_CLASS_NONE;
-                       entity->declaration.type                   = type;
-                       entity->declaration.attributes             = attributes;
-
                        if (attributes != NULL) {
                                handle_entity_attributes(attributes, entity);
                        }
+                       entity->declaration.attributes = attributes;
+
                        append_entity(&compound->members, entity);
                } else {
                        entity = parse_declarator(specifiers,
                                        DECL_MAY_BE_ABSTRACT | DECL_CREATE_COMPOUND_MEMBER);
+                       source_position_t const *const pos = &entity->base.source_position;
                        if (entity->kind == ENTITY_TYPEDEF) {
-                               errorf(&entity->base.source_position,
-                                               "typedef not allowed as compound member");
+                               errorf(pos, "typedef not allowed as compound member");
                        } else {
                                assert(entity->kind == ENTITY_COMPOUND_MEMBER);
 
@@ -5883,39 +5697,27 @@ static void parse_compound_declarators(compound_t *compound,
                                if (symbol != NULL) {
                                        entity_t *prev = find_compound_entry(compound, symbol);
                                        if (prev != NULL) {
-                                               errorf(&entity->base.source_position,
-                                                               "multiple declarations of symbol '%Y' (declared %P)",
-                                                               symbol, &prev->base.source_position);
+                                               source_position_t const *const ppos = &prev->base.source_position;
+                                               errorf(pos, "multiple declarations of symbol '%Y' (declared %P)", symbol, ppos);
                                        }
                                }
 
-                               if (token.type == ':') {
-                                       source_position_t source_position = *HERE;
-                                       next_token();
-                                       expression_t *size = parse_constant_expression();
-
-                                       type_t *type          = entity->declaration.type;
-                                       type_t *bitfield_type = make_bitfield_type(type, size,
-                                                       &source_position, entity->base.symbol);
+                               if (token.kind == ':') {
+                                       parse_bitfield_member(entity);
 
                                        attribute_t *attributes = parse_attributes(NULL);
-                                       entity->declaration.type = bitfield_type;
                                        handle_entity_attributes(attributes, entity);
                                } else {
                                        type_t *orig_type = entity->declaration.type;
                                        type_t *type      = skip_typeref(orig_type);
                                        if (is_type_function(type)) {
-                                               errorf(&entity->base.source_position,
-                                                      "compound member '%Y' must not have function type '%T'",
-                                                               entity->base.symbol, orig_type);
+                                               errorf(pos, "'%N' must not have function type '%T'", entity, orig_type);
                                        } else if (is_type_incomplete(type)) {
                                                /* Â§6.7.2.1:16 flexible array member */
                                                if (!is_type_array(type)       ||
-                                                               token.type          != ';' ||
-                                                               look_ahead(1)->type != '}') {
-                                                       errorf(&entity->base.source_position,
-                                                              "compound member '%Y' has incomplete type '%T'",
-                                                                       entity->base.symbol, orig_type);
+                                                               token.kind          != ';' ||
+                                                               look_ahead(1)->kind != '}') {
+                                                       errorf(pos, "'%N' has incomplete type '%T'", entity, orig_type);
                                                }
                                        }
                                }
@@ -5935,35 +5737,40 @@ static void parse_compound_type_entries(compound_t *compound)
        eat('{');
        add_anchor_token('}');
 
-       while (token.type != '}') {
-               if (token.type == T_EOF) {
-                       errorf(HERE, "EOF while parsing struct");
-                       break;
-               }
-               declaration_specifiers_t specifiers;
-               memset(&specifiers, 0, sizeof(specifiers));
-               parse_declaration_specifiers(&specifiers);
+       for (;;) {
+               switch (token.kind) {
+                       DECLARATION_START
+                       case T___extension__:
+                       case T_IDENTIFIER: {
+                               PUSH_EXTENSION();
+                               declaration_specifiers_t specifiers;
+                               parse_declaration_specifiers(&specifiers);
+                               parse_compound_declarators(compound, &specifiers);
+                               POP_EXTENSION();
+                               break;
+                       }
 
-               parse_compound_declarators(compound, &specifiers);
+                       default:
+                               rem_anchor_token('}');
+                               expect('}', end_error);
+end_error:
+                               /* Â§6.7.2.1:7 */
+                               compound->complete = true;
+                               return;
+               }
        }
-       rem_anchor_token('}');
-       next_token();
-
-       /* Â§6.7.2.1:7 */
-       compound->complete = true;
 }
 
 static type_t *parse_typename(void)
 {
        declaration_specifiers_t specifiers;
-       memset(&specifiers, 0, sizeof(specifiers));
        parse_declaration_specifiers(&specifiers);
        if (specifiers.storage_class != STORAGE_CLASS_NONE
                        || specifiers.thread_local) {
                /* TODO: improve error message, user does probably not know what a
                 * storage class is...
                 */
-               errorf(HERE, "typename must not have a storage class");
+               errorf(&specifiers.source_position, "typename must not have a storage class");
        }
 
        type_t *result = parse_abstract_declarator(specifiers.type);
@@ -5984,7 +5791,7 @@ struct expression_parser_function_t {
        parse_expression_infix_function  infix_parser;
 };
 
-expression_parser_function_t expression_parsers[T_LAST_TOKEN];
+static expression_parser_function_t expression_parsers[T_LAST_TOKEN];
 
 /**
  * Prints an error message if an expression was expected but not read
@@ -5992,7 +5799,7 @@ expression_parser_function_t expression_parsers[T_LAST_TOKEN];
 static expression_t *expected_expression_error(void)
 {
        /* skip the error message if the error token was read */
-       if (token.type != T_ERROR) {
+       if (token.kind != T_ERROR) {
                errorf(HERE, "expected expression, got token %K", &token);
        }
        next_token();
@@ -6002,12 +5809,12 @@ static expression_t *expected_expression_error(void)
 
 static type_t *get_string_type(void)
 {
-       return warning.write_strings ? type_const_char_ptr : type_char_ptr;
+       return is_warn_on(WARN_WRITE_STRINGS) ? type_const_char_ptr : type_char_ptr;
 }
 
 static type_t *get_wide_string_type(void)
 {
-       return warning.write_strings ? type_const_wchar_t_ptr : type_wchar_t_ptr;
+       return is_warn_on(WARN_WRITE_STRINGS) ? type_const_wchar_t_ptr : type_wchar_t_ptr;
 }
 
 /**
@@ -6015,17 +5822,17 @@ static type_t *get_wide_string_type(void)
  */
 static expression_t *parse_string_literal(void)
 {
-       source_position_t begin   = token.source_position;
-       string_t          res     = token.literal;
-       bool              is_wide = (token.type == T_WIDE_STRING_LITERAL);
+       source_position_t begin   = token.base.source_position;
+       string_t          res     = token.string.string;
+       bool              is_wide = (token.kind == T_WIDE_STRING_LITERAL);
 
        next_token();
-       while (token.type == T_STRING_LITERAL
-                       || token.type == T_WIDE_STRING_LITERAL) {
-               warn_string_concat(&token.source_position);
-               res = concat_strings(&res, &token.literal);
+       while (token.kind == T_STRING_LITERAL
+                       || token.kind == T_WIDE_STRING_LITERAL) {
+               warn_string_concat(&token.base.source_position);
+               res = concat_strings(&res, &token.string.string);
                next_token();
-               is_wide |= token.type == T_WIDE_STRING_LITERAL;
+               is_wide |= token.kind == T_WIDE_STRING_LITERAL;
        }
 
        expression_t *literal;
@@ -6048,10 +5855,9 @@ static expression_t *parse_string_literal(void)
 static expression_t *parse_boolean_literal(bool value)
 {
        expression_t *literal = allocate_expression_zero(EXPR_LITERAL_BOOLEAN);
-       literal->base.source_position = token.source_position;
-       literal->base.type            = type_bool;
-       literal->literal.value.begin  = value ? "true" : "false";
-       literal->literal.value.size   = value ? 4 : 5;
+       literal->base.type           = type_bool;
+       literal->literal.value.begin = value ? "true" : "false";
+       literal->literal.value.size  = value ? 4 : 5;
 
        next_token();
        return literal;
@@ -6059,20 +5865,18 @@ static expression_t *parse_boolean_literal(bool value)
 
 static void warn_traditional_suffix(void)
 {
-       if (!warning.traditional)
-               return;
-       warningf(&token.source_position, "traditional C rejects the '%Y' suffix",
-                token.symbol);
+       warningf(WARN_TRADITIONAL, HERE, "traditional C rejects the '%S' suffix",
+                &token.number.suffix);
 }
 
 static void check_integer_suffix(void)
 {
-       symbol_t *suffix = token.symbol;
-       if (suffix == NULL)
+       const string_t *suffix = &token.number.suffix;
+       if (suffix->size == 0)
                return;
 
        bool not_traditional = false;
-       const char *c = suffix->string;
+       const char *c = suffix->begin;
        if (*c == 'l' || *c == 'L') {
                ++c;
                if (*c == *(c-1)) {
@@ -6096,8 +5900,8 @@ static void check_integer_suffix(void)
                }
        }
        if (*c != '\0') {
-               errorf(&token.source_position,
-                      "invalid suffix '%s' on integer constant", suffix->string);
+               errorf(&token.base.source_position,
+                      "invalid suffix '%S' on integer constant", suffix);
        } else if (not_traditional) {
                warn_traditional_suffix();
        }
@@ -6105,13 +5909,13 @@ static void check_integer_suffix(void)
 
 static type_t *check_floatingpoint_suffix(void)
 {
-       symbol_t *suffix = token.symbol;
-       type_t   *type   = type_double;
-       if (suffix == NULL)
+       const string_t *suffix = &token.number.suffix;
+       type_t         *type   = type_double;
+       if (suffix->size == 0)
                return type;
 
        bool not_traditional = false;
-       const char *c = suffix->string;
+       const char *c = suffix->begin;
        if (*c == 'f' || *c == 'F') {
                ++c;
                type = type_float;
@@ -6120,8 +5924,8 @@ static type_t *check_floatingpoint_suffix(void)
                type = type_long_double;
        }
        if (*c != '\0') {
-               errorf(&token.source_position,
-                      "invalid suffix '%s' on floatingpoint constant", suffix->string);
+               errorf(&token.base.source_position,
+                      "invalid suffix '%S' on floatingpoint constant", suffix);
        } else if (not_traditional) {
                warn_traditional_suffix();
        }
@@ -6137,7 +5941,7 @@ static expression_t *parse_number_literal(void)
        expression_kind_t  kind;
        type_t            *type;
 
-       switch (token.type) {
+       switch (token.kind) {
        case T_INTEGER:
                kind = EXPR_LITERAL_INTEGER;
                check_integer_suffix();
@@ -6166,10 +5970,9 @@ static expression_t *parse_number_literal(void)
        }
 
        expression_t *literal = allocate_expression_zero(kind);
-       literal->base.source_position = token.source_position;
-       literal->base.type            = type;
-       literal->literal.value        = token.literal;
-       literal->literal.suffix       = token.symbol;
+       literal->base.type      = type;
+       literal->literal.value  = token.number.number;
+       literal->literal.suffix = token.number.suffix;
        next_token();
 
        /* integer type depends on the size of the number and the size
@@ -6186,17 +5989,16 @@ static expression_t *parse_number_literal(void)
 static expression_t *parse_character_constant(void)
 {
        expression_t *literal = allocate_expression_zero(EXPR_LITERAL_CHARACTER);
-       literal->base.source_position = token.source_position;
-       literal->base.type            = c_mode & _CXX ? type_char : type_int;
-       literal->literal.value        = token.literal;
+       literal->base.type     = c_mode & _CXX ? type_char : type_int;
+       literal->literal.value = token.string.string;
 
        size_t len = literal->literal.value.size;
-       if (len != 1) {
+       if (len > 1) {
                if (!GNU_MODE && !(c_mode & _C99)) {
                        errorf(HERE, "more than 1 character in character constant");
-               } else if (warning.multichar) {
+               } else {
                        literal->base.type = type_int;
-                       warningf(HERE, "multi-character character constant");
+                       warningf(WARN_MULTICHAR, HERE, "multi-character character constant");
                }
        }
 
@@ -6210,13 +6012,12 @@ static expression_t *parse_character_constant(void)
 static expression_t *parse_wide_character_constant(void)
 {
        expression_t *literal = allocate_expression_zero(EXPR_LITERAL_WIDE_CHARACTER);
-       literal->base.source_position = token.source_position;
-       literal->base.type            = type_int;
-       literal->literal.value        = token.literal;
+       literal->base.type     = type_int;
+       literal->literal.value = token.string.string;
 
        size_t len = wstrlen(&literal->literal.value);
-       if (len != 1) {
-               warningf(HERE, "multi-character character constant");
+       if (len > 1) {
+               warningf(WARN_MULTICHAR, HERE, "multi-character character constant");
        }
 
        next_token();
@@ -6232,21 +6033,15 @@ static entity_t *create_implicit_function(symbol_t *symbol,
        ntype->function.linkage                = LINKAGE_C;
        type_t *type                           = identify_new_type(ntype);
 
-       entity_t *entity = allocate_entity_zero(ENTITY_FUNCTION);
+       entity_t *const entity = allocate_entity_zero(ENTITY_FUNCTION, NAMESPACE_NORMAL, symbol);
        entity->declaration.storage_class          = STORAGE_CLASS_EXTERN;
        entity->declaration.declared_storage_class = STORAGE_CLASS_EXTERN;
        entity->declaration.type                   = type;
        entity->declaration.implicit               = true;
-       entity->base.namespc                       = NAMESPACE_NORMAL;
-       entity->base.symbol                        = symbol;
        entity->base.source_position               = *source_position;
 
-       if (current_scope != NULL) {
-               bool strict_prototypes_old = warning.strict_prototypes;
-               warning.strict_prototypes  = false;
+       if (current_scope != NULL)
                record_entity(entity, false);
-               warning.strict_prototypes = strict_prototypes_old;
-       }
 
        return entity;
 }
@@ -6296,8 +6091,7 @@ type_t *revert_automatic_type_conversion(const expression_t *expression)
                entity_t *entity = expression->select.compound_entry;
                assert(is_declaration(entity));
                type_t   *type   = entity->declaration.type;
-               return get_qualified_type(type,
-                               expression->base.type->base.qualifiers);
+               return get_qualified_type(type, expression->base.type->base.qualifiers);
        }
 
        case EXPR_UNARY_DEREFERENCE: {
@@ -6350,7 +6144,8 @@ static entity_t *lookup_entity(const scope_t *scope, symbol_t *symbol,
           construct a hashmap here... */
        entity_t *entity = scope->entities;
        for ( ; entity != NULL; entity = entity->base.next) {
-               if (entity->base.symbol == symbol && entity->base.namespc == namespc)
+               if (entity->base.symbol == symbol
+                   && (namespace_tag_t)entity->base.namespc == namespc)
                        break;
        }
 
@@ -6369,11 +6164,11 @@ static entity_t *parse_qualified_identifier(void)
 
        entity_t *entity;
        while (true) {
-               if (token.type != T_IDENTIFIER) {
+               if (token.kind != T_IDENTIFIER) {
                        parse_error_expected("while parsing identifier", T_IDENTIFIER, NULL);
                        return create_error_entity(sym_anonymous, ENTITY_VARIABLE);
                }
-               symbol = token.symbol;
+               symbol = token.identifier.symbol;
                pos    = *HERE;
                next_token();
 
@@ -6395,19 +6190,18 @@ static entity_t *parse_qualified_identifier(void)
                default:
                        errorf(&pos, "'%Y' must be a namespace, class, struct or union (but is a %s)",
                               symbol, get_entity_kind_name(entity->kind));
-                       goto end_error;
+
+                       /* skip further qualifications */
+                       while (next_if(T_IDENTIFIER) && next_if(T_COLONCOLON)) {}
+
+                       return create_error_entity(sym_anonymous, ENTITY_VARIABLE);
                }
        }
 
        if (entity == NULL) {
-               if (!strict_mode && token.type == '(') {
+               if (!strict_mode && token.kind == '(') {
                        /* an implicitly declared function */
-                       if (warning.error_implicit_function_declaration) {
-                               errorf(&pos, "implicit declaration of function '%Y'", symbol);
-                       } else if (warning.implicit_function_declaration) {
-                               warningf(&pos, "implicit declaration of function '%Y'", symbol);
-                       }
-
+                       warningf(WARN_IMPLICIT_FUNCTION_DECLARATION, &pos, "implicit declaration of function '%Y'", symbol);
                        entity = create_implicit_function(symbol, &pos);
                } else {
                        errorf(&pos, "unknown identifier '%Y' found.", symbol);
@@ -6416,17 +6210,12 @@ static entity_t *parse_qualified_identifier(void)
        }
 
        return entity;
-
-end_error:
-       /* skip further qualifications */
-       while (next_if(T_IDENTIFIER) && next_if(T_COLONCOLON)) {}
-
-       return create_error_entity(sym_anonymous, ENTITY_VARIABLE);
 }
 
 static expression_t *parse_reference(void)
 {
-       entity_t *entity = parse_qualified_identifier();
+       source_position_t const pos    = token.base.source_position;
+       entity_t         *const entity = parse_qualified_identifier();
 
        type_t *orig_type;
        if (is_declaration(entity)) {
@@ -6445,9 +6234,10 @@ static expression_t *parse_reference(void)
        if (entity->kind == ENTITY_ENUM_VALUE)
                kind = EXPR_REFERENCE_ENUM_VALUE;
 
-       expression_t *expression     = allocate_expression_zero(kind);
-       expression->reference.entity = entity;
-       expression->base.type        = type;
+       expression_t *expression         = allocate_expression_zero(kind);
+       expression->base.source_position = pos;
+       expression->base.type            = type;
+       expression->reference.entity     = entity;
 
        /* this declaration is used */
        if (is_declaration(entity)) {
@@ -6467,14 +6257,7 @@ static expression_t *parse_reference(void)
                current_function->need_closure = true;
        }
 
-       check_deprecated(HERE, entity);
-
-       if (warning.init_self && entity == current_init_decl && !in_type_prop
-           && entity->kind == ENTITY_VARIABLE) {
-               current_init_decl = NULL;
-               warningf(HERE, "variable '%#T' is initialized by itself",
-                        entity->declaration.type, entity->base.symbol);
-       }
+       check_deprecated(&pos, entity);
 
        return expression;
 }
@@ -6511,25 +6294,22 @@ static bool semantic_cast(expression_t *cast)
                return false;
        }
 
-       if (warning.cast_qual &&
-           is_type_pointer(src_type) &&
-           is_type_pointer(dst_type)) {
+       if (is_type_pointer(src_type) && is_type_pointer(dst_type)) {
                type_t *src = skip_typeref(src_type->pointer.points_to);
                type_t *dst = skip_typeref(dst_type->pointer.points_to);
                unsigned missing_qualifiers =
                        src->base.qualifiers & ~dst->base.qualifiers;
                if (missing_qualifiers != 0) {
-                       warningf(pos,
-                                "cast discards qualifiers '%Q' in pointer target type of '%T'",
-                                missing_qualifiers, orig_type_right);
+                       warningf(WARN_CAST_QUAL, pos, "cast discards qualifiers '%Q' in pointer target type of '%T'", missing_qualifiers, orig_type_right);
                }
        }
        return true;
 }
 
-static expression_t *parse_compound_literal(type_t *type)
+static expression_t *parse_compound_literal(source_position_t const *const pos, type_t *type)
 {
        expression_t *expression = allocate_expression_zero(EXPR_COMPOUND_LITERAL);
+       expression->base.source_position = *pos;
 
        parse_initializer_env_t env;
        env.type             = type;
@@ -6550,21 +6330,22 @@ static expression_t *parse_compound_literal(type_t *type)
  */
 static expression_t *parse_cast(void)
 {
-       add_anchor_token(')');
+       source_position_t const pos = *HERE;
 
-       source_position_t source_position = token.source_position;
+       eat('(');
+       add_anchor_token(')');
 
        type_t *type = parse_typename();
 
        rem_anchor_token(')');
        expect(')', end_error);
 
-       if (token.type == '{') {
-               return parse_compound_literal(type);
+       if (token.kind == '{') {
+               return parse_compound_literal(&pos, type);
        }
 
        expression_t *cast = allocate_expression_zero(EXPR_UNARY_CAST);
-       cast->base.source_position = source_position;
+       cast->base.source_position = pos;
 
        expression_t *value = parse_subexpression(PREC_CAST);
        cast->base.type   = type;
@@ -6584,10 +6365,11 @@ end_error:
  */
 static expression_t *parse_statement_expression(void)
 {
-       add_anchor_token(')');
-
        expression_t *expression = allocate_expression_zero(EXPR_STATEMENT);
 
+       eat('(');
+       add_anchor_token(')');
+
        statement_t *statement          = parse_compound_statement(true);
        statement->compound.stmt_expr   = true;
        expression->statement.statement = statement;
@@ -6602,8 +6384,9 @@ static expression_t *parse_statement_expression(void)
                if (stmt->kind == STATEMENT_EXPRESSION) {
                        type = stmt->expression.expression->base.type;
                }
-       } else if (warning.other) {
-               warningf(&expression->base.source_position, "empty statement expression ({})");
+       } else {
+               source_position_t const *const pos = &expression->base.source_position;
+               warningf(WARN_OTHER, pos, "empty statement expression ({})");
        }
        expression->base.type = type;
 
@@ -6619,22 +6402,20 @@ end_error:
  */
 static expression_t *parse_parenthesized_expression(void)
 {
-       eat('(');
-
-       switch (token.type) {
+       token_t const* const la1 = look_ahead(1);
+       switch (la1->kind) {
        case '{':
                /* gcc extension: a statement expression */
                return parse_statement_expression();
 
-       TYPE_QUALIFIERS
-       TYPE_SPECIFIERS
-               return parse_cast();
        case T_IDENTIFIER:
-               if (is_typedef_symbol(token.symbol)) {
+               if (is_typedef_symbol(la1->identifier.symbol)) {
+       DECLARATION_START
                        return parse_cast();
                }
        }
 
+       eat('(');
        add_anchor_token(')');
        expression_t *result = parse_expression();
        result->base.parenthesized = true;
@@ -6712,25 +6493,25 @@ static designator_t *parse_designator(void)
        designator_t *result    = allocate_ast_zero(sizeof(result[0]));
        result->source_position = *HERE;
 
-       if (token.type != T_IDENTIFIER) {
+       if (token.kind != T_IDENTIFIER) {
                parse_error_expected("while parsing member designator",
                                     T_IDENTIFIER, NULL);
                return NULL;
        }
-       result->symbol = token.symbol;
+       result->symbol = token.identifier.symbol;
        next_token();
 
        designator_t *last_designator = result;
        while (true) {
                if (next_if('.')) {
-                       if (token.type != T_IDENTIFIER) {
+                       if (token.kind != T_IDENTIFIER) {
                                parse_error_expected("while parsing member designator",
                                                     T_IDENTIFIER, NULL);
                                return NULL;
                        }
                        designator_t *designator    = allocate_ast_zero(sizeof(result[0]));
                        designator->source_position = *HERE;
-                       designator->symbol          = token.symbol;
+                       designator->symbol          = token.identifier.symbol;
                        next_token();
 
                        last_designator->next = designator;
@@ -6943,7 +6724,7 @@ static expression_t *parse_compare_builtin(void)
 {
        expression_t *expression;
 
-       switch (token.type) {
+       switch (token.kind) {
        case T___builtin_isgreater:
                expression = allocate_expression_zero(EXPR_BINARY_ISGREATER);
                break;
@@ -7015,40 +6796,27 @@ end_error:
 }
 
 /**
- * Return the declaration for a given label symbol or create a new one.
- *
- * @param symbol  the symbol of the label
+ * Return the label for the current symbol or create a new one.
  */
-static label_t *get_label(symbol_t *symbol)
+static label_t *get_label(void)
 {
-       entity_t *label;
+       assert(token.kind == T_IDENTIFIER);
        assert(current_function != NULL);
 
-       label = get_entity(symbol, NAMESPACE_LABEL);
-       /* if we found a local label, we already created the declaration */
+       entity_t *label = get_entity(token.identifier.symbol, NAMESPACE_LABEL);
+       /* If we find a local label, we already created the declaration. */
        if (label != NULL && label->kind == ENTITY_LOCAL_LABEL) {
                if (label->base.parent_scope != current_scope) {
                        assert(label->base.parent_scope->depth < current_scope->depth);
                        current_function->goto_to_outer = true;
                }
-               return &label->label;
-       }
-
-       label = get_entity(symbol, NAMESPACE_LABEL);
-       /* if we found a label in the same function, then we already created the
-        * declaration */
-       if (label != NULL
-                       && label->base.parent_scope == &current_function->parameters) {
-               return &label->label;
+       } else if (label == NULL || label->base.parent_scope != &current_function->parameters) {
+               /* There is no matching label in the same function, so create a new one. */
+               label = allocate_entity_zero(ENTITY_LABEL, NAMESPACE_LABEL, token.identifier.symbol);
+               label_push(label);
        }
 
-       /* otherwise we need to create a new one */
-       label               = allocate_entity_zero(ENTITY_LABEL);
-       label->base.namespc = NAMESPACE_LABEL;
-       label->base.symbol  = symbol;
-
-       label_push(label);
-
+       eat(T_IDENTIFIER);
        return &label->label;
 }
 
@@ -7057,28 +6825,24 @@ static label_t *get_label(symbol_t *symbol)
  */
 static expression_t *parse_label_address(void)
 {
-       source_position_t source_position = token.source_position;
+       source_position_t source_position = token.base.source_position;
        eat(T_ANDAND);
-       if (token.type != T_IDENTIFIER) {
+       if (token.kind != T_IDENTIFIER) {
                parse_error_expected("while parsing label address", T_IDENTIFIER, NULL);
-               goto end_error;
+               return create_invalid_expression();
        }
-       symbol_t *symbol = token.symbol;
-       next_token();
 
-       label_t *label       = get_label(symbol);
+       label_t *const label = get_label();
        label->used          = true;
        label->address_taken = true;
 
        expression_t *expression = allocate_expression_zero(EXPR_LABEL_ADDRESS);
        expression->base.source_position = source_position;
 
-       /* label address is threaten as a void pointer */
+       /* label address is treated as a void pointer */
        expression->base.type           = type_void_ptr;
        expression->label_address.label = label;
        return expression;
-end_error:
-       return create_invalid_expression();
 }
 
 /**
@@ -7088,20 +6852,19 @@ static expression_t *parse_noop_expression(void)
 {
        /* the result is a (int)0 */
        expression_t *literal = allocate_expression_zero(EXPR_LITERAL_MS_NOOP);
-       literal->base.type            = type_int;
-       literal->base.source_position = token.source_position;
-       literal->literal.value.begin  = "__noop";
-       literal->literal.value.size   = 6;
+       literal->base.type           = type_int;
+       literal->literal.value.begin = "__noop";
+       literal->literal.value.size  = 6;
 
        eat(T___noop);
 
-       if (token.type == '(') {
+       if (token.kind == '(') {
                /* parse arguments */
                eat('(');
                add_anchor_token(')');
                add_anchor_token(',');
 
-               if (token.type != ')') do {
+               if (token.kind != ')') do {
                        (void)parse_assignment_expression();
                } while (next_if(','));
        }
@@ -7118,7 +6881,7 @@ end_error:
  */
 static expression_t *parse_primary_expression(void)
 {
-       switch (token.type) {
+       switch (token.kind) {
        case T_false:                        return parse_boolean_literal(false);
        case T_true:                         return parse_boolean_literal(true);
        case T_INTEGER:
@@ -7160,13 +6923,15 @@ static expression_t *parse_primary_expression(void)
        case T_COLONCOLON:
                return parse_reference();
        case T_IDENTIFIER:
-               if (!is_typedef_symbol(token.symbol)) {
+               if (!is_typedef_symbol(token.identifier.symbol)) {
                        return parse_reference();
                }
                /* FALLTHROUGH */
-       TYPENAME_START {
-               source_position_t  const pos  = *HERE;
-               type_t const      *const type = parse_typename();
+       DECLARATION_START {
+               source_position_t const  pos = *HERE;
+               declaration_specifiers_t specifiers;
+               parse_declaration_specifiers(&specifiers);
+               type_t const *const type = parse_abstract_declarator(specifiers.type);
                errorf(&pos, "encountered type '%T' while parsing expression", type);
                return create_invalid_expression();
        }
@@ -7177,29 +6942,15 @@ static expression_t *parse_primary_expression(void)
        return create_invalid_expression();
 }
 
-/**
- * Check if the expression has the character type and issue a warning then.
- */
-static void check_for_char_index_type(const expression_t *expression)
-{
-       type_t       *const type      = expression->base.type;
-       const type_t *const base_type = skip_typeref(type);
-
-       if (is_type_atomic(base_type, ATOMIC_TYPE_CHAR) &&
-                       warning.char_subscripts) {
-               warningf(&expression->base.source_position,
-                        "array subscript has type '%T'", type);
-       }
-}
-
 static expression_t *parse_array_expression(expression_t *left)
 {
-       expression_t *expression = allocate_expression_zero(EXPR_ARRAY_ACCESS);
+       expression_t              *const expr = allocate_expression_zero(EXPR_ARRAY_ACCESS);
+       array_access_expression_t *const arr  = &expr->array_access;
 
        eat('[');
        add_anchor_token(']');
 
-       expression_t *inside = parse_expression();
+       expression_t *const inside = parse_expression();
 
        type_t *const orig_type_left   = left->base.type;
        type_t *const orig_type_inside = inside->base.type;
@@ -7207,36 +6958,53 @@ static expression_t *parse_array_expression(expression_t *left)
        type_t *const type_left   = skip_typeref(orig_type_left);
        type_t *const type_inside = skip_typeref(orig_type_inside);
 
-       type_t                    *return_type;
-       array_access_expression_t *array_access = &expression->array_access;
+       expression_t *ref;
+       expression_t *idx;
+       type_t       *idx_type;
+       type_t       *res_type;
        if (is_type_pointer(type_left)) {
-               return_type             = type_left->pointer.points_to;
-               array_access->array_ref = left;
-               array_access->index     = inside;
-               check_for_char_index_type(inside);
+               ref      = left;
+               idx      = inside;
+               idx_type = type_inside;
+               res_type = type_left->pointer.points_to;
+               goto check_idx;
        } else if (is_type_pointer(type_inside)) {
-               return_type             = type_inside->pointer.points_to;
-               array_access->array_ref = inside;
-               array_access->index     = left;
-               array_access->flipped   = true;
-               check_for_char_index_type(left);
+               arr->flipped = true;
+               ref      = inside;
+               idx      = left;
+               idx_type = type_left;
+               res_type = type_inside->pointer.points_to;
+check_idx:
+               res_type = automatic_type_conversion(res_type);
+               if (!is_type_integer(idx_type)) {
+                       errorf(&idx->base.source_position, "array subscript must have integer type");
+               } else if (is_type_atomic(idx_type, ATOMIC_TYPE_CHAR)) {
+                       source_position_t const *const pos = &idx->base.source_position;
+                       warningf(WARN_CHAR_SUBSCRIPTS, pos, "array subscript has char type");
+               }
        } else {
                if (is_type_valid(type_left) && is_type_valid(type_inside)) {
-                       errorf(HERE,
-                               "array access on object with non-pointer types '%T', '%T'",
-                               orig_type_left, orig_type_inside);
+                       errorf(&expr->base.source_position, "invalid types '%T[%T]' for array access", orig_type_left, orig_type_inside);
                }
-               return_type             = type_error_type;
-               array_access->array_ref = left;
-               array_access->index     = inside;
+               res_type = type_error_type;
+               ref      = left;
+               idx      = inside;
        }
 
-       expression->base.type = automatic_type_conversion(return_type);
+       arr->array_ref = ref;
+       arr->index     = idx;
+       arr->base.type = res_type;
 
        rem_anchor_token(']');
        expect(']', end_error);
 end_error:
-       return expression;
+       return expr;
+}
+
+static bool is_bitfield(const expression_t *expression)
+{
+       return expression->kind == EXPR_SELECT
+               && expression->select.compound_entry->compound_member.bitfield;
 }
 
 static expression_t *parse_typeprop(expression_kind_t const kind)
@@ -7246,29 +7014,32 @@ static expression_t *parse_typeprop(expression_kind_t const kind)
 
        eat(kind == EXPR_SIZEOF ? T_sizeof : T___alignof__);
 
-       /* we only refer to a type property, mark this case */
-       bool old     = in_type_prop;
-       in_type_prop = true;
-
        type_t       *orig_type;
        expression_t *expression;
-       if (token.type == '(' && is_declaration_specifier(look_ahead(1), true)) {
+       if (token.kind == '(' && is_declaration_specifier(look_ahead(1))) {
+               source_position_t const pos = *HERE;
                next_token();
                add_anchor_token(')');
                orig_type = parse_typename();
                rem_anchor_token(')');
                expect(')', end_error);
 
-               if (token.type == '{') {
+               if (token.kind == '{') {
                        /* It was not sizeof(type) after all.  It is sizeof of an expression
                         * starting with a compound literal */
-                       expression = parse_compound_literal(orig_type);
+                       expression = parse_compound_literal(&pos, orig_type);
                        goto typeprop_expression;
                }
        } else {
                expression = parse_subexpression(PREC_UNARY);
 
 typeprop_expression:
+               if (is_bitfield(expression)) {
+                       char const* const what = kind == EXPR_SIZEOF ? "sizeof" : "alignof";
+                       errorf(&tp_expression->base.source_position,
+                                  "operand of %s expression must not be a bitfield", what);
+               }
+
                tp_expression->typeprop.tp_expression = expression;
 
                orig_type = revert_automatic_type_conversion(expression);
@@ -7284,20 +7055,13 @@ typeprop_expression:
        } else if (type->kind == TYPE_FUNCTION) {
                if (GNU_MODE) {
                        /* function types are allowed (and return 1) */
-                       if (warning.other) {
-                               char const* const what = kind == EXPR_SIZEOF ? "sizeof" : "alignof";
-                               warningf(&tp_expression->base.source_position,
-                                        "%s expression with function argument returns invalid result", what);
-                       }
+                       source_position_t const *const pos  = &tp_expression->base.source_position;
+                       char              const *const what = kind == EXPR_SIZEOF ? "sizeof" : "alignof";
+                       warningf(WARN_OTHER, pos, "%s expression with function argument returns invalid result", what);
                } else {
                        wrong_type = "function";
                }
-       } else {
-               if (is_type_incomplete(type))
-                       wrong_type = "incomplete";
        }
-       if (type->kind == TYPE_BITFIELD)
-               wrong_type = "bitfield";
 
        if (wrong_type != NULL) {
                char const* const what = kind == EXPR_SIZEOF ? "sizeof" : "alignof";
@@ -7307,7 +7071,6 @@ typeprop_expression:
        }
 
 end_error:
-       in_type_prop = old;
        return tp_expression;
 }
 
@@ -7323,16 +7086,16 @@ static expression_t *parse_alignof(void)
 
 static expression_t *parse_select_expression(expression_t *addr)
 {
-       assert(token.type == '.' || token.type == T_MINUSGREATER);
-       bool select_left_arrow = (token.type == T_MINUSGREATER);
+       assert(token.kind == '.' || token.kind == T_MINUSGREATER);
+       bool select_left_arrow = (token.kind == T_MINUSGREATER);
        source_position_t const pos = *HERE;
        next_token();
 
-       if (token.type != T_IDENTIFIER) {
+       if (token.kind != T_IDENTIFIER) {
                parse_error_expected("while parsing select", T_IDENTIFIER, NULL);
                return create_invalid_expression();
        }
-       symbol_t *symbol = token.symbol;
+       symbol_t *symbol = token.identifier.symbol;
        next_token();
 
        type_t *const orig_type = addr->base.type;
@@ -7429,15 +7192,14 @@ static void check_call_argument(type_t          *expected_type,
                snprintf(buf, sizeof(buf), "call argument %u", pos);
                report_assign_error(error, expected_type, arg_expr, buf,
                                    &arg_expr->base.source_position);
-       } else if (warning.traditional || warning.conversion) {
+       } else {
                type_t *const promoted_type = get_default_promoted_type(arg_type);
                if (!types_compatible(expected_type_skip, promoted_type) &&
                    !types_compatible(expected_type_skip, type_void_ptr) &&
                    !types_compatible(type_void_ptr,      promoted_type)) {
                        /* Deliberately show the skipped types in this warning */
-                       warningf(&arg_expr->base.source_position,
-                               "passing call argument %u as '%T' rather than '%T' due to prototype",
-                               pos, expected_type_skip, promoted_type);
+                       source_position_t const *const apos = &arg_expr->base.source_position;
+                       warningf(WARN_TRADITIONAL, apos, "passing call argument %u as '%T' rather than '%T' due to prototype", pos, expected_type_skip, promoted_type);
                }
        }
 }
@@ -7534,7 +7296,7 @@ static expression_t *parse_call_expression(expression_t *expression)
        add_anchor_token(')');
        add_anchor_token(',');
 
-       if (token.type != ')') {
+       if (token.kind != ')') {
                call_argument_t **anchor = &call->arguments;
                do {
                        call_argument_t *argument = allocate_ast_zero(sizeof(*argument));
@@ -7561,36 +7323,35 @@ static expression_t *parse_call_expression(expression_t *expression)
                }
 
                if (parameter != NULL) {
-                       errorf(HERE, "too few arguments to function '%E'", expression);
+                       errorf(&expression->base.source_position, "too few arguments to function '%E'", expression);
                } else if (argument != NULL && !function_type->variadic) {
-                       errorf(HERE, "too many arguments to function '%E'", expression);
+                       errorf(&argument->expression->base.source_position, "too many arguments to function '%E'", expression);
                }
        }
 
        /* do default promotion for other arguments */
        for (; argument != NULL; argument = argument->next) {
-               type_t *type = argument->expression->base.type;
-               if (!is_type_object(skip_typeref(type))) {
+               type_t *argument_type = argument->expression->base.type;
+               if (!is_type_object(skip_typeref(argument_type))) {
                        errorf(&argument->expression->base.source_position,
                               "call argument '%E' must not be void", argument->expression);
                }
 
-               type = get_default_promoted_type(type);
+               argument_type = get_default_promoted_type(argument_type);
 
                argument->expression
-                       = create_implicit_cast(argument->expression, type);
+                       = create_implicit_cast(argument->expression, argument_type);
        }
 
-       check_format(&result->call);
+       check_format(call);
 
-       if (warning.aggregate_return &&
-           is_type_compound(skip_typeref(function_type->return_type))) {
-               warningf(&result->base.source_position,
-                        "function call has aggregate value");
+       if (is_type_compound(skip_typeref(function_type->return_type))) {
+               source_position_t const *const pos = &expression->base.source_position;
+               warningf(WARN_AGGREGATE_RETURN, pos, "function call has aggregate value");
        }
 
-       if (call->function->kind == EXPR_REFERENCE) {
-               reference_expression_t *reference = &call->function->reference;
+       if (expression->kind == EXPR_REFERENCE) {
+               reference_expression_t *reference = &expression->reference;
                if (reference->entity->kind == ENTITY_FUNCTION &&
                    reference->entity->function.btk != bk_none)
                        handle_builtin_argument_restrictions(call);
@@ -7641,27 +7402,22 @@ static expression_t const *get_reference_address(expression_t const *expr)
 
 static void warn_reference_address_as_bool(expression_t const* expr)
 {
-       if (!warning.address)
-               return;
-
        expr = get_reference_address(expr);
        if (expr != NULL) {
-               warningf(&expr->base.source_position,
-                        "the address of '%Y' will always evaluate as 'true'",
-                        expr->reference.entity->base.symbol);
+               source_position_t const *const pos = &expr->base.source_position;
+               entity_t          const *const ent = expr->reference.entity;
+               warningf(WARN_ADDRESS, pos, "the address of '%N' will always evaluate as 'true'", ent);
        }
 }
 
 static void warn_assignment_in_condition(const expression_t *const expr)
 {
-       if (!warning.parentheses)
-               return;
        if (expr->base.kind != EXPR_BINARY_ASSIGN)
                return;
        if (expr->base.parenthesized)
                return;
-       warningf(&expr->base.source_position,
-                       "suggest parentheses around assignment used as truth value");
+       source_position_t const *const pos = &expr->base.source_position;
+       warningf(WARN_PARENTHESES, pos, "suggest parentheses around assignment used as truth value");
 }
 
 static void semantic_condition(expression_t const *const expr,
@@ -7697,7 +7453,7 @@ static expression_t *parse_conditional_expression(expression_t *expression)
 
        expression_t *true_expression = expression;
        bool          gnu_cond = false;
-       if (GNU_MODE && token.type == ':') {
+       if (GNU_MODE && token.kind == ':') {
                gnu_cond = true;
        } else {
                true_expression = parse_expression();
@@ -7714,7 +7470,8 @@ end_error:;
        type_t *const false_type      = skip_typeref(orig_false_type);
 
        /* 6.5.15.3 */
-       type_t *result_type;
+       source_position_t const *const pos = &conditional->base.source_position;
+       type_t                        *result_type;
        if (is_type_atomic(true_type,  ATOMIC_TYPE_VOID) ||
                        is_type_atomic(false_type, ATOMIC_TYPE_VOID)) {
                /* ISO/IEC 14882:1998(E) Â§5.16:2 */
@@ -7723,12 +7480,9 @@ end_error:;
                } else if (false_expression->kind == EXPR_UNARY_THROW) {
                        result_type = true_type;
                } else {
-                       if (warning.other && (
-                                               !is_type_atomic(true_type,  ATOMIC_TYPE_VOID) ||
-                                               !is_type_atomic(false_type, ATOMIC_TYPE_VOID)
-                                       )) {
-                               warningf(&conditional->base.source_position,
-                                               "ISO C forbids conditional expression with only one void side");
+                       if (!is_type_atomic(true_type,  ATOMIC_TYPE_VOID) ||
+                           !is_type_atomic(false_type, ATOMIC_TYPE_VOID)) {
+                               warningf(WARN_OTHER, pos, "ISO C forbids conditional expression with only one void side");
                        }
                        result_type = type_void;
                }
@@ -7767,11 +7521,7 @@ end_error:;
                                                    get_unqualified_type(to2))) {
                                to = to1;
                        } else {
-                               if (warning.other) {
-                                       warningf(&conditional->base.source_position,
-                                                       "pointer types '%T' and '%T' in conditional expression are incompatible",
-                                                       true_type, false_type);
-                               }
+                               warningf(WARN_OTHER, pos, "pointer types '%T' and '%T' in conditional expression are incompatible", true_type, false_type);
                                to = type_void;
                        }
 
@@ -7779,23 +7529,15 @@ end_error:;
                                get_qualified_type(to, to1->base.qualifiers | to2->base.qualifiers);
                        result_type = make_pointer_type(type, TYPE_QUALIFIER_NONE);
                } else if (is_type_integer(other_type)) {
-                       if (warning.other) {
-                               warningf(&conditional->base.source_position,
-                                               "pointer/integer type mismatch in conditional expression ('%T' and '%T')", true_type, false_type);
-                       }
+                       warningf(WARN_OTHER, pos, "pointer/integer type mismatch in conditional expression ('%T' and '%T')", true_type, false_type);
                        result_type = pointer_type;
                } else {
-                       if (is_type_valid(other_type)) {
-                               type_error_incompatible("while parsing conditional",
-                                               &expression->base.source_position, true_type, false_type);
-                       }
-                       result_type = type_error_type;
+                       goto types_incompatible;
                }
        } else {
+types_incompatible:
                if (is_type_valid(true_type) && is_type_valid(false_type)) {
-                       type_error_incompatible("while parsing conditional",
-                                               &conditional->base.source_position, true_type,
-                                               false_type);
+                       type_error_incompatible("while parsing conditional", pos, true_type, false_type);
                }
                result_type = type_error_type;
        }
@@ -7813,12 +7555,9 @@ end_error:;
  */
 static expression_t *parse_extension(void)
 {
-       eat(T___extension__);
-
-       bool old_gcc_extension   = in_gcc_extension;
-       in_gcc_extension         = true;
+       PUSH_EXTENSION();
        expression_t *expression = parse_subexpression(PREC_UNARY);
-       in_gcc_extension         = old_gcc_extension;
+       POP_EXTENSION();
        return expression;
 }
 
@@ -7870,10 +7609,9 @@ end_error:;
                        errorf(&value->base.source_position,
                                        "operand of delete must have pointer type");
                }
-       } else if (warning.other &&
-                       is_type_atomic(skip_typeref(type->pointer.points_to), ATOMIC_TYPE_VOID)) {
-               warningf(&value->base.source_position,
-                               "deleting 'void*' is undefined");
+       } else if (is_type_atomic(skip_typeref(type->pointer.points_to), ATOMIC_TYPE_VOID)) {
+               source_position_t const *const pos = &value->base.source_position;
+               warningf(WARN_OTHER, pos, "deleting 'void*' is undefined");
        }
 
        return result;
@@ -7891,7 +7629,7 @@ static expression_t *parse_throw(void)
        eat(T_throw);
 
        expression_t *value = NULL;
-       switch (token.type) {
+       switch (token.kind) {
                EXPRESSION_START {
                        value = parse_assignment_expression();
                        /* ISO/IEC 14882:1998(E) Â§15.1:3 */
@@ -7931,10 +7669,8 @@ static bool check_pointer_arithmetic(const source_position_t *source_position,
                               "arithmetic with pointer to incomplete type '%T' not allowed",
                               orig_pointer_type);
                        return false;
-               } else if (warning.pointer_arith) {
-                       warningf(source_position,
-                                "pointer of type '%T' used in arithmetic",
-                                orig_pointer_type);
+               } else {
+                       warningf(WARN_POINTER_ARITH, source_position, "pointer of type '%T' used in arithmetic", orig_pointer_type);
                }
        } else if (is_type_function(points_to)) {
                if (!GNU_MODE) {
@@ -7942,10 +7678,8 @@ static bool check_pointer_arithmetic(const source_position_t *source_position,
                               "arithmetic with pointer to function type '%T' not allowed",
                               orig_pointer_type);
                        return false;
-               } else if (warning.pointer_arith) {
-                       warningf(source_position,
-                                "pointer to a function '%T' used in arithmetic",
-                                orig_pointer_type);
+               } else {
+                       warningf(WARN_POINTER_ARITH, source_position, "pointer to a function '%T' used in arithmetic", orig_pointer_type);
                }
        }
        return true;
@@ -8016,9 +7750,8 @@ static void semantic_unexpr_arithmetic(unary_expression_t *expression)
 static void semantic_unexpr_plus(unary_expression_t *expression)
 {
        semantic_unexpr_arithmetic(expression);
-       if (warning.traditional)
-               warningf(&expression->base.source_position,
-                       "traditional C rejects the unary plus operator");
+       source_position_t const *const pos = &expression->base.source_position;
+       warningf(WARN_TRADITIONAL, pos, "traditional C rejects the unary plus operator");
 }
 
 static void semantic_not(unary_expression_t *expression)
@@ -8078,9 +7811,8 @@ static void set_address_taken(expression_t *expression, bool may_be_register)
 
        if (entity->declaration.storage_class == STORAGE_CLASS_REGISTER
                        && !may_be_register) {
-               errorf(&expression->base.source_position,
-                      "address of register %s '%Y' requested",
-                      get_entity_kind_name(entity->kind), entity->base.symbol);
+               source_position_t const *const pos = &expression->base.source_position;
+               errorf(pos, "address of register '%N' requested", entity);
        }
 
        if (entity->kind == ENTITY_VARIABLE) {
@@ -8108,10 +7840,9 @@ static void semantic_take_addr(unary_expression_t *expression)
        if (!is_lvalue(value)) {
                errorf(&expression->base.source_position, "'&' requires an lvalue");
        }
-       if (type->kind == TYPE_BITFIELD) {
+       if (is_bitfield(value)) {
                errorf(&expression->base.source_position,
-                      "'&' not allowed on object with bitfield type '%T'",
-                      type);
+                      "'&' not allowed on bitfield");
        }
 
        set_address_taken(value, false);
@@ -8119,12 +7850,12 @@ static void semantic_take_addr(unary_expression_t *expression)
        expression->base.type = make_pointer_type(orig_type, TYPE_QUALIFIER_NONE);
 }
 
-#define CREATE_UNARY_EXPRESSION_PARSER(token_type, unexpression_type, sfunc) \
+#define CREATE_UNARY_EXPRESSION_PARSER(token_kind, unexpression_type, sfunc) \
 static expression_t *parse_##unexpression_type(void)                         \
 {                                                                            \
        expression_t *unary_expression                                           \
                = allocate_expression_zero(unexpression_type);                       \
-       eat(token_type);                                                         \
+       eat(token_kind);                                                         \
        unary_expression->unary.value = parse_subexpression(PREC_UNARY);         \
                                                                                 \
        sfunc(&unary_expression->unary);                                         \
@@ -8149,13 +7880,13 @@ CREATE_UNARY_EXPRESSION_PARSER(T_PLUSPLUS,   EXPR_UNARY_PREFIX_INCREMENT,
 CREATE_UNARY_EXPRESSION_PARSER(T_MINUSMINUS, EXPR_UNARY_PREFIX_DECREMENT,
                                semantic_incdec)
 
-#define CREATE_UNARY_POSTFIX_EXPRESSION_PARSER(token_type, unexpression_type, \
+#define CREATE_UNARY_POSTFIX_EXPRESSION_PARSER(token_kind, unexpression_type, \
                                                sfunc)                         \
 static expression_t *parse_##unexpression_type(expression_t *left)            \
 {                                                                             \
        expression_t *unary_expression                                            \
                = allocate_expression_zero(unexpression_type);                        \
-       eat(token_type);                                                          \
+       eat(token_kind);                                                          \
        unary_expression->unary.value = left;                                     \
                                                                                  \
        sfunc(&unary_expression->unary);                                          \
@@ -8287,8 +8018,7 @@ static void semantic_binexpr_integer(binary_expression_t *const expression)
 
 static void warn_div_by_zero(binary_expression_t const *const expression)
 {
-       if (!warning.div_by_zero ||
-           !is_type_integer(expression->base.type))
+       if (!is_type_integer(expression->base.type))
                return;
 
        expression_t const *const right = expression->right;
@@ -8296,7 +8026,8 @@ static void warn_div_by_zero(binary_expression_t const *const expression)
        if (is_type_integer(right->base.type)                    &&
            is_constant_expression(right) == EXPR_CLASS_CONSTANT &&
            !fold_constant_to_bool(right)) {
-               warningf(&expression->base.source_position, "division by zero");
+               source_position_t const *const pos = &expression->base.source_position;
+               warningf(WARN_DIV_BY_ZERO, pos, "division by zero");
        }
 }
 
@@ -8321,8 +8052,8 @@ static void warn_addsub_in_shift(const expression_t *const expr)
                default:              return;
        }
 
-       warningf(&expr->base.source_position,
-                       "suggest parentheses around '%c' inside shift", op);
+       source_position_t const *const pos = &expr->base.source_position;
+       warningf(WARN_PARENTHESES, pos, "suggest parentheses around '%c' inside shift", op);
 }
 
 static bool semantic_shift(binary_expression_t *expression)
@@ -8346,14 +8077,13 @@ static bool semantic_shift(binary_expression_t *expression)
        type_left = promote_integer(type_left);
 
        if (is_constant_expression(right) == EXPR_CLASS_CONSTANT) {
-               long count = fold_constant_to_int(right);
+               source_position_t const *const pos   = &right->base.source_position;
+               long                     const count = fold_constant_to_int(right);
                if (count < 0) {
-                       warningf(&right->base.source_position,
-                                       "shift count must be non-negative");
+                       warningf(WARN_OTHER, pos, "shift count must be non-negative");
                } else if ((unsigned long)count >=
                                get_atomic_type_size(type_left->atomic.akind) * 8) {
-                       warningf(&right->base.source_position,
-                                       "shift count must be less than type width");
+                       warningf(WARN_OTHER, pos, "shift count must be less than type width");
                }
        }
 
@@ -8371,10 +8101,8 @@ static void semantic_shift_op(binary_expression_t *expression)
        if (!semantic_shift(expression))
                return;
 
-       if (warning.parentheses) {
-               warn_addsub_in_shift(left);
-               warn_addsub_in_shift(right);
-       }
+       warn_addsub_in_shift(left);
+       warn_addsub_in_shift(right);
 
        type_t *const orig_type_left = left->base.type;
        type_t *      type_left      = skip_typeref(orig_type_left);
@@ -8445,8 +8173,8 @@ static void semantic_sub(binary_expression_t *expression)
                        if (!is_type_atomic(unqual_left, ATOMIC_TYPE_VOID)) {
                                errorf(pos, "subtracting pointers to non-object types '%T'",
                                       orig_type_left);
-                       } else if (warning.other) {
-                               warningf(pos, "subtracting pointers to void");
+                       } else {
+                               warningf(WARN_OTHER, pos, "subtracting pointers to void");
                        }
                }
                expression->base.type = type_ptrdiff_t;
@@ -8467,27 +8195,8 @@ static void warn_string_literal_address(expression_t const* expr)
 
        if (expr->kind == EXPR_STRING_LITERAL
                        || expr->kind == EXPR_WIDE_STRING_LITERAL) {
-               warningf(&expr->base.source_position,
-                       "comparison with string literal results in unspecified behaviour");
-       }
-}
-
-static void warn_comparison_in_comparison(const expression_t *const expr)
-{
-       if (expr->base.parenthesized)
-               return;
-       switch (expr->base.kind) {
-               case EXPR_BINARY_LESS:
-               case EXPR_BINARY_GREATER:
-               case EXPR_BINARY_LESSEQUAL:
-               case EXPR_BINARY_GREATEREQUAL:
-               case EXPR_BINARY_NOTEQUAL:
-               case EXPR_BINARY_EQUAL:
-                       warningf(&expr->base.source_position,
-                                       "comparisons like 'x <= y < z' do not have their mathematical meaning");
-                       break;
-               default:
-                       break;
+               source_position_t const *const pos = &expr->base.source_position;
+               warningf(WARN_ADDRESS, pos, "comparison with string literal results in unspecified behaviour");
        }
 }
 
@@ -8495,11 +8204,37 @@ static bool maybe_negative(expression_t const *const expr)
 {
        switch (is_constant_expression(expr)) {
                case EXPR_CLASS_ERROR:    return false;
-               case EXPR_CLASS_CONSTANT: return fold_constant_to_int(expr) < 0;
+               case EXPR_CLASS_CONSTANT: return constant_is_negative(expr);
                default:                  return true;
        }
 }
 
+static void warn_comparison(source_position_t const *const pos, expression_t const *const expr, expression_t const *const other)
+{
+       warn_string_literal_address(expr);
+
+       expression_t const* const ref = get_reference_address(expr);
+       if (ref != NULL && is_null_pointer_constant(other)) {
+               entity_t const *const ent = ref->reference.entity;
+               warningf(WARN_ADDRESS, pos, "the address of '%N' will never be NULL", ent);
+       }
+
+       if (!expr->base.parenthesized) {
+               switch (expr->base.kind) {
+                       case EXPR_BINARY_LESS:
+                       case EXPR_BINARY_GREATER:
+                       case EXPR_BINARY_LESSEQUAL:
+                       case EXPR_BINARY_GREATEREQUAL:
+                       case EXPR_BINARY_NOTEQUAL:
+                       case EXPR_BINARY_EQUAL:
+                               warningf(WARN_PARENTHESES, pos, "comparisons like 'x <= y < z' do not have their mathematical meaning");
+                               break;
+                       default:
+                               break;
+               }
+       }
+}
+
 /**
  * Check the semantics of comparison expressions.
  *
@@ -8507,32 +8242,12 @@ static bool maybe_negative(expression_t const *const expr)
  */
 static void semantic_comparison(binary_expression_t *expression)
 {
-       expression_t *left  = expression->left;
-       expression_t *right = expression->right;
+       source_position_t const *const pos   = &expression->base.source_position;
+       expression_t            *const left  = expression->left;
+       expression_t            *const right = expression->right;
 
-       if (warning.address) {
-               warn_string_literal_address(left);
-               warn_string_literal_address(right);
-
-               expression_t const* const func_left = get_reference_address(left);
-               if (func_left != NULL && is_null_pointer_constant(right)) {
-                       warningf(&expression->base.source_position,
-                                "the address of '%Y' will never be NULL",
-                                func_left->reference.entity->base.symbol);
-               }
-
-               expression_t const* const func_right = get_reference_address(right);
-               if (func_right != NULL && is_null_pointer_constant(right)) {
-                       warningf(&expression->base.source_position,
-                                "the address of '%Y' will never be NULL",
-                                func_right->reference.entity->base.symbol);
-               }
-       }
-
-       if (warning.parentheses) {
-               warn_comparison_in_comparison(left);
-               warn_comparison_in_comparison(right);
-       }
+       warn_comparison(pos, left, right);
+       warn_comparison(pos, right, left);
 
        type_t *orig_type_left  = left->base.type;
        type_t *orig_type_right = right->base.type;
@@ -8544,7 +8259,7 @@ static void semantic_comparison(binary_expression_t *expression)
                type_t *arithmetic_type = semantic_arithmetic(type_left, type_right);
 
                /* test for signed vs unsigned compares */
-               if (warning.sign_compare && is_type_integer(arithmetic_type)) {
+               if (is_type_integer(arithmetic_type)) {
                        bool const signed_left  = is_type_signed(type_left);
                        bool const signed_right = is_type_signed(type_right);
                        if (signed_left != signed_right) {
@@ -8552,8 +8267,7 @@ static void semantic_comparison(binary_expression_t *expression)
                                /* TODO check whether constant value can be represented by other type */
                                if ((signed_left  && maybe_negative(left)) ||
                                                (signed_right && maybe_negative(right))) {
-                                       warningf(&expression->base.source_position,
-                                                       "comparison between signed and unsigned");
+                                       warningf(WARN_SIGN_COMPARE, pos, "comparison between signed and unsigned");
                                }
                        }
                }
@@ -8561,12 +8275,10 @@ static void semantic_comparison(binary_expression_t *expression)
                expression->left        = create_implicit_cast(left, arithmetic_type);
                expression->right       = create_implicit_cast(right, arithmetic_type);
                expression->base.type   = arithmetic_type;
-               if (warning.float_equal &&
-                   (expression->base.kind == EXPR_BINARY_EQUAL ||
+               if ((expression->base.kind == EXPR_BINARY_EQUAL ||
                     expression->base.kind == EXPR_BINARY_NOTEQUAL) &&
                    is_type_float(arithmetic_type)) {
-                       warningf(&expression->base.source_position,
-                                "comparing floating point with == or != is unsafe");
+                       warningf(WARN_FLOAT_EQUAL, pos, "comparing floating point with == or != is unsafe");
                }
        } else if (is_type_pointer(type_left) && is_type_pointer(type_right)) {
                /* TODO check compatibility */
@@ -8575,9 +8287,7 @@ static void semantic_comparison(binary_expression_t *expression)
        } else if (is_type_pointer(type_right)) {
                expression->left = create_implicit_cast(left, type_right);
        } else if (is_type_valid(type_left) && is_type_valid(type_right)) {
-               type_error_incompatible("invalid operands in comparison",
-                                       &expression->base.source_position,
-                                       type_left, type_right);
+               type_error_incompatible("invalid operands in comparison", pos, type_left, type_right);
        }
        expression->base.type = c_mode & _CXX ? type_bool : type_int;
 }
@@ -8608,33 +8318,33 @@ static bool is_valid_assignment_lhs(expression_t const* const left)
        type_t *const type_left      = skip_typeref(orig_type_left);
 
        if (!is_lvalue(left)) {
-               errorf(HERE, "left hand side '%E' of assignment is not an lvalue",
+               errorf(&left->base.source_position, "left hand side '%E' of assignment is not an lvalue",
                       left);
                return false;
        }
 
        if (left->kind == EXPR_REFERENCE
                        && left->reference.entity->kind == ENTITY_FUNCTION) {
-               errorf(HERE, "cannot assign to function '%E'", left);
+               errorf(&left->base.source_position, "cannot assign to function '%E'", left);
                return false;
        }
 
        if (is_type_array(type_left)) {
-               errorf(HERE, "cannot assign to array '%E'", left);
+               errorf(&left->base.source_position, "cannot assign to array '%E'", left);
                return false;
        }
        if (type_left->base.qualifiers & TYPE_QUALIFIER_CONST) {
-               errorf(HERE, "assignment to readonly location '%E' (type '%T')", left,
+               errorf(&left->base.source_position, "assignment to read-only location '%E' (type '%T')", left,
                       orig_type_left);
                return false;
        }
        if (is_type_incomplete(type_left)) {
-               errorf(HERE, "left-hand side '%E' of assignment has incomplete type '%T'",
+               errorf(&left->base.source_position, "left-hand side '%E' of assignment has incomplete type '%T'",
                       left, orig_type_left);
                return false;
        }
        if (is_type_compound(type_left) && has_const_fields(&type_left->compound)) {
-               errorf(HERE, "cannot assign to '%E' because compound type '%T' has readonly fields",
+               errorf(&left->base.source_position, "cannot assign to '%E' because compound type '%T' has read-only fields",
                       left, orig_type_left);
                return false;
        }
@@ -8760,8 +8470,8 @@ static void warn_logical_and_within_or(const expression_t *const expr)
                return;
        if (expr->base.parenthesized)
                return;
-       warningf(&expr->base.source_position,
-                       "suggest parentheses around && within ||");
+       source_position_t const *const pos = &expr->base.source_position;
+       warningf(WARN_PARENTHESES, pos, "suggest parentheses around && within ||");
 }
 
 /**
@@ -8773,8 +8483,7 @@ static void semantic_logical_op(binary_expression_t *expression)
         * Â§6.5.14:2  Each of the operands shall have scalar type. */
        semantic_condition(expression->left,   "left operand of logical operator");
        semantic_condition(expression->right, "right operand of logical operator");
-       if (expression->base.kind == EXPR_BINARY_LOGICAL_OR &&
-                       warning.parentheses) {
+       if (expression->base.kind == EXPR_BINARY_LOGICAL_OR) {
                warn_logical_and_within_or(expression->left);
                warn_logical_and_within_or(expression->right);
        }
@@ -8814,7 +8523,6 @@ static void semantic_binexpr_assign(binary_expression_t *expression)
 static bool expression_has_effect(const expression_t *const expr)
 {
        switch (expr->kind) {
-               case EXPR_UNKNOWN:                    break;
                case EXPR_INVALID:                    return true; /* do NOT warn */
                case EXPR_REFERENCE:                  return false;
                case EXPR_REFERENCE_ENUM_VALUE:       return false;
@@ -8888,7 +8596,6 @@ static bool expression_has_effect(const expression_t *const expr)
                        return is_type_atomic(type, ATOMIC_TYPE_VOID);
                }
 
-               case EXPR_UNARY_CAST_IMPLICIT:        return true;
                case EXPR_UNARY_ASSUME:               return true;
                case EXPR_UNARY_DELETE:               return true;
                case EXPR_UNARY_DELETE_ARRAY:         return true;
@@ -8945,12 +8652,10 @@ static bool expression_has_effect(const expression_t *const expr)
 
 static void semantic_comma(binary_expression_t *expression)
 {
-       if (warning.unused_value) {
-               const expression_t *const left = expression->left;
-               if (!expression_has_effect(left)) {
-                       warningf(&left->base.source_position,
-                                "left-hand operand of comma expression has no effect");
-               }
+       const expression_t *const left = expression->left;
+       if (!expression_has_effect(left)) {
+               source_position_t const *const pos = &left->base.source_position;
+               warningf(WARN_UNUSED_VALUE, pos, "left-hand operand of comma expression has no effect");
        }
        expression->base.type = expression->right->base.type;
 }
@@ -8958,12 +8663,12 @@ static void semantic_comma(binary_expression_t *expression)
 /**
  * @param prec_r precedence of the right operand
  */
-#define CREATE_BINEXPR_PARSER(token_type, binexpression_type, prec_r, sfunc) \
+#define CREATE_BINEXPR_PARSER(token_kind, binexpression_type, prec_r, sfunc) \
 static expression_t *parse_##binexpression_type(expression_t *left)          \
 {                                                                            \
        expression_t *binexpr = allocate_expression_zero(binexpression_type);    \
        binexpr->binary.left  = left;                                            \
-       eat(token_type);                                                         \
+       eat(token_kind);                                                         \
                                                                              \
        expression_t *right = parse_subexpression(prec_r);                       \
                                                                              \
@@ -9007,13 +8712,12 @@ CREATE_BINEXPR_PARSER(',',                    EXPR_BINARY_COMMA,              PR
 
 static expression_t *parse_subexpression(precedence_t precedence)
 {
-       if (token.type < 0) {
+       if (token.kind < 0) {
                return expected_expression_error();
        }
 
        expression_parser_function_t *parser
-               = &expression_parsers[token.type];
-       source_position_t             source_position = token.source_position;
+               = &expression_parsers[token.kind];
        expression_t                 *left;
 
        if (parser->parser != NULL) {
@@ -9022,14 +8726,13 @@ static expression_t *parse_subexpression(precedence_t precedence)
                left = parse_primary_expression();
        }
        assert(left != NULL);
-       left->base.source_position = source_position;
 
        while (true) {
-               if (token.type < 0) {
+               if (token.kind < 0) {
                        return expected_expression_error();
                }
 
-               parser = &expression_parsers[token.type];
+               parser = &expression_parsers[token.kind];
                if (parser->infix_parser == NULL)
                        break;
                if (parser->infix_precedence < precedence)
@@ -9038,8 +8741,6 @@ static expression_t *parse_subexpression(precedence_t precedence)
                left = parser->infix_parser(left);
 
                assert(left != NULL);
-               assert(left->kind != EXPR_UNKNOWN);
-               left->base.source_position = source_position;
        }
 
        return left;
@@ -9057,15 +8758,15 @@ static expression_t *parse_expression(void)
  * Register a parser for a prefix-like operator.
  *
  * @param parser      the parser function
- * @param token_type  the token type of the prefix token
+ * @param token_kind  the token type of the prefix token
  */
 static void register_expression_parser(parse_expression_function parser,
-                                       int token_type)
+                                       int token_kind)
 {
-       expression_parser_function_t *entry = &expression_parsers[token_type];
+       expression_parser_function_t *entry = &expression_parsers[token_kind];
 
        if (entry->parser != NULL) {
-               diagnosticf("for token '%k'\n", (token_type_t)token_type);
+               diagnosticf("for token '%k'\n", (token_kind_t)token_kind);
                panic("trying to register multiple expression parsers for a token");
        }
        entry->parser = parser;
@@ -9075,16 +8776,16 @@ static void register_expression_parser(parse_expression_function parser,
  * Register a parser for an infix operator with given precedence.
  *
  * @param parser      the parser function
- * @param token_type  the token type of the infix operator
+ * @param token_kind  the token type of the infix operator
  * @param precedence  the precedence of the operator
  */
 static void register_infix_parser(parse_expression_infix_function parser,
-                                  int token_type, precedence_t precedence)
+                                  int token_kind, precedence_t precedence)
 {
-       expression_parser_function_t *entry = &expression_parsers[token_type];
+       expression_parser_function_t *entry = &expression_parsers[token_kind];
 
        if (entry->infix_parser != NULL) {
-               diagnosticf("for token '%k'\n", (token_type_t)token_type);
+               diagnosticf("for token '%k'\n", (token_kind_t)token_kind);
                panic("trying to register multiple infix expression parsers for a "
                      "token");
        }
@@ -9161,17 +8862,17 @@ static asm_argument_t *parse_asm_arguments(bool is_out)
        asm_argument_t  *result = NULL;
        asm_argument_t **anchor = &result;
 
-       while (token.type == T_STRING_LITERAL || token.type == '[') {
+       while (token.kind == T_STRING_LITERAL || token.kind == '[') {
                asm_argument_t *argument = allocate_ast_zero(sizeof(argument[0]));
                memset(argument, 0, sizeof(argument[0]));
 
                if (next_if('[')) {
-                       if (token.type != T_IDENTIFIER) {
+                       if (token.kind != T_IDENTIFIER) {
                                parse_error_expected("while parsing asm argument",
                                                     T_IDENTIFIER, NULL);
                                return NULL;
                        }
-                       argument->symbol = token.symbol;
+                       argument->symbol = token.identifier.symbol;
 
                        expect(']', end_error);
                }
@@ -9197,7 +8898,7 @@ static asm_argument_t *parse_asm_arguments(bool is_out)
                                                size  = get_atomic_type_size(akind);
                                        } else {
                                                flags = ATOMIC_TYPE_FLAG_INTEGER | ATOMIC_TYPE_FLAG_ARITHMETIC;
-                                               size  = get_atomic_type_size(get_intptr_kind());
+                                               size  = get_type_size(type_void_ptr);
                                        }
 
                                        do {
@@ -9213,7 +8914,7 @@ static asm_argument_t *parse_asm_arguments(bool is_out)
                                                        value_size  = get_atomic_type_size(value_akind);
                                                } else if (value_kind == TYPE_POINTER) {
                                                        value_flags = ATOMIC_TYPE_FLAG_INTEGER | ATOMIC_TYPE_FLAG_ARITHMETIC;
-                                                       value_size  = get_atomic_type_size(get_intptr_kind());
+                                                       value_size  = get_type_size(type_void_ptr);
                                                } else {
                                                        break;
                                                }
@@ -9263,7 +8964,7 @@ static asm_clobber_t *parse_asm_clobbers(void)
        asm_clobber_t *result  = NULL;
        asm_clobber_t **anchor = &result;
 
-       while (token.type == T_STRING_LITERAL) {
+       while (token.kind == T_STRING_LITERAL) {
                asm_clobber_t *clobber = allocate_ast_zero(sizeof(clobber[0]));
                clobber->clobber       = parse_string_literals();
 
@@ -9292,7 +8993,7 @@ static statement_t *parse_asm_statement(void)
 
        expect('(', end_error);
        add_anchor_token(')');
-       if (token.type != T_STRING_LITERAL) {
+       if (token.kind != T_STRING_LITERAL) {
                parse_error_expected("after asm(", T_STRING_LITERAL, NULL);
                goto end_of_asm;
        }
@@ -9338,7 +9039,7 @@ end_error:
 static statement_t *parse_label_inner_statement(statement_t const *const label, char const *const label_kind)
 {
        statement_t *inner_stmt;
-       switch (token.type) {
+       switch (token.kind) {
                case '}':
                        errorf(&label->base.source_position, "%s at end of compound statement", label_kind);
                        inner_stmt = create_invalid_statement();
@@ -9357,7 +9058,8 @@ static statement_t *parse_label_inner_statement(statement_t const *const label,
 
                default:
                        inner_stmt = parse_statement();
-                       /* ISO/IEC 14882:1998(E) Â§6:1/§6.7  Declarations are statements */
+                       /* ISO/IEC  9899:1999(E) Â§6.8:1/6.8.2:1  Declarations are no statements */
+                       /* ISO/IEC 14882:1998(E) Â§6:1/§6.7       Declarations are statements */
                        if (inner_stmt->kind == STATEMENT_DECLARATION && !(c_mode & _CXX)) {
                                errorf(&inner_stmt->base.source_position, "declaration after %s", label_kind);
                        }
@@ -9404,9 +9106,9 @@ static statement_t *parse_case_statement(void)
                                long const val = fold_constant_to_int(end_range);
                                statement->case_label.last_case = val;
 
-                               if (warning.other && val < statement->case_label.first_case) {
+                               if (val < statement->case_label.first_case) {
                                        statement->case_label.is_empty_range = true;
-                                       warningf(pos, "empty range specified");
+                                       warningf(WARN_OTHER, pos, "empty range specified");
                                }
                        }
                }
@@ -9446,7 +9148,7 @@ end_error:
 
        statement->case_label.statement = parse_label_inner_statement(statement, "case label");
 
-       POP_PARENT;
+       POP_PARENT();
        return statement;
 }
 
@@ -9467,8 +9169,7 @@ end_error:
        if (current_switch != NULL) {
                const case_label_statement_t *def_label = current_switch->default_label;
                if (def_label != NULL) {
-                       errorf(HERE, "multiple default labels in one switch (previous declared %P)",
-                              &def_label->base.source_position);
+                       errorf(&statement->base.source_position, "multiple default labels in one switch (previous declared %P)", &def_label->base.source_position);
                } else {
                        current_switch->default_label = &statement->case_label;
 
@@ -9487,7 +9188,7 @@ end_error:
 
        statement->case_label.statement = parse_label_inner_statement(statement, "default label");
 
-       POP_PARENT;
+       POP_PARENT();
        return statement;
 }
 
@@ -9496,40 +9197,50 @@ end_error:
  */
 static statement_t *parse_label_statement(void)
 {
-       assert(token.type == T_IDENTIFIER);
-       symbol_t *symbol = token.symbol;
-       label_t  *label  = get_label(symbol);
-
        statement_t *const statement = allocate_statement_zero(STATEMENT_LABEL);
-       statement->label.label       = label;
-
-       next_token();
+       label_t     *const label     = get_label();
+       statement->label.label = label;
 
        PUSH_PARENT(statement);
 
        /* if statement is already set then the label is defined twice,
         * otherwise it was just mentioned in a goto/local label declaration so far
         */
+       source_position_t const* const pos = &statement->base.source_position;
        if (label->statement != NULL) {
-               errorf(HERE, "duplicate label '%Y' (declared %P)",
-                      symbol, &label->base.source_position);
+               errorf(pos, "duplicate '%N' (declared %P)", (entity_t const*)label, &label->base.source_position);
        } else {
-               label->base.source_position = token.source_position;
+               label->base.source_position = *pos;
                label->statement            = statement;
        }
 
        eat(':');
 
+       if (token.kind == T___attribute__ && !(c_mode & _CXX)) {
+               parse_attributes(NULL); // TODO process attributes
+       }
+
        statement->label.statement = parse_label_inner_statement(statement, "label");
 
        /* remember the labels in a list for later checking */
        *label_anchor = &statement->label;
        label_anchor  = &statement->label.next;
 
-       POP_PARENT;
+       POP_PARENT();
        return statement;
 }
 
+static statement_t *parse_inner_statement(void)
+{
+       statement_t *const stmt = parse_statement();
+       /* ISO/IEC  9899:1999(E) Â§6.8:1/6.8.2:1  Declarations are no statements */
+       /* ISO/IEC 14882:1998(E) Â§6:1/§6.7       Declarations are statements */
+       if (stmt->kind == STATEMENT_DECLARATION && !(c_mode & _CXX)) {
+               errorf(&stmt->base.source_position, "declaration as inner statement, use {}");
+       }
+       return stmt;
+}
+
 /**
  * Parse an if statement.
  */
@@ -9558,20 +9269,29 @@ end_error:
        rem_anchor_token('{');
 
        add_anchor_token(T_else);
-       statement_t *const true_stmt = parse_statement();
+       statement_t *const true_stmt = parse_inner_statement();
        statement->ifs.true_statement = true_stmt;
        rem_anchor_token(T_else);
 
+       if (true_stmt->kind == STATEMENT_EMPTY) {
+               warningf(WARN_EMPTY_BODY, HERE,
+                       "suggest braces around empty body in an â€˜if’ statement");
+       }
+
        if (next_if(T_else)) {
-               statement->ifs.false_statement = parse_statement();
-       } else if (warning.parentheses &&
-                       true_stmt->kind == STATEMENT_IF &&
+               statement->ifs.false_statement = parse_inner_statement();
+
+               if (statement->ifs.false_statement->kind == STATEMENT_EMPTY) {
+                       warningf(WARN_EMPTY_BODY, HERE,
+                                       "suggest braces around empty body in an â€˜if’ statement");
+               }
+       } else if (true_stmt->kind == STATEMENT_IF &&
                        true_stmt->ifs.false_statement != NULL) {
-               warningf(&true_stmt->base.source_position,
-                               "suggest explicit braces to avoid ambiguous 'else'");
+               source_position_t const *const pos = &true_stmt->base.source_position;
+               warningf(WARN_PARENTHESES, pos, "suggest explicit braces to avoid ambiguous 'else'");
        }
 
-       POP_PARENT;
+       POP_PARENT();
        return statement;
 }
 
@@ -9582,6 +9302,8 @@ end_error:
  */
 static void check_enum_cases(const switch_statement_t *statement)
 {
+       if (!is_warn_on(WARN_SWITCH_ENUM))
+               return;
        const type_t *type = skip_typeref(statement->expression->base.type);
        if (! is_type_enum(type))
                return;
@@ -9608,10 +9330,9 @@ static void check_enum_cases(const switch_statement_t *statement)
                                break;
                        }
                }
-               if (! found) {
-                       warningf(&statement->base.source_position,
-                                "enumeration value '%Y' not handled in switch",
-                                entry->base.symbol);
+               if (!found) {
+                       source_position_t const *const pos = &statement->base.source_position;
+                       warningf(WARN_SWITCH_ENUM, pos, "'%N' not handled in switch", entry);
                }
                last_value = value;
        }
@@ -9635,12 +9356,8 @@ static statement_t *parse_switch(void)
        type_t       *      type = skip_typeref(expr->base.type);
        if (is_type_integer(type)) {
                type = promote_integer(type);
-               if (warning.traditional) {
-                       if (get_rank(type) >= get_akind_rank(ATOMIC_TYPE_LONG)) {
-                               warningf(&expr->base.source_position,
-                                       "'%T' switch expression not converted to '%T' in ISO C",
-                                       type, type_int);
-                       }
+               if (get_rank(type) >= get_akind_rank(ATOMIC_TYPE_LONG)) {
+                       warningf(WARN_TRADITIONAL, &expr->base.source_position, "'%T' switch expression not converted to '%T' in ISO C", type, type_int);
                }
        } else if (is_type_valid(type)) {
                errorf(&expr->base.source_position,
@@ -9653,20 +9370,18 @@ static statement_t *parse_switch(void)
 
        switch_statement_t *rem = current_switch;
        current_switch          = &statement->switchs;
-       statement->switchs.body = parse_statement();
+       statement->switchs.body = parse_inner_statement();
        current_switch          = rem;
 
-       if (warning.switch_default &&
-           statement->switchs.default_label == NULL) {
-               warningf(&statement->base.source_position, "switch has no default case");
+       if (statement->switchs.default_label == NULL) {
+               warningf(WARN_SWITCH_DEFAULT, &statement->base.source_position, "switch has no default case");
        }
-       if (warning.switch_enum)
-               check_enum_cases(&statement->switchs);
+       check_enum_cases(&statement->switchs);
 
-       POP_PARENT;
+       POP_PARENT();
        return statement;
 end_error:
-       POP_PARENT;
+       POP_PARENT();
        return create_invalid_statement();
 }
 
@@ -9675,7 +9390,7 @@ static statement_t *parse_loop_body(statement_t *const loop)
        statement_t *const rem = current_loop;
        current_loop = loop;
 
-       statement_t *const body = parse_statement();
+       statement_t *const body = parse_inner_statement();
 
        current_loop = rem;
        return body;
@@ -9705,10 +9420,10 @@ static statement_t *parse_while(void)
 
        statement->whiles.body = parse_loop_body(statement);
 
-       POP_PARENT;
+       POP_PARENT();
        return statement;
 end_error:
-       POP_PARENT;
+       POP_PARENT();
        return create_invalid_statement();
 }
 
@@ -9740,10 +9455,10 @@ static statement_t *parse_do(void)
        expect(')', end_error);
        expect(';', end_error);
 
-       POP_PARENT;
+       POP_PARENT();
        return statement;
 end_error:
-       POP_PARENT;
+       POP_PARENT();
        return create_invalid_statement();
 }
 
@@ -9760,33 +9475,28 @@ static statement_t *parse_for(void)
        add_anchor_token(')');
 
        PUSH_PARENT(statement);
+       PUSH_SCOPE(&statement->fors.scope);
 
-       size_t const  top       = environment_top();
-       scope_t      *old_scope = scope_push(&statement->fors.scope);
-
-       bool old_gcc_extension = in_gcc_extension;
-       while (next_if(T___extension__)) {
-               in_gcc_extension = true;
-       }
+       PUSH_EXTENSION();
 
        if (next_if(';')) {
-       } else if (is_declaration_specifier(&token, false)) {
+       } else if (is_declaration_specifier(&token)) {
                parse_declaration(record_entity, DECL_FLAGS_NONE);
        } else {
                add_anchor_token(';');
                expression_t *const init = parse_expression();
                statement->fors.initialisation = init;
                mark_vars_read(init, ENT_ANY);
-               if (warning.unused_value && !expression_has_effect(init)) {
-                       warningf(&init->base.source_position,
-                                       "initialisation of 'for'-statement has no effect");
+               if (!expression_has_effect(init)) {
+                       warningf(WARN_UNUSED_VALUE, &init->base.source_position, "initialisation of 'for'-statement has no effect");
                }
                rem_anchor_token(';');
                expect(';', end_error2);
        }
-       in_gcc_extension = old_gcc_extension;
 
-       if (token.type != ';') {
+       POP_EXTENSION();
+
+       if (token.kind != ';') {
                add_anchor_token(';');
                expression_t *const cond = parse_expression();
                statement->fors.condition = cond;
@@ -9797,32 +9507,26 @@ static statement_t *parse_for(void)
                rem_anchor_token(';');
        }
        expect(';', end_error2);
-       if (token.type != ')') {
+       if (token.kind != ')') {
                expression_t *const step = parse_expression();
                statement->fors.step = step;
                mark_vars_read(step, ENT_ANY);
-               if (warning.unused_value && !expression_has_effect(step)) {
-                       warningf(&step->base.source_position,
-                                "step of 'for'-statement has no effect");
+               if (!expression_has_effect(step)) {
+                       warningf(WARN_UNUSED_VALUE, &step->base.source_position, "step of 'for'-statement has no effect");
                }
        }
        expect(')', end_error2);
        rem_anchor_token(')');
        statement->fors.body = parse_loop_body(statement);
 
-       assert(current_scope == &statement->fors.scope);
-       scope_pop(old_scope);
-       environment_pop_to(top);
-
-       POP_PARENT;
+       POP_SCOPE();
+       POP_PARENT();
        return statement;
 
 end_error2:
-       POP_PARENT;
+       POP_PARENT();
        rem_anchor_token(')');
-       assert(current_scope == &statement->fors.scope);
-       scope_pop(old_scope);
-       environment_pop_to(top);
+       POP_SCOPE();
        /* fallthrough */
 
 end_error1:
@@ -9849,18 +9553,17 @@ static statement_t *parse_goto(void)
                        if (!is_type_pointer(type) && !is_type_integer(type)) {
                                errorf(&expression->base.source_position,
                                        "cannot convert to a pointer type");
-                       } else if (warning.other && type != type_void_ptr) {
-                               warningf(&expression->base.source_position,
-                                       "type of computed goto expression should be 'void*' not '%T'", type);
+                       } else if (type != type_void_ptr) {
+                               warningf(WARN_OTHER, &expression->base.source_position, "type of computed goto expression should be 'void*' not '%T'", type);
                        }
                        expression = create_implicit_cast(expression, type_void_ptr);
                }
 
                statement->gotos.expression = expression;
-       } else if (token.type == T_IDENTIFIER) {
-               symbol_t *symbol = token.symbol;
-               next_token();
-               statement->gotos.label = get_label(symbol);
+       } else if (token.kind == T_IDENTIFIER) {
+               label_t *const label = get_label();
+               label->used            = true;
+               statement->gotos.label = label;
        } else {
                if (GNU_MODE)
                        parse_error_expected("while parsing goto", T_IDENTIFIER, '*', NULL);
@@ -9990,12 +9693,11 @@ entity_t *expression_is_variable(const expression_t *expression)
  */
 static statement_t *parse_return(void)
 {
-       eat(T_return);
-
        statement_t *statement = allocate_statement_zero(STATEMENT_RETURN);
+       eat(T_return);
 
        expression_t *return_value = NULL;
-       if (token.type != ';') {
+       if (token.kind != ';') {
                return_value = parse_expression();
                mark_vars_read(return_value, NULL);
        }
@@ -10015,18 +9717,16 @@ static statement_t *parse_return(void)
                                if (c_mode & _CXX || strict_mode) {
                                        errorf(pos,
                                                        "'return' with a value, in function returning 'void'");
-                               } else if (warning.other) {
-                                       warningf(pos,
-                                                       "'return' with a value, in function returning 'void'");
+                               } else {
+                                       warningf(WARN_OTHER, pos, "'return' with a value, in function returning 'void'");
                                }
                        } else if (!(c_mode & _CXX)) { /* ISO/IEC 14882:1998(E) Â§6.6.3:3 */
                                /* Only warn in C mode, because GCC does the same */
                                if (strict_mode) {
                                        errorf(pos,
                                                        "'return' with expression in function returning 'void'");
-                               } else if (warning.other) {
-                                       warningf(pos,
-                                                       "'return' with expression in function returning 'void'");
+                               } else {
+                                       warningf(WARN_OTHER, pos, "'return' with expression in function returning 'void'");
                                }
                        }
                } else {
@@ -10036,21 +9736,19 @@ static statement_t *parse_return(void)
                }
                return_value = create_implicit_cast(return_value, return_type);
                /* check for returning address of a local var */
-               if (warning.other && return_value != NULL
-                   && return_value->base.kind == EXPR_UNARY_TAKE_ADDRESS) {
+               if (return_value != NULL && return_value->base.kind == EXPR_UNARY_TAKE_ADDRESS) {
                        const expression_t *expression = return_value->unary.value;
                        if (expression_is_local_variable(expression)) {
-                               warningf(pos, "function returns address of local variable");
+                               warningf(WARN_OTHER, pos, "function returns address of local variable");
                        }
                }
-       } else if (warning.other && !is_type_atomic(return_type, ATOMIC_TYPE_VOID)) {
+       } else if (!is_type_atomic(return_type, ATOMIC_TYPE_VOID)) {
                /* ISO/IEC 14882:1998(E) Â§6.6.3:3 */
                if (c_mode & _CXX || strict_mode) {
                        errorf(pos,
                               "'return' without value, in function returning non-void");
                } else {
-                       warningf(pos,
-                                "'return' without value, in function returning non-void");
+                       warningf(WARN_OTHER, pos, "'return' without value, in function returning non-void");
                }
        }
        statement->returns.value = return_value;
@@ -10117,7 +9815,7 @@ static statement_t *parse_ms_try_statment(void)
        statement->ms_try.try_statement = parse_compound_statement(false);
        current_try = rem;
 
-       POP_PARENT;
+       POP_PARENT();
 
        if (next_if(T___except)) {
                expect('(', end_error);
@@ -10149,9 +9847,7 @@ end_error:
 
 static statement_t *parse_empty_statement(void)
 {
-       if (warning.empty_statement) {
-               warningf(HERE, "statement is empty");
-       }
+       warningf(WARN_EMPTY_STATEMENT, HERE, "statement is empty");
        statement_t *const statement = create_empty_statement();
        eat(';');
        return statement;
@@ -10167,23 +9863,20 @@ static statement_t *parse_local_label_declaration(void)
        entity_t *end     = NULL;
        entity_t **anchor = &begin;
        do {
-               if (token.type != T_IDENTIFIER) {
+               if (token.kind != T_IDENTIFIER) {
                        parse_error_expected("while parsing local label declaration",
                                T_IDENTIFIER, NULL);
                        goto end_error;
                }
-               symbol_t *symbol = token.symbol;
+               symbol_t *symbol = token.identifier.symbol;
                entity_t *entity = get_entity(symbol, NAMESPACE_LABEL);
                if (entity != NULL && entity->base.parent_scope == current_scope) {
-                       errorf(HERE, "multiple definitions of '__label__ %Y' (previous definition %P)",
-                              symbol, &entity->base.source_position);
+                       source_position_t const *const ppos = &entity->base.source_position;
+                       errorf(HERE, "multiple definitions of '%N' (previous definition %P)", entity, ppos);
                } else {
-                       entity = allocate_entity_zero(ENTITY_LOCAL_LABEL);
-
+                       entity = allocate_entity_zero(ENTITY_LOCAL_LABEL, NAMESPACE_LABEL, symbol);
                        entity->base.parent_scope    = current_scope;
-                       entity->base.namespc         = NAMESPACE_LABEL;
-                       entity->base.source_position = token.source_position;
-                       entity->base.symbol          = symbol;
+                       entity->base.source_position = token.base.source_position;
 
                        *anchor = entity;
                        anchor  = &entity->base.next;
@@ -10207,8 +9900,8 @@ static void parse_namespace_definition(void)
        entity_t *entity = NULL;
        symbol_t *symbol = NULL;
 
-       if (token.type == T_IDENTIFIER) {
-               symbol = token.symbol;
+       if (token.kind == T_IDENTIFIER) {
+               symbol = token.identifier.symbol;
                next_token();
 
                entity = get_entity(symbol, NAMESPACE_NORMAL);
@@ -10216,7 +9909,7 @@ static void parse_namespace_definition(void)
                                && entity->kind != ENTITY_NAMESPACE
                                && entity->base.parent_scope == current_scope) {
                        if (is_entity_valid(entity)) {
-                               error_redefined_as_different_kind(&token.source_position,
+                               error_redefined_as_different_kind(&token.base.source_position,
                                                entity, ENTITY_NAMESPACE);
                        }
                        entity = NULL;
@@ -10224,14 +9917,12 @@ static void parse_namespace_definition(void)
        }
 
        if (entity == NULL) {
-               entity                       = allocate_entity_zero(ENTITY_NAMESPACE);
-               entity->base.symbol          = symbol;
-               entity->base.source_position = token.source_position;
-               entity->base.namespc         = NAMESPACE_NORMAL;
+               entity = allocate_entity_zero(ENTITY_NAMESPACE, NAMESPACE_NORMAL, symbol);
+               entity->base.source_position = token.base.source_position;
                entity->base.parent_scope    = current_scope;
        }
 
-       if (token.type == '=') {
+       if (token.kind == '=') {
                /* TODO: parse namespace alias */
                panic("namespace alias definition not supported yet");
        }
@@ -10239,8 +9930,7 @@ static void parse_namespace_definition(void)
        environment_push(entity);
        append_entity(current_scope, entity);
 
-       size_t const  top       = environment_top();
-       scope_t      *old_scope = scope_push(&entity->namespacee.members);
+       PUSH_SCOPE(&entity->namespacee.members);
 
        entity_t     *old_current_entity = current_entity;
        current_entity = entity;
@@ -10250,11 +9940,9 @@ static void parse_namespace_definition(void)
        expect('}', end_error);
 
 end_error:
-       assert(current_scope == &entity->namespacee.members);
        assert(current_entity == entity);
        current_entity = old_current_entity;
-       scope_pop(old_scope);
-       environment_pop_to(top);
+       POP_SCOPE();
 }
 
 /**
@@ -10268,12 +9956,12 @@ static statement_t *intern_parse_statement(void)
 
        /* declaration or statement */
        add_anchor_token(';');
-       switch (token.type) {
+       switch (token.kind) {
        case T_IDENTIFIER: {
-               token_type_t la1_type = (token_type_t)look_ahead(1)->type;
+               token_kind_t la1_type = (token_kind_t)look_ahead(1)->kind;
                if (la1_type == ':') {
                        statement = parse_label_statement();
-               } else if (is_typedef_symbol(token.symbol)) {
+               } else if (is_typedef_symbol(token.identifier.symbol)) {
                        statement = parse_declaration_statement();
                } else {
                        /* it's an identifier, the grammar says this must be an
@@ -10283,7 +9971,7 @@ static statement_t *intern_parse_statement(void)
                        switch (la1_type) {
                        case '&':
                        case '*':
-                               if (get_entity(token.symbol, NAMESPACE_NORMAL) != NULL) {
+                               if (get_entity(token.identifier.symbol, NAMESPACE_NORMAL) != NULL) {
                        default:
                                        statement = parse_expression_statement();
                                } else {
@@ -10297,15 +9985,14 @@ static statement_t *intern_parse_statement(void)
                break;
        }
 
-       case T___extension__:
+       case T___extension__: {
                /* This can be a prefix to a declaration or an expression statement.
                 * We simply eat it now and parse the rest with tail recursion. */
-               while (next_if(T___extension__)) {}
-               bool old_gcc_extension = in_gcc_extension;
-               in_gcc_extension       = true;
+               PUSH_EXTENSION();
                statement = intern_parse_statement();
-               in_gcc_extension = old_gcc_extension;
+               POP_EXTENSION();
                break;
+       }
 
        DECLARATION_START
                statement = parse_declaration_statement();
@@ -10361,11 +10048,10 @@ static statement_t *parse_statement(void)
 {
        statement_t *statement = intern_parse_statement();
 
-       if (statement->kind == STATEMENT_EXPRESSION && warning.unused_value) {
+       if (statement->kind == STATEMENT_EXPRESSION) {
                expression_t *expression = statement->expression.expression;
                if (!expression_has_effect(expression)) {
-                       warningf(&expression->base.source_position,
-                                       "statement has no effect");
+                       warningf(WARN_UNUSED_VALUE, &expression->base.source_position, "statement has no effect");
                }
        }
 
@@ -10380,6 +10066,7 @@ static statement_t *parse_compound_statement(bool inside_expression_statement)
        statement_t *statement = allocate_statement_zero(STATEMENT_COMPOUND);
 
        PUSH_PARENT(statement);
+       PUSH_SCOPE(&statement->compound.scope);
 
        eat('{');
        add_anchor_token('}');
@@ -10472,13 +10159,10 @@ static statement_t *parse_compound_statement(bool inside_expression_statement)
        add_anchor_token(T_wchar_t);
        add_anchor_token(T_while);
 
-       size_t const  top       = environment_top();
-       scope_t      *old_scope = scope_push(&statement->compound.scope);
-
        statement_t **anchor            = &statement->compound.statements;
        bool          only_decls_so_far = true;
-       while (token.type != '}') {
-               if (token.type == T_EOF) {
+       while (token.kind != '}') {
+               if (token.kind == T_EOF) {
                        errorf(&statement->base.source_position,
                               "EOF while parsing compound statement");
                        break;
@@ -10491,13 +10175,11 @@ static statement_t *parse_compound_statement(bool inside_expression_statement)
                        continue;
                }
 
-               if (warning.declaration_after_statement) {
-                       if (sub_statement->kind != STATEMENT_DECLARATION) {
-                               only_decls_so_far = false;
-                       } else if (!only_decls_so_far) {
-                               warningf(&sub_statement->base.source_position,
-                                        "ISO C90 forbids mixed declarations and code");
-                       }
+               if (sub_statement->kind != STATEMENT_DECLARATION) {
+                       only_decls_so_far = false;
+               } else if (!only_decls_so_far) {
+                       source_position_t const *const pos = &sub_statement->base.source_position;
+                       warningf(WARN_DECLARATION_AFTER_STATEMENT, pos, "ISO C90 forbids mixed declarations and code");
                }
 
                *anchor = sub_statement;
@@ -10510,7 +10192,7 @@ static statement_t *parse_compound_statement(bool inside_expression_statement)
        next_token();
 
        /* look over all statements again to produce no effect warnings */
-       if (warning.unused_value) {
+       if (is_warn_on(WARN_UNUSED_VALUE)) {
                statement_t *sub_statement = statement->compound.statements;
                for (; sub_statement != NULL; sub_statement = sub_statement->base.next) {
                        if (sub_statement->kind != STATEMENT_EXPRESSION)
@@ -10522,8 +10204,7 @@ static statement_t *parse_compound_statement(bool inside_expression_statement)
 
                        expression_t *expression = sub_statement->expression.expression;
                        if (!expression_has_effect(expression)) {
-                               warningf(&expression->base.source_position,
-                                        "statement has no effect");
+                               warningf(WARN_UNUSED_VALUE, &expression->base.source_position, "statement has no effect");
                        }
                }
        }
@@ -10616,11 +10297,9 @@ end_error:
        rem_anchor_token('&');
        rem_anchor_token('!');
        rem_anchor_token('}');
-       assert(current_scope == &statement->compound.scope);
-       scope_pop(old_scope);
-       environment_pop_to(top);
 
-       POP_PARENT;
+       POP_SCOPE();
+       POP_PARENT();
        return statement;
 }
 
@@ -10629,7 +10308,7 @@ end_error:
  */
 static void check_unused_globals(void)
 {
-       if (!warning.unused_function && !warning.unused_variable)
+       if (!is_warn_on(WARN_UNUSED_FUNCTION) && !is_warn_on(WARN_UNUSED_VARIABLE))
                return;
 
        for (const entity_t *entity = file_scope->entities; entity != NULL;
@@ -10644,20 +10323,21 @@ static void check_unused_globals(void)
                    declaration->storage_class != STORAGE_CLASS_STATIC)
                        continue;
 
-               type_t *const type = declaration->type;
-               const char *s;
+               warning_t   why;
+               char const *s;
                if (entity->kind == ENTITY_FUNCTION) {
                        /* inhibit warning for static inline functions */
                        if (entity->function.is_inline)
                                continue;
 
-                       s = entity->function.statement != NULL ? "defined" : "declared";
+                       why = WARN_UNUSED_FUNCTION;
+                       s   = entity->function.statement != NULL ? "defined" : "declared";
                } else {
-                       s = "defined";
+                       why = WARN_UNUSED_VARIABLE;
+                       s   = "defined";
                }
 
-               warningf(&declaration->base.source_position, "'%#T' %s but not used",
-                       type, declaration->base.symbol, s);
+               warningf(why, &declaration->base.source_position, "'%#N' %s but not used", entity, s);
        }
 }
 
@@ -10682,7 +10362,8 @@ static void parse_linkage_specification(void)
 {
        eat(T_extern);
 
-       const char *linkage = parse_string_literals().begin;
+       source_position_t const pos     = *HERE;
+       char const       *const linkage = parse_string_literals().begin;
 
        linkage_kind_t old_linkage = current_linkage;
        linkage_kind_t new_linkage;
@@ -10691,7 +10372,7 @@ static void parse_linkage_specification(void)
        } else if (strcmp(linkage, "C++") == 0) {
                new_linkage = LINKAGE_CXX;
        } else {
-               errorf(HERE, "linkage string \"%s\" not recognized", linkage);
+               errorf(&pos, "linkage string \"%s\" not recognized", linkage);
                new_linkage = LINKAGE_INVALID;
        }
        current_linkage = new_linkage;
@@ -10710,23 +10391,22 @@ end_error:
 
 static void parse_external(void)
 {
-       switch (token.type) {
+       switch (token.kind) {
+               case T_extern:
+                       if (look_ahead(1)->kind == T_STRING_LITERAL) {
+                               parse_linkage_specification();
+                       } else {
                DECLARATION_START_NO_EXTERN
                case T_IDENTIFIER:
                case T___extension__:
                /* tokens below are for implicit int */
-               case '&': /* & x; -> int& x; (and error later, because C++ has no
-                            implicit int) */
-               case '*': /* * x; -> int* x; */
-               case '(': /* (x); -> int (x); */
-                       parse_external_declaration();
-                       return;
-
-               case T_extern:
-                       if (look_ahead(1)->type == T_STRING_LITERAL) {
-                               parse_linkage_specification();
-                       } else {
+               case '&':  /* & x; -> int& x; (and error later, because C++ has no
+                             implicit int) */
+               case '*':  /* * x; -> int* x; */
+               case '(':  /* (x); -> int (x); */
+                               PUSH_EXTENSION();
                                parse_external_declaration();
+                               POP_EXTENSION();
                        }
                        return;
 
@@ -10740,8 +10420,7 @@ static void parse_external(void)
 
                case ';':
                        if (!strict_mode) {
-                               if (warning.other)
-                                       warningf(HERE, "stray ';' outside of function");
+                               warningf(WARN_STRAY_SEMICOLON, HERE, "stray ';' outside of function");
                                next_token();
                                return;
                        }
@@ -10749,8 +10428,8 @@ static void parse_external(void)
 
                default:
                        errorf(HERE, "stray %K outside of function", &token);
-                       if (token.type == '(' || token.type == '{' || token.type == '[')
-                               eat_until_matching_token(token.type);
+                       if (token.kind == '(' || token.kind == '{' || token.kind == '[')
+                               eat_until_matching_token(token.kind);
                        next_token();
                        return;
        }
@@ -10763,29 +10442,23 @@ static void parse_externals(void)
 
 #ifndef NDEBUG
        /* make a copy of the anchor set, so we can check if it is restored after parsing */
-       unsigned char token_anchor_copy[T_LAST_TOKEN];
+       unsigned short token_anchor_copy[T_LAST_TOKEN];
        memcpy(token_anchor_copy, token_anchor_set, sizeof(token_anchor_copy));
 #endif
 
-       while (token.type != T_EOF && token.type != '}') {
+       while (token.kind != T_EOF && token.kind != '}') {
 #ifndef NDEBUG
-               bool anchor_leak = false;
                for (int i = 0; i < T_LAST_TOKEN; ++i) {
-                       unsigned char count = token_anchor_set[i] - token_anchor_copy[i];
+                       unsigned short count = token_anchor_set[i] - token_anchor_copy[i];
                        if (count != 0) {
                                /* the anchor set and its copy differs */
-                               errorf(HERE, "Leaked anchor token %k %d times", i, count);
-                               anchor_leak = true;
+                               internal_errorf(HERE, "Leaked anchor token %k %d times", i, count);
                        }
                }
                if (in_gcc_extension) {
                        /* an gcc extension scope was not closed */
-                       errorf(HERE, "Leaked __extension__");
-                       anchor_leak = true;
+                       internal_errorf(HERE, "Leaked __extension__");
                }
-
-               if (anchor_leak)
-                       abort();
 #endif
 
                parse_external();
@@ -10805,16 +10478,21 @@ static void parse_translation_unit(void)
        while (true) {
                parse_externals();
 
-               if (token.type == T_EOF)
+               if (token.kind == T_EOF)
                        break;
 
                errorf(HERE, "stray %K outside of function", &token);
-               if (token.type == '(' || token.type == '{' || token.type == '[')
-                       eat_until_matching_token(token.type);
+               if (token.kind == '(' || token.kind == '{' || token.kind == '[')
+                       eat_until_matching_token(token.kind);
                next_token();
        }
 }
 
+void set_default_visibility(elf_visibility_tag_t visibility)
+{
+       default_visibility = visibility;
+}
+
 /**
  * Parse the input.
  *
@@ -10867,18 +10545,14 @@ static void complete_incomplete_arrays(void)
 {
        size_t n = ARR_LEN(incomplete_arrays);
        for (size_t i = 0; i != n; ++i) {
-               declaration_t *const decl      = incomplete_arrays[i];
-               type_t        *const orig_type = decl->type;
-               type_t        *const type      = skip_typeref(orig_type);
+               declaration_t *const decl = incomplete_arrays[i];
+               type_t        *const type = skip_typeref(decl->type);
 
                if (!is_type_incomplete(type))
                        continue;
 
-               if (warning.other) {
-                       warningf(&decl->base.source_position,
-                                       "array '%#T' assumed to have one element",
-                                       orig_type, decl->base.symbol);
-               }
+               source_position_t const *const pos = &decl->base.source_position;
+               warningf(WARN_OTHER, pos, "array '%#N' assumed to have one element", (entity_t const*)decl);
 
                type_t *const new_type = duplicate_type(type);
                new_type->array.size_constant     = true;