#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 {
}
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;
}
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 */
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 */
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 */
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 */
expect('}');
return result;
} else {
- result = parse_sub_initializer(type, NULL, NULL);
+ result = parse_sub_initializer(type, NULL);
}
return result;
/* 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 {
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)
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);
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);
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;
}
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);
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 */
* 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);
}
}
{
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 */
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;
}
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;
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;
}
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(';');
break;
case ';':
+ if (warning.empty_statement) {
+ warningf(HERE, "statement is empty");
+ }
next_token();
statement = NULL;
break;
*/
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;