+ type = hashed_type;
+ }
+ }
+
+ return type;
+}
+
+static declaration_t *parse_declarator(
+ const declaration_specifiers_t *specifiers, bool may_be_abstract)
+{
+ declaration_t *const declaration = allocate_declaration_zero();
+ declaration->declared_storage_class = specifiers->declared_storage_class;
+ declaration->decl_modifiers = specifiers->decl_modifiers;
+ declaration->deprecated = specifiers->deprecated;
+ declaration->deprecated_string = specifiers->deprecated_string;
+ declaration->get_property_sym = specifiers->get_property_sym;
+ declaration->put_property_sym = specifiers->put_property_sym;
+ declaration->is_inline = specifiers->is_inline;
+
+ declaration->storage_class = specifiers->declared_storage_class;
+ if(declaration->storage_class == STORAGE_CLASS_NONE
+ && scope != global_scope) {
+ declaration->storage_class = STORAGE_CLASS_AUTO;
+ }
+
+ if(specifiers->alignment != 0) {
+ /* TODO: add checks here */
+ declaration->alignment = specifiers->alignment;
+ }
+
+ construct_type_t *construct_type
+ = parse_inner_declarator(declaration, may_be_abstract);
+ type_t *const type = specifiers->type;
+ declaration->type = construct_declarator_type(construct_type, type);
+
+ if(construct_type != NULL) {
+ obstack_free(&temp_obst, construct_type);
+ }
+
+ return declaration;
+}
+
+static type_t *parse_abstract_declarator(type_t *base_type)
+{
+ construct_type_t *construct_type = parse_inner_declarator(NULL, 1);
+
+ type_t *result = construct_declarator_type(construct_type, base_type);
+ if(construct_type != NULL) {
+ obstack_free(&temp_obst, construct_type);
+ }
+
+ return result;
+}
+
+static declaration_t *append_declaration(declaration_t* const declaration)
+{
+ if (last_declaration != NULL) {
+ last_declaration->next = declaration;
+ } else {
+ scope->declarations = declaration;
+ }
+ last_declaration = declaration;
+ return declaration;
+}
+
+/**
+ * Check if the declaration of main is suspicious. main should be a
+ * function with external linkage, returning int, taking either zero
+ * arguments, two, or three arguments of appropriate types, ie.
+ *
+ * int main([ int argc, char **argv [, char **env ] ]).
+ *
+ * @param decl the declaration to check
+ * @param type the function type of the declaration
+ */
+static void check_type_of_main(const declaration_t *const decl, const function_type_t *const func_type)
+{
+ if (decl->storage_class == STORAGE_CLASS_STATIC) {
+ warningf(&decl->source_position,
+ "'main' is normally a non-static function");
+ }
+ if (skip_typeref(func_type->return_type) != type_int) {
+ warningf(&decl->source_position,
+ "return type of 'main' should be 'int', but is '%T'",
+ func_type->return_type);
+ }
+ const function_parameter_t *parm = func_type->parameters;
+ if (parm != NULL) {
+ type_t *const first_type = parm->type;
+ if (!types_compatible(skip_typeref(first_type), type_int)) {
+ warningf(&decl->source_position,
+ "first argument of 'main' should be 'int', but is '%T'", first_type);
+ }
+ parm = parm->next;
+ if (parm != NULL) {
+ type_t *const second_type = parm->type;
+ if (!types_compatible(skip_typeref(second_type), type_char_ptr_ptr)) {
+ warningf(&decl->source_position,
+ "second argument of 'main' should be 'char**', but is '%T'", second_type);
+ }
+ parm = parm->next;
+ if (parm != NULL) {
+ type_t *const third_type = parm->type;
+ if (!types_compatible(skip_typeref(third_type), type_char_ptr_ptr)) {
+ warningf(&decl->source_position,
+ "third argument of 'main' should be 'char**', but is '%T'", third_type);
+ }
+ parm = parm->next;
+ if (parm != NULL) {
+ warningf(&decl->source_position, "'main' takes only zero, two or three arguments");
+ }
+ }
+ } else {
+ warningf(&decl->source_position, "'main' takes only zero, two or three arguments");
+ }
+ }
+}
+
+/**
+ * Check if a symbol is the equal to "main".
+ */
+static bool is_sym_main(const symbol_t *const sym)
+{
+ return strcmp(sym->string, "main") == 0;
+}
+
+static declaration_t *internal_record_declaration(
+ declaration_t *const declaration,
+ const bool is_function_definition)
+{
+ const symbol_t *const symbol = declaration->symbol;
+ const namespace_t namespc = (namespace_t)declaration->namespc;
+
+ type_t *const orig_type = declaration->type;
+ type_t *const type = skip_typeref(orig_type);
+ if (is_type_function(type) &&
+ type->function.unspecified_parameters &&
+ warning.strict_prototypes) {
+ warningf(&declaration->source_position,
+ "function declaration '%#T' is not a prototype",
+ orig_type, declaration->symbol);
+ }
+
+ if (is_function_definition && warning.main && is_sym_main(symbol)) {
+ check_type_of_main(declaration, &type->function);
+ }
+
+ assert(declaration->symbol != NULL);
+ declaration_t *previous_declaration = get_declaration(symbol, namespc);
+
+ assert(declaration != previous_declaration);
+ if (previous_declaration != NULL) {
+ if (previous_declaration->parent_scope == scope) {
+ /* can happen for K&R style declarations */
+ if(previous_declaration->type == NULL) {
+ previous_declaration->type = declaration->type;
+ }
+
+ const type_t *prev_type = skip_typeref(previous_declaration->type);
+ if (!types_compatible(type, prev_type)) {
+ errorf(&declaration->source_position,
+ "declaration '%#T' is incompatible with '%#T' (declared %P)",
+ orig_type, symbol, previous_declaration->type, symbol,
+ &previous_declaration->source_position);
+ } else {
+ unsigned old_storage_class = previous_declaration->storage_class;
+ if(old_storage_class == STORAGE_CLASS_ENUM_ENTRY) {
+ errorf(&declaration->source_position,
+ "redeclaration of enum entry '%Y' (declared %P)",
+ symbol, &previous_declaration->source_position);
+ return previous_declaration;
+ }
+
+ unsigned new_storage_class = declaration->storage_class;
+
+ if(is_type_incomplete(prev_type)) {
+ previous_declaration->type = type;
+ prev_type = type;
+ }
+
+ /* pretend no storage class means extern for function
+ * declarations (except if the previous declaration is neither
+ * none nor extern) */
+ if (is_type_function(type)) {
+ switch (old_storage_class) {
+ case STORAGE_CLASS_NONE:
+ old_storage_class = STORAGE_CLASS_EXTERN;
+
+ case STORAGE_CLASS_EXTERN:
+ if (is_function_definition) {
+ if (warning.missing_prototypes &&
+ prev_type->function.unspecified_parameters &&
+ !is_sym_main(symbol)) {
+ warningf(&declaration->source_position,
+ "no previous prototype for '%#T'",
+ orig_type, symbol);
+ }
+ } else if (new_storage_class == STORAGE_CLASS_NONE) {
+ new_storage_class = STORAGE_CLASS_EXTERN;
+ }
+ break;
+
+ default: break;
+ }
+ }
+
+ if (old_storage_class == STORAGE_CLASS_EXTERN &&
+ new_storage_class == STORAGE_CLASS_EXTERN) {
+warn_redundant_declaration:
+ if (warning.redundant_decls) {
+ warningf(&declaration->source_position,
+ "redundant declaration for '%Y' (declared %P)",
+ symbol, &previous_declaration->source_position);
+ }
+ } else if (current_function == NULL) {
+ if (old_storage_class != STORAGE_CLASS_STATIC &&
+ new_storage_class == STORAGE_CLASS_STATIC) {
+ errorf(&declaration->source_position,
+ "static declaration of '%Y' follows non-static declaration (declared %P)",
+ symbol, &previous_declaration->source_position);
+ } else {
+ if (old_storage_class != STORAGE_CLASS_EXTERN && !is_function_definition) {
+ goto warn_redundant_declaration;
+ }
+ if (new_storage_class == STORAGE_CLASS_NONE) {
+ previous_declaration->storage_class = STORAGE_CLASS_NONE;
+ previous_declaration->declared_storage_class = STORAGE_CLASS_NONE;
+ }
+ }
+ } else {
+ if (old_storage_class == new_storage_class) {
+ errorf(&declaration->source_position,
+ "redeclaration of '%Y' (declared %P)",
+ symbol, &previous_declaration->source_position);
+ } else {
+ errorf(&declaration->source_position,
+ "redeclaration of '%Y' with different linkage (declared %P)",
+ symbol, &previous_declaration->source_position);
+ }
+ }
+ }
+ return previous_declaration;
+ }
+ } else if (is_function_definition) {
+ if (declaration->storage_class != STORAGE_CLASS_STATIC) {
+ if (warning.missing_prototypes && !is_sym_main(symbol)) {
+ warningf(&declaration->source_position,
+ "no previous prototype for '%#T'", orig_type, symbol);
+ } else if (warning.missing_declarations && !is_sym_main(symbol)) {
+ warningf(&declaration->source_position,
+ "no previous declaration for '%#T'", orig_type,
+ symbol);
+ }
+ }
+ } else if (warning.missing_declarations &&
+ scope == global_scope &&
+ !is_type_function(type) && (
+ declaration->storage_class == STORAGE_CLASS_NONE ||
+ declaration->storage_class == STORAGE_CLASS_THREAD
+ )) {
+ warningf(&declaration->source_position,
+ "no previous declaration for '%#T'", orig_type, symbol);
+ }
+
+ assert(declaration->parent_scope == NULL);
+ assert(scope != NULL);
+
+ declaration->parent_scope = scope;
+
+ environment_push(declaration);
+ return append_declaration(declaration);
+}
+
+static declaration_t *record_declaration(declaration_t *declaration)
+{
+ return internal_record_declaration(declaration, false);
+}
+
+static declaration_t *record_function_definition(declaration_t *declaration)
+{
+ return internal_record_declaration(declaration, true);
+}
+
+static void parser_error_multiple_definition(declaration_t *declaration,
+ const source_position_t *source_position)
+{
+ errorf(source_position, "multiple definition of symbol '%Y' (declared %P)",
+ declaration->symbol, &declaration->source_position);
+}
+
+static bool is_declaration_specifier(const token_t *token,
+ bool only_type_specifiers)
+{
+ switch(token->type) {
+ TYPE_SPECIFIERS
+ return true;
+ case T_IDENTIFIER:
+ return is_typedef_symbol(token->v.symbol);
+
+ case T___extension__:
+ STORAGE_CLASSES
+ TYPE_QUALIFIERS
+ return !only_type_specifiers;
+
+ default:
+ return false;
+ }
+}
+
+static void parse_init_declarator_rest(declaration_t *declaration)
+{
+ eat('=');
+
+ type_t *orig_type = declaration->type;
+ type_t *type = skip_typeref(orig_type);
+
+ if(declaration->init.initializer != NULL) {
+ parser_error_multiple_definition(declaration, HERE);
+ }
+
+ bool must_be_constant = false;
+ if(declaration->storage_class == STORAGE_CLASS_STATIC
+ || declaration->storage_class == STORAGE_CLASS_THREAD_STATIC
+ || declaration->parent_scope == global_scope) {
+ must_be_constant = true;
+ }
+
+ parse_initializer_env_t env;
+ env.type = orig_type;
+ env.must_be_constant = must_be_constant;
+ env.declaration = declaration;
+
+ initializer_t *initializer = parse_initializer(&env);
+
+ if(env.type != orig_type) {
+ orig_type = env.type;
+ type = skip_typeref(orig_type);
+ declaration->type = env.type;
+ }
+
+ if(is_type_function(type)) {
+ errorf(&declaration->source_position,
+ "initializers not allowed for function types at declator '%Y' (type '%T')",
+ declaration->symbol, orig_type);
+ } else {
+ declaration->init.initializer = initializer;
+ }
+}
+
+/* parse rest of a declaration without any declarator */
+static void parse_anonymous_declaration_rest(
+ const declaration_specifiers_t *specifiers,
+ parsed_declaration_func finished_declaration)
+{
+ eat(';');
+
+ declaration_t *const declaration = allocate_declaration_zero();
+ declaration->type = specifiers->type;
+ declaration->declared_storage_class = specifiers->declared_storage_class;
+ declaration->source_position = specifiers->source_position;
+ declaration->decl_modifiers = specifiers->decl_modifiers;
+
+ if (declaration->declared_storage_class != STORAGE_CLASS_NONE) {
+ warningf(&declaration->source_position,
+ "useless storage class in empty declaration");
+ }
+ declaration->storage_class = STORAGE_CLASS_NONE;
+
+ type_t *type = declaration->type;
+ switch (type->kind) {
+ case TYPE_COMPOUND_STRUCT:
+ case TYPE_COMPOUND_UNION: {
+ if (type->compound.declaration->symbol == NULL) {
+ warningf(&declaration->source_position,
+ "unnamed struct/union that defines no instances");
+ }
+ break;
+ }
+
+ case TYPE_ENUM:
+ break;
+
+ default:
+ warningf(&declaration->source_position, "empty declaration");
+ break;
+ }
+
+ finished_declaration(declaration);
+}
+
+static void parse_declaration_rest(declaration_t *ndeclaration,
+ const declaration_specifiers_t *specifiers,
+ parsed_declaration_func finished_declaration)
+{
+ add_anchor_token(';');
+ add_anchor_token('=');
+ add_anchor_token(',');
+ while(true) {
+ declaration_t *declaration = finished_declaration(ndeclaration);
+
+ type_t *orig_type = declaration->type;
+ type_t *type = skip_typeref(orig_type);
+
+ if (type->kind != TYPE_FUNCTION &&
+ declaration->is_inline &&
+ is_type_valid(type)) {
+ warningf(&declaration->source_position,
+ "variable '%Y' declared 'inline'\n", declaration->symbol);
+ }
+
+ if(token.type == '=') {
+ parse_init_declarator_rest(declaration);
+ }
+
+ if(token.type != ',')
+ break;
+ eat(',');
+
+ ndeclaration = parse_declarator(specifiers, /*may_be_abstract=*/false);
+ }
+ expect(';');
+
+end_error:
+ rem_anchor_token(';');
+ rem_anchor_token('=');
+ rem_anchor_token(',');
+}
+
+static declaration_t *finished_kr_declaration(declaration_t *declaration)
+{
+ symbol_t *symbol = declaration->symbol;
+ if(symbol == NULL) {
+ errorf(HERE, "anonymous declaration not valid as function parameter");
+ return declaration;
+ }
+ namespace_t namespc = (namespace_t) declaration->namespc;
+ if(namespc != NAMESPACE_NORMAL) {
+ return record_declaration(declaration);
+ }
+
+ declaration_t *previous_declaration = get_declaration(symbol, namespc);
+ if(previous_declaration == NULL ||
+ previous_declaration->parent_scope != scope) {
+ errorf(HERE, "expected declaration of a function parameter, found '%Y'",
+ symbol);
+ return declaration;
+ }
+
+ if(previous_declaration->type == NULL) {
+ previous_declaration->type = declaration->type;
+ previous_declaration->declared_storage_class = declaration->declared_storage_class;
+ previous_declaration->storage_class = declaration->storage_class;
+ previous_declaration->parent_scope = scope;
+ return previous_declaration;
+ } else {
+ return record_declaration(declaration);
+ }
+}
+
+static void parse_declaration(parsed_declaration_func finished_declaration)
+{
+ declaration_specifiers_t specifiers;
+ memset(&specifiers, 0, sizeof(specifiers));
+ parse_declaration_specifiers(&specifiers);
+
+ if(token.type == ';') {
+ parse_anonymous_declaration_rest(&specifiers, append_declaration);
+ } else {
+ declaration_t *declaration = parse_declarator(&specifiers, /*may_be_abstract=*/false);
+ parse_declaration_rest(declaration, &specifiers, finished_declaration);
+ }
+}
+
+static void parse_kr_declaration_list(declaration_t *declaration)
+{
+ type_t *type = skip_typeref(declaration->type);
+ if(!is_type_function(type))
+ return;
+
+ if(!type->function.kr_style_parameters)
+ return;
+
+ /* push function parameters */
+ int top = environment_top();
+ scope_t *last_scope = scope;
+ set_scope(&declaration->scope);
+
+ declaration_t *parameter = declaration->scope.declarations;
+ for( ; parameter != NULL; parameter = parameter->next) {
+ assert(parameter->parent_scope == NULL);
+ parameter->parent_scope = scope;
+ environment_push(parameter);
+ }
+
+ /* parse declaration list */
+ while(is_declaration_specifier(&token, false)) {
+ parse_declaration(finished_kr_declaration);
+ }
+
+ /* pop function parameters */
+ assert(scope == &declaration->scope);
+ set_scope(last_scope);
+ environment_pop_to(top);
+
+ /* update function type */
+ type_t *new_type = duplicate_type(type);
+ new_type->function.kr_style_parameters = false;
+
+ function_parameter_t *parameters = NULL;
+ function_parameter_t *last_parameter = NULL;
+
+ declaration_t *parameter_declaration = declaration->scope.declarations;
+ for( ; parameter_declaration != NULL;
+ parameter_declaration = parameter_declaration->next) {
+ type_t *parameter_type = parameter_declaration->type;
+ if(parameter_type == NULL) {
+ if (strict_mode) {
+ errorf(HERE, "no type specified for function parameter '%Y'",
+ parameter_declaration->symbol);
+ } else {
+ if (warning.implicit_int) {
+ warningf(HERE, "no type specified for function parameter '%Y', using 'int'",
+ parameter_declaration->symbol);
+ }
+ parameter_type = type_int;
+ parameter_declaration->type = parameter_type;
+ }