+/**
+ * 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);
+ }
+
+ declaration_t *const 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 "
+ "previous declaration '%#T'",
+ orig_type, symbol, previous_declaration->type, symbol);
+ errorf(previous_declaration->source_position,
+ "previous declaration of '%Y' was here", symbol);
+ } else {
+ unsigned old_storage_class
+ = previous_declaration->storage_class;
+ 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'", symbol);
+ warningf(previous_declaration->source_position,
+ "previous declaration of '%Y' was here",
+ symbol);
+ }
+ } 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",
+ symbol);
+ errorf(previous_declaration->source_position,
+ "previous declaration of '%Y' was here", symbol);
+ } 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;
+ }
+ }
+ } else {
+ if (old_storage_class == new_storage_class) {
+ errorf(declaration->source_position,
+ "redeclaration of '%Y'", symbol);
+ } else {
+ errorf(declaration->source_position,
+ "redeclaration of '%Y' with different linkage",
+ symbol);
+ }
+ errorf(previous_declaration->source_position,
+ "previous declaration of '%Y' was here", symbol);
+ }
+ }
+ 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(declaration->symbol != 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);
+}
+