*/
static void parse_error(const char *msg)
{
- errorf(&lexer_token.source_position, "%s", msg);
+ errorf(&lexer_token.source_position, "%s", msg);
}
/**
*/
static NORETURN internal_error(const char *msg)
{
- internal_errorf(&lexer_token.source_position, "%s", msg);
+ internal_errorf(&lexer_token.source_position, "%s", msg);
}
static inline void next_real_char(void)
static void parse_integer_suffix(bool is_oct_hex)
{
- bool is_unsigned = false;
- bool min_long = false;
- bool min_longlong = false;
-
- if(c == 'U' || c == 'u') {
- is_unsigned = true;
+ bool is_unsigned = false;
+ bool min_long = false;
+ bool min_longlong = false;
+ bool not_traditional = false;
+ int pos = 0;
+ char suffix[4];
+
+ if (c == 'U' || c == 'u') {
+ not_traditional = true;
+ suffix[pos++] = toupper(c);
+ is_unsigned = true;
next_char();
- if(c == 'L' || c == 'l') {
+ if (c == 'L' || c == 'l') {
+ suffix[pos++] = toupper(c);
min_long = true;
next_char();
- if(c == 'L' || c == 'l') {
+ if (c == 'L' || c == 'l') {
+ suffix[pos++] = toupper(c);
min_longlong = true;
next_char();
}
}
- } else if(c == 'l' || c == 'L') {
+ } else if (c == 'l' || c == 'L') {
+ suffix[pos++] = toupper(c);
min_long = true;
next_char();
- if(c == 'l' || c == 'L') {
- min_longlong = true;
+ if (c == 'l' || c == 'L') {
+ not_traditional = true;
+ suffix[pos++] = toupper(c);
+ min_longlong = true;
next_char();
- if(c == 'u' || c == 'U') {
- is_unsigned = true;
+ if (c == 'u' || c == 'U') {
+ suffix[pos++] = toupper(c);
+ is_unsigned = true;
next_char();
}
- } else if(c == 'u' || c == 'U') {
- is_unsigned = true;
+ } else if (c == 'u' || c == 'U') {
+ not_traditional = true;
+ suffix[pos++] = toupper(c);
+ is_unsigned = true;
next_char();
lexer_token.datatype = type_unsigned_long;
}
}
- if(!is_unsigned) {
+ if (warning.traditional && not_traditional) {
+ suffix[pos] = '\0';
+ warningf(&lexer_token.source_position,
+ "traditional C rejects the '%s' suffix", suffix);
+ }
+ if (!is_unsigned) {
long long v = lexer_token.v.intvalue;
- if(!min_long) {
- if(v >= TARGET_INT_MIN && v <= TARGET_INT_MAX) {
+ if (!min_long) {
+ if (v >= TARGET_INT_MIN && v <= TARGET_INT_MAX) {
lexer_token.datatype = type_int;
return;
- } else if(is_oct_hex && v >= 0 && v <= TARGET_UINT_MAX) {
+ } else if (is_oct_hex && v >= 0 && v <= TARGET_UINT_MAX) {
lexer_token.datatype = type_unsigned_int;
return;
}
}
- if(!min_longlong) {
- if(v >= TARGET_LONG_MIN && v <= TARGET_LONG_MAX) {
+ if (!min_longlong) {
+ if (v >= TARGET_LONG_MIN && v <= TARGET_LONG_MAX) {
lexer_token.datatype = type_long;
return;
- } else if(is_oct_hex && v >= 0 && (unsigned long long)v <= (unsigned long long)TARGET_ULONG_MAX) {
+ } else if (is_oct_hex && v >= 0 && (unsigned long long)v <= (unsigned long long)TARGET_ULONG_MAX) {
lexer_token.datatype = type_unsigned_long;
return;
}
}
unsigned long long uv = (unsigned long long) v;
- if(is_oct_hex && uv > (unsigned long long) TARGET_LONGLONG_MAX) {
+ if (is_oct_hex && uv > (unsigned long long) TARGET_LONGLONG_MAX) {
lexer_token.datatype = type_unsigned_long_long;
return;
}
lexer_token.datatype = type_long_long;
} else {
unsigned long long v = (unsigned long long) lexer_token.v.intvalue;
- if(!min_long && v <= TARGET_UINT_MAX) {
+ if (!min_long && v <= TARGET_UINT_MAX) {
lexer_token.datatype = type_unsigned_int;
return;
}
- if(!min_longlong && v <= TARGET_ULONG_MAX) {
+ if (!min_longlong && v <= TARGET_ULONG_MAX) {
lexer_token.datatype = type_unsigned_long;
return;
}
/* TODO: do something useful with the suffixes... */
case 'f':
case 'F':
+ if (warning.traditional) {
+ warningf(&lexer_token.source_position,
+ "traditional C rejects the 'F' suffix");
+ }
next_char();
lexer_token.datatype = type_float;
break;
case 'l':
case 'L':
+ if (warning.traditional) {
+ warningf(&lexer_token.source_position,
+ "traditional C rejects the 'F' suffix");
+ }
next_char();
lexer_token.datatype = type_long_double;
break;
memcpy(concat, s1->begin, len1);
memcpy(concat + len1, s2->begin, len2 + 1);
+ if (warning.traditional) {
+ warningf(&lexer_token.source_position,
+ "traditional C rejects string constant concatenation");
+ }
#if 0 /* TODO hash */
const char *result = strset_insert(&stringset, concat);
if(result != concat) {
concat[i] = src[i];
}
memcpy(concat + len1, s2->begin, (len2 + 1) * sizeof(*concat));
+ if (warning.traditional) {
+ warningf(&lexer_token.source_position,
+ "traditional C rejects string constant concatenation");
+ }
return (wide_string_t){ concat, len1 + len2 + 1 };
}
wchar_rep_t *const concat = obstack_alloc(&symbol_obstack, (len1 + len2 + 1) * sizeof(*concat));
memcpy(concat, s1->begin, len1 * sizeof(*concat));
memcpy(concat + len1, s2->begin, (len2 + 1) * sizeof(*concat));
+ if (warning.traditional) {
+ warningf(&lexer_token.source_position,
+ "traditional C rejects string constant concatenation");
+ }
return (wide_string_t){ concat, len1 + len2 + 1 };
}
for (size_t i = 0; i != len2 + 1; ++i) {
concat[i] = src[i];
}
+ if (warning.traditional) {
+ warningf(&lexer_token.source_position,
+ "traditional C rejects string constant concatenation");
+ }
return (wide_string_t){ concat, len1 + len2 + 1 };
}
if (token.type == T_IDENTIFIER &&
!is_typedef_symbol(token.v.symbol)) {
- token_type_t la1_type = look_ahead(1)->type;
+ token_type_t la1_type = (token_type_t)look_ahead(1)->type;
if (la1_type == ',' || la1_type == ')') {
type->kr_style_parameters = true;
declarations = parse_identifier_list();
warningf(HERE, "function '%Y' returns an aggregate",
ndeclaration->symbol);
}
+ if (warning.traditional && !type->function.unspecified_parameters) {
+ warningf(HERE, "traditional C rejects ISO C style function definition of function '%Y'",
+ ndeclaration->symbol);
+ }
/* ยง 6.7.5.3 (14) a function definition with () means no
* parameters (and not unspecified parameters) */
}
static void check_call_argument(const function_parameter_t *parameter,
- call_argument_t *argument)
+ call_argument_t *argument, unsigned pos)
{
type_t *expected_type = parameter->type;
type_t *expected_type_skip = skip_typeref(expected_type);
assign_error_t error = ASSIGN_ERROR_INCOMPATIBLE;
expression_t *arg_expr = argument->expression;
+ type_t *arg_type = skip_typeref(arg_expr->base.type);
/* handle transparent union gnu extension */
if (is_type_union(expected_type_skip)
argument->expression = create_implicit_cast(argument->expression,
expected_type);
- /* TODO report exact scope in error messages (like "in 3rd parameter") */
- report_assign_error(error, expected_type, arg_expr, "function call",
- &arg_expr->base.source_position);
+ if (error != ASSIGN_SUCCESS) {
+ /* report exact scope in error messages (like "in argument 3") */
+ char buf[64];
+ snprintf(buf, sizeof(buf), "call argument %u", pos);
+ report_assign_error(error, expected_type, arg_expr, buf,
+ &arg_expr->base.source_position);
+ } else if (warning.traditional | warning.conversion) {
+ if (
+ /* passing as integer instead of float or complex */
+ (is_type_integer(expected_type) &&
+ (is_type_float(arg_type) || is_type_complex(arg_type))) ||
+ /* passing as complex instead of integer or float */
+ (is_type_complex(expected_type) &&
+ (is_type_integer(arg_type) || is_type_float(arg_type))) ||
+ /* passing as float instead of integer or complex */
+ (is_type_float(expected_type) &&
+ (is_type_integer(arg_type) || is_type_complex(arg_type))) ||
+ /* passing as float instead of double */
+ (is_type_float(expected_type) && expected_type != type_double &&
+ is_type_float(arg_type))) {
+ warningf(&arg_expr->base.source_position,
+ "passing call argument %u as '%T' rather than '%T' due to prototype",
+ pos, expected_type, arg_type);
+ }
+ if (is_type_integer(expected_type) && is_type_integer(arg_type)) {
+ /* TODO check for size HERE */
+ }
+ }
}
/**
if (token.type != ')') {
call_argument_t *last_argument = NULL;
- while(true) {
+ while (true) {
call_argument_t *argument = allocate_ast_zero(sizeof(argument[0]));
argument->expression = parse_assignment_expression();
function_parameter_t *parameter = function_type->parameters;
call_argument_t *argument = call->arguments;
if (!function_type->unspecified_parameters) {
- for( ; parameter != NULL && argument != NULL;
+ for (unsigned pos = 0; parameter != NULL && argument != NULL;
parameter = parameter->next, argument = argument->next) {
- check_call_argument(parameter, argument);
+ check_call_argument(parameter, argument, ++pos);
}
if (parameter != NULL) {
if (is_type_valid(type)) {
/* TODO: improve error message */
errorf(&expression->base.source_position,
- "operation needs an arithmetic type");
+ "operation needs an arithmetic type");
}
return;
}
expression->base.type = orig_type;
}
+static void semantic_unexpr_plus(unary_expression_t *expression)
+{
+ semantic_unexpr_arithmetic(expression);
+ if (warning.traditional)
+ warningf(&expression->base.source_position,
+ "traditional C rejects the unary plus operator");
+}
+
static void semantic_not(unary_expression_t *expression)
{
type_t *const orig_type = expression->value->base.type;
CREATE_UNARY_EXPRESSION_PARSER('-', EXPR_UNARY_NEGATE,
semantic_unexpr_arithmetic)
CREATE_UNARY_EXPRESSION_PARSER('+', EXPR_UNARY_PLUS,
- semantic_unexpr_arithmetic)
+ semantic_unexpr_plus)
CREATE_UNARY_EXPRESSION_PARSER('!', EXPR_UNARY_NOT,
semantic_not)
CREATE_UNARY_EXPRESSION_PARSER('*', EXPR_UNARY_DEREFERENCE,
type_t * type = skip_typeref(expr->base.type);
if (is_type_integer(type)) {
type = promote_integer(type);
+ if (warning.traditional) {
+ if (get_rank(type) >= get_akind_rank(ATOMIC_TYPE_LONG)) {
+ warningf(&expr->base.source_position,
+ "'%T' switch expression not converted to '%T' in ISO C",
+ type, type_int);
+ }
+ }
} else if (is_type_valid(type)) {
errorf(&expr->base.source_position,
"switch quantity is not an integer, but '%T'", type);
.cast_qual = false,
.char_subscripts = true,
.comment = false,
+ .conversion = false,
.declaration_after_statement = false,
.deprecated_declarations = true,
.div_by_zero = true,
.strict_prototypes = true,
.switch_default = false,
.switch_enum = false,
+ .traditional = false,
.unknown_pragmas = true,
.unreachable_code = false,
.unused_function = false,
OPT("cast-qual", cast_qual);
OPT("char-subscripts", char_subscripts);
OPT("comment", comment);
+ OPT("conversion", conversion);
OPT("declaration-after-statement", declaration_after_statement);
OPT("deprecated-declarations", deprecated_declarations);
OPT("div_by_zero", div_by_zero);
OPT("strict-prototypes", strict_prototypes);
OPT("switch-default", switch_default);
OPT("switch-enum", switch_enum);
+ OPT("traditional", traditional);
OPT("unknown-pragmas", unknown_pragmas);
OPT("unreachable-code", unreachable_code);
OPTX("unused") {
bool cast_qual:1; /**< Warn whenever a pointer is cast so as to remove a type qualifier from the target type */
bool char_subscripts:1; /**< Warn if an array subscript has the type 'char' */
bool comment:1; /**< Warn whenever a comment-start sequence appears in a comment, or whenever a Backslash-Newline appears in a '//' comment. */
-#if 0 // TODO
bool conversion:1; /**< Warn if a prototype causes a type conversion that is different from what would happen to the same argument in the absence of a prototype */
+#if 0 // TODO
bool cpp_compat:1; /**< Warn about ISO C constructs that are outside of the common subset of ISO C and ISO C++. */
#endif
bool declaration_after_statement:1; /**< Warn when a declaration is found after a statement in a block */
bool strict_prototypes:1; /**< Warn if a function declaration has an unspecified parameter list */
bool switch_default:1; /**< Warn whenever a 'switch' statement does not have a 'default' case */
bool switch_enum:1; /**< Warn about 'switch' statements with an enum as index type and missing case labels or case labels outside the enum range TODO has an alias -Wswitch? */
-#if 0 // TODO
bool traditional:1; /**< Warn about certain constructs that behave differently in traditional and ISO C */
+#if 0 // TODO
bool undef:1; /**< Warn if an undefined identifier is evaluated in an '#if' directive */
bool uninitialized:1; /**< Warn if an automatic variable is used without being initialized or if a variable may be clobbered by a 'setjmp' call. */
#endif