diff --git a/lib/std/encoding/json.c3 b/lib/std/encoding/json.c3 index 75a6c18ae..cc91b86f3 100644 --- a/lib/std/encoding/json.c3 +++ b/lib/std/encoding/json.c3 @@ -249,7 +249,7 @@ fn JsonTokenType? advance(JsonContext* context) @local { char c; // Skip whitespace - while WS: (c = read_next(context)!) + while WS: ((c = read_next(context)!)) { switch (c) { @@ -272,12 +272,12 @@ fn JsonTokenType? advance(JsonContext* context) @local while COMMENT: (true) { // Skip to */ - while (c = read_next(context)!) + while ((c = read_next(context)!)) { if (c == '\n') context.line++; if (c != '*') continue; // Skip through all the '*' - while (c = read_next(context)!) + while ((c = read_next(context)!)) { if (c == '\n') context.line++; if (c != '*') break; diff --git a/releasenotes.md b/releasenotes.md index e7da7d712..2217b7810 100644 --- a/releasenotes.md +++ b/releasenotes.md @@ -15,6 +15,7 @@ - Reduced memory usage for backtraces on Linux. - On win32 utf-8 console output is now enabled by default in compiled programs - Add `$$VERSION` and `$$PRERELEASE` compile time constants. +- Require () around assignment in conditionals. #2716 ### Fixes - Regression with npot vector in struct triggering an assert #2219. diff --git a/src/compiler/sema_stmts.c b/src/compiler/sema_stmts.c index d0a9ccca9..cdd80be24 100644 --- a/src/compiler/sema_stmts.c +++ b/src/compiler/sema_stmts.c @@ -1192,15 +1192,26 @@ static inline bool sema_analyse_cond(SemaContext *context, Expr *expr, CondType { RETURN_SEMA_ERROR(last->decl_expr->var.init_expr, "The expression needs to be convertible to a boolean."); } + cast_no_check(last, type_bool, false); } if (cast_to_bool && expr_is_const_bool(init)) { *result = init->const_expr.b ? COND_TRUE : COND_FALSE; } - return true; } + if (cast_to_bool && last->expr_kind == EXPR_BINARY && last->binary_expr.operator >= BINARYOP_ASSIGN && !last->binary_expr.grouped) + { + if (vec_size(expr->cond_expr) > 1) + { + RETURN_SEMA_ERROR(last, "An assignment in the last conditional must be parenthesized - did you mean to use '==' instead?"); + } + else + { + RETURN_SEMA_ERROR(last, "An assignment in a conditional must have an extra parenthesis - did you mean to use '==' instead?"); + } + } // 3a. Check for optional in case of an expression. if (IS_OPTIONAL(last)) diff --git a/test/test_suite/clang/2002-07.c3t b/test/test_suite/clang/2002-07.c3t index a5bdd1789..0dfc4bba7 100644 --- a/test/test_suite/clang/2002-07.c3t +++ b/test/test_suite/clang/2002-07.c3t @@ -266,13 +266,13 @@ fn double mathFunc(double x, double y, double z, fn void strcpy(char *s1, char *s2) { - while (*s1++ = *s2++); + while ((*s1++ = *s2++)); } fn void strcat(char *s1, char *s2) { while (*s1++); s1--; - while (*s1++ = *s2++); + while ((*s1++ = *s2++)); } fn int strcmp(char *s1, char *s2) { diff --git a/test/test_suite/statements/if_assign.c3 b/test/test_suite/statements/if_assign.c3 new file mode 100644 index 000000000..a6aee2f27 --- /dev/null +++ b/test/test_suite/statements/if_assign.c3 @@ -0,0 +1,12 @@ +fn void main() +{ + int x = 5; + + if (x = 1) {} // #error: An assignment in a conditional must have an extra parenthesis + + if ((x = 1)) {} + + while (x = 1) {} // #error: An assignment in a conditional must have an extra parenthesis + + while (x = 1, x = 1) {} // #error: An assignment in the last conditional must be parenthesized +} \ No newline at end of file diff --git a/test/test_suite/statements/while_statement_placement2.c3 b/test/test_suite/statements/while_statement_placement2.c3 index c53d6086f..830bb9ff6 100644 --- a/test/test_suite/statements/while_statement_placement2.c3 +++ b/test/test_suite/statements/while_statement_placement2.c3 @@ -12,7 +12,7 @@ macro @thing(; @body()) } fn void strcpy(char *s1, char *s2) { - while (*s1++ = *s2++); + while ((*s1++ = *s2++)); } fn void main()