Do not crash when the second parameter of va_start() has no declaration.
[cparser] / parser.c
index 10fd845..920fa15 100644 (file)
--- a/parser.c
+++ b/parser.c
@@ -206,7 +206,8 @@ static size_t get_expression_struct_size(expression_kind_t kind)
                [EXPR_CONDITIONAL]         = sizeof(conditional_expression_t),
                [EXPR_SELECT]              = sizeof(select_expression_t),
                [EXPR_ARRAY_ACCESS]        = sizeof(array_access_expression_t),
-               [EXPR_SIZEOF]              = sizeof(sizeof_expression_t),
+               [EXPR_SIZEOF]              = sizeof(typeprop_expression_t),
+               [EXPR_ALIGNOF]             = sizeof(typeprop_expression_t),
                [EXPR_CLASSIFY_TYPE]       = sizeof(classify_type_expression_t),
                [EXPR_FUNCTION]            = sizeof(string_literal_expression_t),
                [EXPR_PRETTY_FUNCTION]     = sizeof(string_literal_expression_t),
@@ -2713,13 +2714,27 @@ static void parse_kr_declaration_list(declaration_t *declaration)
        declaration->type = type;
 }
 
+static bool first_err = true;
+
+/**
+ * When called with first_err set, prints the name of the current function,
+ * else does noting.
+ */
+static void print_in_function(void) {
+       if (first_err) {
+               first_err = false;
+               diagnosticf("%s: In function '%Y':\n",
+                       current_function->source_position.input_name,
+                       current_function->symbol);
+       }
+}
+
 /**
  * Check if all labels are defined in the current function.
  * Check if all labels are used in the current function.
  */
 static void check_labels(void)
 {
-       bool first_err = true;
        for (const goto_statement_t *goto_statement = goto_first;
            goto_statement != NULL;
            goto_statement = goto_statement->next) {
@@ -2727,12 +2742,7 @@ static void check_labels(void)
 
                label->used = true;
                if (label->source_position.input_name == NULL) {
-                       if (first_err) {
-                               first_err = false;
-                               diagnosticf("%s: In function '%Y':\n",
-                                       current_function->source_position.input_name,
-                                       current_function->symbol);
-                       }
+                       print_in_function();
                        errorf(goto_statement->statement.source_position,
                                "label '%Y' used but not defined", label->symbol);
                 }
@@ -2746,12 +2756,7 @@ static void check_labels(void)
                        const declaration_t *label = label_statement->label;
 
                        if (! label->used) {
-                               if (first_err) {
-                                       first_err = false;
-                                       diagnosticf("%s: In function '%Y':\n",
-                                               current_function->source_position.input_name,
-                                               current_function->symbol);
-                               }
+                               print_in_function();
                                warningf(label_statement->statement.source_position,
                                        "label '%Y' defined but not used", label->symbol);
                        }
@@ -2760,6 +2765,27 @@ static void check_labels(void)
        label_first = label_last = NULL;
 }
 
+/**
+ * Check declarations of current_function for unused entities.
+ */
+static void check_declarations(void)
+{
+       if (warning.unused_parameter) {
+               const scope_t *scope = &current_function->scope;
+
+               const declaration_t *parameter = scope->declarations;
+               for (; parameter != NULL; parameter = parameter->next) {
+                       if (! parameter->used) {
+                               print_in_function();
+                               warningf(parameter->source_position,
+                                       "unused parameter '%Y'", parameter->symbol);
+                       }
+               }
+       }
+       if (warning.unused_variable) {
+       }
+}
+
 static void parse_external_declaration(void)
 {
        /* function-definitions and declarations both start with declaration
@@ -2851,7 +2877,9 @@ static void parse_external_declaration(void)
                current_function                    = declaration;
 
                declaration->init.statement = parse_compound_statement();
+               first_err = true;
                check_labels();
+               check_declarations();
 
                assert(current_function == declaration);
                current_function = old_current_function;
@@ -3217,6 +3245,9 @@ static expression_t *parse_reference(void)
        ref->declaration         = declaration;
        ref->expression.datatype = type;
 
+       /* this declaration is used */
+       declaration->used = true;
+
        return expression;
 }
 
@@ -3413,6 +3444,8 @@ static expression_t *parse_va_start(void)
        expression_t *const expr = parse_assignment_expression();
        if (expr->kind == EXPR_REFERENCE) {
                declaration_t *const decl = expr->reference.declaration;
+               if (decl == NULL)
+                       return create_invalid_expression();
                if (decl->parent_scope == &current_function->scope &&
                    decl->next == NULL) {
                        expression->va_starte.parameter = decl;
@@ -3533,7 +3566,7 @@ static expression_t *parse_compare_builtin(void)
 
        type_t *const type_left  = skip_typeref(orig_type_left);
        type_t *const type_right = skip_typeref(orig_type_right);
-       if(!is_type_floating(type_left) && !is_type_floating(type_right)) {
+       if(!is_type_float(type_left) && !is_type_float(type_right)) {
                if (is_type_valid(type_left) && is_type_valid(type_right)) {
                        type_error_incompatible("invalid operands in comparison",
                                expression->base.source_position, orig_type_left, orig_type_right);
@@ -3577,20 +3610,6 @@ static expression_t *parse_assume(void) {
        return expression;
 }
 
-static expression_t *parse_alignof(void) {
-       eat(T___alignof__);
-
-       expression_t *expression
-               = allocate_expression_zero(EXPR_ALIGNOF);
-
-       expect('(');
-       expression->alignofe.type = parse_typename();
-       expect(')');
-
-       expression->base.datatype = type_size_t;
-       return expression;
-}
-
 static expression_t *parse_primary_expression(void)
 {
        switch(token.type) {
@@ -3632,8 +3651,6 @@ static expression_t *parse_primary_expression(void)
                return parse_builtin_constant();
        case T___builtin_prefetch:
                return parse_builtin_prefetch();
-       case T___alignof__:
-               return parse_alignof();
        case T_assume:
                return parse_assume();
 
@@ -3715,28 +3732,37 @@ static expression_t *parse_array_expression(unsigned precedence,
        return (expression_t*) array_access;
 }
 
-static expression_t *parse_sizeof(unsigned precedence)
+static expression_t *parse_typeprop(expression_kind_t kind, unsigned precedence)
 {
-       eat(T_sizeof);
-
-       sizeof_expression_t *sizeof_expression
-               = allocate_ast_zero(sizeof(sizeof_expression[0]));
-       sizeof_expression->expression.kind     = EXPR_SIZEOF;
-       sizeof_expression->expression.datatype = type_size_t;
+       expression_t *tp_expression
+               = allocate_expression_zero(kind);
+       tp_expression->base.datatype = type_size_t;
 
        if(token.type == '(' && is_declaration_specifier(look_ahead(1), true)) {
                next_token();
-               sizeof_expression->type = parse_typename();
+               tp_expression->typeprop.type = parse_typename();
                expect(')');
        } else {
                expression_t *expression  = parse_sub_expression(precedence);
                expression->base.datatype = revert_automatic_type_conversion(expression);
 
-               sizeof_expression->type            = expression->base.datatype;
-               sizeof_expression->size_expression = expression;
+               tp_expression->typeprop.type          = expression->base.datatype;
+               tp_expression->typeprop.tp_expression = expression;
        }
 
-       return (expression_t*) sizeof_expression;
+       return tp_expression;
+}
+
+static expression_t *parse_sizeof(unsigned precedence)
+{
+       eat(T_sizeof);
+       return parse_typeprop(EXPR_SIZEOF, precedence);
+}
+
+static expression_t *parse_alignof(unsigned precedence)
+{
+       eat(T___alignof__);
+       return parse_typeprop(EXPR_SIZEOF, precedence);
 }
 
 static expression_t *parse_select_expression(unsigned precedence,
@@ -4354,6 +4380,13 @@ static void semantic_comparison(binary_expression_t *expression)
 
        /* TODO non-arithmetic types */
        if(is_type_arithmetic(type_left) && is_type_arithmetic(type_right)) {
+               if (warning.sign_compare &&
+                   (expression->expression.kind != EXPR_BINARY_EQUAL &&
+                    expression->expression.kind != EXPR_BINARY_NOTEQUAL) &&
+                   (is_type_signed(type_left) != is_type_signed(type_right))) {
+                       warningf(expression->expression.source_position,
+                               "comparison between signed and unsigned");
+               }
                type_t *arithmetic_type = semantic_arithmetic(type_left, type_right);
                expression->left  = create_implicit_cast(left, arithmetic_type);
                expression->right = create_implicit_cast(right, arithmetic_type);
@@ -4361,7 +4394,7 @@ static void semantic_comparison(binary_expression_t *expression)
                if (warning.float_equal &&
                    (expression->expression.kind == EXPR_BINARY_EQUAL ||
                     expression->expression.kind == EXPR_BINARY_NOTEQUAL) &&
-                   is_type_floating(arithmetic_type)) {
+                   is_type_float(arithmetic_type)) {
                        warningf(expression->expression.source_position,
                                 "comparing floating point with == or != is unsafe");
                }
@@ -4857,7 +4890,8 @@ static void init_expression_parsers(void)
                                                                  T_PLUSPLUS,   25);
        register_expression_parser(parse_EXPR_UNARY_PREFIX_DECREMENT,
                                                                  T_MINUSMINUS, 25);
-       register_expression_parser(parse_sizeof,                  T_sizeof,     25);
+       register_expression_parser(parse_sizeof,                      T_sizeof, 25);
+       register_expression_parser(parse_alignof,                T___alignof__, 25);
        register_expression_parser(parse_extension,            T___extension__, 25);
        register_expression_parser(parse_builtin_classify_type,
                                                     T___builtin_classify_type, 25);
@@ -5134,10 +5168,11 @@ static statement_t *parse_label_statement(void)
 
        /* remember the labels's in a list for later checking */
        if (label_last == NULL) {
-               label_first = label_last = label_statement;
+               label_first = label_statement;
        } else {
                label_last->next = label_statement;
        }
+       label_last = label_statement;
 
        return (statement_t*) label_statement;
 }
@@ -5322,10 +5357,11 @@ static statement_t *parse_goto(void)
 
        /* remember the goto's in a list for later checking */
        if (goto_last == NULL) {
-               goto_first = goto_last = statement;
+               goto_first = statement;
        } else {
                goto_last->next = statement;
        }
+       goto_last = statement;
 
        expect(';');
 
@@ -5394,6 +5430,31 @@ static bool is_local_var_declaration(const declaration_t *declaration) {
        }
 }
 
+/**
+ * Check if a given declaration represents a variable.
+ */
+static bool is_var_declaration(const declaration_t *declaration) {
+       switch ((storage_class_tag_t) declaration->storage_class) {
+       case STORAGE_CLASS_NONE:
+       case STORAGE_CLASS_EXTERN:
+       case STORAGE_CLASS_STATIC:
+       case STORAGE_CLASS_AUTO:
+       case STORAGE_CLASS_REGISTER:
+       case STORAGE_CLASS_THREAD:
+       case STORAGE_CLASS_THREAD_EXTERN:
+       case STORAGE_CLASS_THREAD_STATIC: {
+               const type_t *type = skip_typeref(declaration->type);
+               if(is_type_function(type)) {
+                       return false;
+               } else {
+                       return true;
+               }
+       }
+       default:
+               return false;
+       }
+}
+
 /**
  * Check if a given expression represents a local variable.
  */
@@ -5406,6 +5467,21 @@ static bool is_local_variable(const expression_t *expression)
        return is_local_var_declaration(declaration);
 }
 
+/**
+ * Check if a given expression represents a local variable and
+ * return its declaration then, else return NULL.
+ */
+declaration_t *expr_is_variable(const expression_t *expression)
+{
+       if (expression->base.kind != EXPR_REFERENCE) {
+               return NULL;
+       }
+       declaration_t *declaration = expression->reference.declaration;
+       if (is_var_declaration(declaration))
+               return declaration;
+       return NULL;
+}
+
 /**
  * Parse a return statement.
  */
@@ -5683,6 +5759,37 @@ static void initialize_builtin_types(void)
        type_wchar_t_ptr   = make_pointer_type(type_wchar_t,   TYPE_QUALIFIER_NONE);
 }
 
+/**
+ * Check for unused global static functions and variables
+ */
+static void check_unused_globals(void)
+{
+       if (!warning.unused_function && !warning.unused_variable)
+               return;
+
+       for (const declaration_t *decl = global_scope->declarations; decl != NULL; decl = decl->next) {
+               if (decl->used || decl->storage_class != STORAGE_CLASS_STATIC)
+                       continue;
+
+               type_t *const type = decl->type;
+               const char *s;
+               if (is_type_function(skip_typeref(type))) {
+                       if (!warning.unused_function || decl->is_inline)
+                               continue;
+
+                       s = (decl->init.statement != NULL ? "defined" : "declared");
+               } else {
+                       if (!warning.unused_variable)
+                               continue;
+
+                       s = "defined";
+               }
+
+               warningf(decl->source_position, "'%#T' %s but not used",
+                       type, decl->symbol, s);
+       }
+}
+
 /**
  * Parse a translation unit.
  */
@@ -5713,6 +5820,7 @@ static translation_unit_t *parse_translation_unit(void)
        last_declaration = NULL;
 
        assert(global_scope == &unit->scope);
+       check_unused_globals();
        global_scope = NULL;
 
        return unit;