Also do not warn about function declarations without a prior declaration. *sigh*
[cparser] / parser.c
index 088421c..5fa99de 100644 (file)
--- a/parser.c
+++ b/parser.c
 #include "type_hash.h"
 #include "ast_t.h"
 #include "lang_features.h"
+#include "warning.h"
 #include "adt/bitfiddle.h"
 #include "adt/error.h"
 #include "adt/array.h"
 
 //#define PRINT_TOKENS
-//#define ABORT_ON_ERROR
 #define MAX_LOOKAHEAD 2
 
 typedef struct {
@@ -983,19 +983,16 @@ static initializer_t *initializer_from_expression(type_t *type,
 }
 
 static initializer_t *parse_sub_initializer(type_t *type,
-                                            expression_t *expression,
-                                            type_t *expression_type);
+                                            expression_t *expression);
 
 static initializer_t *parse_sub_initializer_elem(type_t *type)
 {
        if(token.type == '{') {
-               return parse_sub_initializer(type, NULL, NULL);
+               return parse_sub_initializer(type, NULL);
        }
 
-       expression_t *expression      = parse_assignment_expression();
-       type_t       *expression_type = skip_typeref(expression->base.datatype);
-
-       return parse_sub_initializer(type, expression, expression_type);
+       expression_t *expression = parse_assignment_expression();
+       return parse_sub_initializer(type, expression);
 }
 
 static bool had_initializer_brace_warning;
@@ -1019,8 +1016,7 @@ static void skip_designator(void)
 }
 
 static initializer_t *parse_sub_initializer(type_t *type,
-                                            expression_t *expression,
-                                            type_t *expression_type)
+                                            expression_t *expression)
 {
        if(is_type_scalar(type)) {
                /* there might be extra {} hierarchies */
@@ -1030,7 +1026,7 @@ static initializer_t *parse_sub_initializer(type_t *type,
                                warningf(HERE, "braces around scalar initializer");
                                had_initializer_brace_warning = true;
                        }
-                       initializer_t *result = parse_sub_initializer(type, NULL, NULL);
+                       initializer_t *result = parse_sub_initializer(type, NULL);
                        if(token.type == ',') {
                                next_token();
                                /* TODO: warn about excessive elements */
@@ -1076,8 +1072,7 @@ static initializer_t *parse_sub_initializer(type_t *type,
                if(expression == NULL) {
                        sub = parse_sub_initializer_elem(element_type);
                } else {
-                       sub = parse_sub_initializer(element_type, expression,
-                                                   expression_type);
+                       sub = parse_sub_initializer(element_type, expression);
                }
 
                /* didn't match the subtypes -> try the parent type */
@@ -1127,7 +1122,7 @@ static initializer_t *parse_sub_initializer(type_t *type,
                if(expression == NULL) {
                        sub = parse_sub_initializer_elem(first_type);
                } else {
-                       sub = parse_sub_initializer(first_type, expression,expression_type);
+                       sub = parse_sub_initializer(first_type, expression);
                }
 
                /* didn't match the subtypes -> try our parent type */
@@ -1216,7 +1211,7 @@ static initializer_t *parse_initializer(type_t *const orig_type)
                expect('}');
                return result;
        } else {
-               result = parse_sub_initializer(type, NULL, NULL);
+               result = parse_sub_initializer(type, NULL);
        }
 
        return result;
@@ -1746,7 +1741,9 @@ finish_specifiers:
                        /* invalid specifier combination, give an error message */
                        if(type_specifiers == 0) {
                                if (! strict_mode) {
-                                       warningf(HERE, "no type specifiers in declaration, using int");
+                                       if (warning.implicit_int) {
+                                               warningf(HERE, "no type specifiers in declaration, using 'int'");
+                                       }
                                        atomic_type = ATOMIC_TYPE_INT;
                                        break;
                                } else {
@@ -2252,6 +2249,11 @@ static declaration_t *append_declaration(declaration_t* const declaration)
        return declaration;
 }
 
+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)
@@ -2260,7 +2262,9 @@ static declaration_t *internal_record_declaration(
        const namespace_t     namespc = (namespace_t)declaration->namespc;
 
        const type_t *const type = skip_typeref(declaration->type);
-       if (is_type_function(type) && type->function.unspecified_parameters) {
+       if (is_type_function(type) &&
+                       type->function.unspecified_parameters &&
+                       warning.strict_prototypes) {
                warningf(declaration->source_position,
                         "function declaration '%#T' is not a prototype",
                         type, declaration->symbol);
@@ -2268,68 +2272,92 @@ static declaration_t *internal_record_declaration(
 
        declaration_t *const previous_declaration = get_declaration(symbol, namespc);
        assert(declaration != previous_declaration);
-       if (previous_declaration != NULL
-                       && previous_declaration->parent_context == context) {
-               /* can happen for K&R style declarations */
-               if(previous_declaration->type == NULL) {
-                       previous_declaration->type = declaration->type;
-               }
-
-               const type_t *const 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'",
-                               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;
-
-                       /* 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 (new_storage_class == STORAGE_CLASS_NONE && !is_function_definition) {
-                                                       new_storage_class = STORAGE_CLASS_EXTERN;
-                                               }
-                                               break;
+       if (previous_declaration != NULL) {
+               if (previous_declaration->parent_context == context) {
+                       /* can happen for K&R style declarations */
+                       if(previous_declaration->type == NULL) {
+                               previous_declaration->type = declaration->type;
+                       }
 
-                                       default: break;
+                       const type_t *const 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'",
+                                       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;
+
+                               /* 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'", 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) {
+                               if (old_storage_class == STORAGE_CLASS_EXTERN &&
+                                               new_storage_class == STORAGE_CLASS_EXTERN) {
 warn_redundant_declaration:
-                                       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 (warning.redundant_decls) {
+                                               warningf(declaration->source_position, "redundant declaration for '%Y'", symbol);
+                                               warningf(previous_declaration->source_position, "previous declaration of '%Y' was here", symbol);
                                        }
-                                       if (new_storage_class == STORAGE_CLASS_NONE) {
-                                               previous_declaration->storage_class = STORAGE_CLASS_NONE;
+                               } 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);
+                                       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);
                                }
-                               errorf(previous_declaration->source_position, "previous declaration of '%Y' was here", symbol);
                        }
+                       return previous_declaration;
                }
-               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'", type, symbol);
+                       } else if (warning.missing_declarations && !is_sym_main(symbol)) {
+                               warningf(declaration->source_position, "no previous declaration for '%#T'", type, symbol);
+                       }
+               }
+       } else if (warning.missing_declarations &&
+           context == global_context &&
+           !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'", type, symbol);
        }
 
        assert(declaration->parent_context == NULL);
@@ -2594,8 +2622,10 @@ static void parse_kr_declaration_list(declaration_t *declaration)
                                errorf(HERE, "no type specified for function parameter '%Y'",
                                       parameter_declaration->symbol);
                        } else {
-                               warningf(HERE, "no type specified for function parameter '%Y', using int",
-                                        parameter_declaration->symbol);
+                               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;
                        }
@@ -3086,8 +3116,10 @@ static expression_t *parse_reference(void)
        if(declaration == NULL) {
                if (! strict_mode && token.type == '(') {
                        /* an implicitly defined function */
-                       warningf(HERE, "implicit declaration of function '%Y'",
-                                ref->symbol);
+                       if (warning.implicit_function_declaration) {
+                               warningf(HERE, "implicit declaration of function '%Y'",
+                                       ref->symbol);
+                       }
 
                        declaration = create_implicit_function(ref->symbol,
                                                               source_position);
@@ -3141,10 +3173,6 @@ static expression_t *parse_statement_expression(void)
 
        statement_t *statement           = parse_compound_statement();
        expression->statement.statement  = statement;
-       if(statement == NULL) {
-               expect(')');
-               return NULL;
-       }
        expression->base.source_position = statement->base.source_position;
 
        /* find last statement and use its type */
@@ -3543,14 +3571,13 @@ static expression_t *parse_primary_expression(void)
  * 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 *type      = expression->base.datatype;
-       type_t *base_type = skip_typeref(type);
+       type_t       *const type      = expression->base.datatype;
+       const type_t *const base_type = skip_typeref(type);
 
-       if (base_type->base.kind == TYPE_ATOMIC) {
-               switch (base_type->atomic.akind == ATOMIC_TYPE_CHAR) {
-                       warningf(expression->base.source_position,
-                               "array subscript has type '%T'", type);
-               }
+       if (is_type_atomic(base_type, ATOMIC_TYPE_CHAR) &&
+                       warning.char_subscripts) {
+               warningf(expression->base.source_position,
+                       "array subscript has type '%T'", type);
        }
 }
 
@@ -3933,6 +3960,7 @@ static void semantic_incdec(unary_expression_t *expression)
 {
        type_t *const orig_type = expression->value->base.datatype;
        type_t *const type      = skip_typeref(orig_type);
+       /* TODO !is_type_real && !is_type_pointer */
        if(!is_type_arithmetic(type) && type->kind != TYPE_POINTER) {
                if (is_type_valid(type)) {
                        /* TODO: improve error message */
@@ -4398,8 +4426,115 @@ static void semantic_binexpr_assign(binary_expression_t *expression)
        expression->expression.datatype = orig_type_left;
 }
 
+static bool expression_has_effect(const expression_t *const expr)
+{
+       switch (expr->kind) {
+               case EXPR_UNKNOWN:                   break;
+               case EXPR_INVALID:                   break;
+               case EXPR_REFERENCE:                 return false;
+               case EXPR_CONST:                     return false;
+               case EXPR_STRING_LITERAL:            return false;
+               case EXPR_WIDE_STRING_LITERAL:       return false;
+               case EXPR_CALL: {
+                       const call_expression_t *const call = &expr->call;
+                       if (call->function->kind != EXPR_BUILTIN_SYMBOL)
+                               return true;
+
+                       switch (call->function->builtin_symbol.symbol->ID) {
+                               case T___builtin_va_end: return true;
+                               default:                 return false;
+                       }
+               }
+               case EXPR_CONDITIONAL: {
+                       const conditional_expression_t *const cond = &expr->conditional;
+                       return
+                               expression_has_effect(cond->true_expression) &&
+                               expression_has_effect(cond->false_expression);
+               }
+               case EXPR_SELECT:                    return false;
+               case EXPR_ARRAY_ACCESS:              return false;
+               case EXPR_SIZEOF:                    return false;
+               case EXPR_CLASSIFY_TYPE:             return false;
+               case EXPR_ALIGNOF:                   return false;
+
+               case EXPR_FUNCTION:                  return false;
+               case EXPR_PRETTY_FUNCTION:           return false;
+               case EXPR_BUILTIN_SYMBOL:            break; /* handled in EXPR_CALL */
+               case EXPR_BUILTIN_CONSTANT_P:        return false;
+               case EXPR_BUILTIN_PREFETCH:          return true;
+               case EXPR_OFFSETOF:                  return false;
+               case EXPR_VA_START:                  return true;
+               case EXPR_VA_ARG:                    return true;
+               case EXPR_STATEMENT:                 return true; // TODO
+
+               case EXPR_UNARY_NEGATE:              return false;
+               case EXPR_UNARY_PLUS:                return false;
+               case EXPR_UNARY_BITWISE_NEGATE:      return false;
+               case EXPR_UNARY_NOT:                 return false;
+               case EXPR_UNARY_DEREFERENCE:         return false;
+               case EXPR_UNARY_TAKE_ADDRESS:        return false;
+               case EXPR_UNARY_POSTFIX_INCREMENT:   return true;
+               case EXPR_UNARY_POSTFIX_DECREMENT:   return true;
+               case EXPR_UNARY_PREFIX_INCREMENT:    return true;
+               case EXPR_UNARY_PREFIX_DECREMENT:    return true;
+               case EXPR_UNARY_CAST:
+                       return is_type_atomic(expr->base.datatype, ATOMIC_TYPE_VOID);
+               case EXPR_UNARY_CAST_IMPLICIT:       return true;
+               case EXPR_UNARY_ASSUME:              return true;
+               case EXPR_UNARY_BITFIELD_EXTRACT:    return false;
+
+               case EXPR_BINARY_ADD:                return false;
+               case EXPR_BINARY_SUB:                return false;
+               case EXPR_BINARY_MUL:                return false;
+               case EXPR_BINARY_DIV:                return false;
+               case EXPR_BINARY_MOD:                return false;
+               case EXPR_BINARY_EQUAL:              return false;
+               case EXPR_BINARY_NOTEQUAL:           return false;
+               case EXPR_BINARY_LESS:               return false;
+               case EXPR_BINARY_LESSEQUAL:          return false;
+               case EXPR_BINARY_GREATER:            return false;
+               case EXPR_BINARY_GREATEREQUAL:       return false;
+               case EXPR_BINARY_BITWISE_AND:        return false;
+               case EXPR_BINARY_BITWISE_OR:         return false;
+               case EXPR_BINARY_BITWISE_XOR:        return false;
+               case EXPR_BINARY_SHIFTLEFT:          return false;
+               case EXPR_BINARY_SHIFTRIGHT:         return false;
+               case EXPR_BINARY_ASSIGN:             return true;
+               case EXPR_BINARY_MUL_ASSIGN:         return true;
+               case EXPR_BINARY_DIV_ASSIGN:         return true;
+               case EXPR_BINARY_MOD_ASSIGN:         return true;
+               case EXPR_BINARY_ADD_ASSIGN:         return true;
+               case EXPR_BINARY_SUB_ASSIGN:         return true;
+               case EXPR_BINARY_SHIFTLEFT_ASSIGN:   return true;
+               case EXPR_BINARY_SHIFTRIGHT_ASSIGN:  return true;
+               case EXPR_BINARY_BITWISE_AND_ASSIGN: return true;
+               case EXPR_BINARY_BITWISE_XOR_ASSIGN: return true;
+               case EXPR_BINARY_BITWISE_OR_ASSIGN:  return true;
+               case EXPR_BINARY_LOGICAL_AND:
+               case EXPR_BINARY_LOGICAL_OR:
+               case EXPR_BINARY_COMMA:
+                       return expression_has_effect(expr->binary.right);
+
+               case EXPR_BINARY_BUILTIN_EXPECT:     return true;
+               case EXPR_BINARY_ISGREATER:          return false;
+               case EXPR_BINARY_ISGREATEREQUAL:     return false;
+               case EXPR_BINARY_ISLESS:             return false;
+               case EXPR_BINARY_ISLESSEQUAL:        return false;
+               case EXPR_BINARY_ISLESSGREATER:      return false;
+               case EXPR_BINARY_ISUNORDERED:        return false;
+       }
+
+       panic("unexpected statement");
+}
+
 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");
+               }
+       }
        expression->expression.datatype = expression->right->base.datatype;
 }
 
@@ -4892,7 +5027,14 @@ static statement_t *parse_label_statement(void)
                errorf(HERE, "label at end of compound statement");
                return (statement_t*) label_statement;
        } else {
-               label_statement->label_statement = parse_statement();
+               if (token.type == ';') {
+                       /* eat an empty statement here, to avoid the warning about an empty
+                        * after a label.  label:; is commonly used to have a label before
+                        * a }. */
+                       next_token();
+               } else {
+                       label_statement->label_statement = parse_statement();
+               }
        }
 
        return (statement_t*) label_statement;
@@ -4950,6 +5092,10 @@ static statement_t *parse_switch(void)
        statement->body = parse_statement();
        current_switch  = rem;
 
+       if (warning.switch_default && find_default_label(statement) == NULL) {
+               warningf(statement->statement.source_position, "switch has no default case");
+       }
+
        return (statement_t*) statement;
 }
 
@@ -5248,7 +5394,12 @@ static statement_t *parse_expression_statement(void)
        statement_t *statement = allocate_statement_zero(STATEMENT_EXPRESSION);
 
        statement->base.source_position  = token.source_position;
-       statement->expression.expression = parse_expression();
+       expression_t *const expr         = parse_expression();
+       statement->expression.expression = expr;
+
+       if (warning.unused_value  && !expression_has_effect(expr)) {
+               warningf(expr->base.source_position, "statement has no effect");
+       }
 
        expect(';');
 
@@ -5317,6 +5468,9 @@ static statement_t *parse_statement(void)
                break;
 
        case ';':
+               if (warning.empty_statement) {
+                       warningf(HERE, "statement is empty");
+               }
                next_token();
                statement = NULL;
                break;
@@ -5364,7 +5518,7 @@ static statement_t *parse_statement(void)
  */
 static statement_t *parse_compound_statement(void)
 {
-       compound_statement_t *compound_statement
+       compound_statement_t *const compound_statement
                = allocate_ast_zero(sizeof(compound_statement[0]));
        compound_statement->statement.kind            = STATEMENT_COMPOUND;
        compound_statement->statement.source_position = token.source_position;