checker: require () in a && b || c

pull/5182/head
Alexander Medvednikov 2020-06-02 22:21:40 +02:00
parent 0a84f0feb5
commit 63b8cdea7a
6 changed files with 61 additions and 47 deletions

View File

@ -1,4 +1,6 @@
fn test_float_decl() { fn test_float_decl() {
//z := 1f
//assert z > 0
x1 := 1e10 x1 := 1e10
x2 := -2e16 x2 := -2e16
x3 := 1e-15 x3 := 1e-15

View File

@ -213,10 +213,10 @@ fn utf8_str_visible_length(s string) int {
} }
} else if c == 0xe1 || c == 0xe2 || c == 0xef { } else if c == 0xe1 || c == 0xe2 || c == 0xef {
r := (u32(c) << 16) | (u32(s.str[i+1]) << 8) | s.str[i+2] r := (u32(c) << 16) | (u32(s.str[i+1]) << 8) | s.str[i+2]
if r >= 0xe1aab0 && r < 0xe1ac80 // diacritical marks extended if (r >= 0xe1aab0 && r < 0xe1ac80) // diacritical marks extended
|| r >= 0xe1b780 && r < 0xe1b880 // diacritical marks supplement || (r >= 0xe1b780 && r < 0xe1b880) // diacritical marks supplement
|| r >= 0xe28390 && r < 0xe28480 // diacritical marks for symbols || (r >= 0xe28390 && r < 0xe28480) // diacritical marks for symbols
|| r >= 0xefb8a0 && r < 0xefb8b0 { // half marks || (r >= 0xefb8a0 && r < 0xefb8b0) { // half marks
l-- l--
} }
} }

View File

@ -419,9 +419,9 @@ pub fn (mut c Checker) infix_expr(mut infix_expr ast.InfixExpr) table.Type {
} }
c.expected_type = table.void_type c.expected_type = table.void_type
mut left_type := c.expr(infix_expr.left) mut left_type := c.expr(infix_expr.left)
if false && left_type == table.t_type { // if false && left_type == table.t_type {
left_type = c.cur_generic_type // left_type = c.cur_generic_type
} // }
infix_expr.left_type = left_type infix_expr.left_type = left_type
c.expected_type = left_type c.expected_type = left_type
mut right_type := c.expr(infix_expr.right) mut right_type := c.expr(infix_expr.right)
@ -501,8 +501,8 @@ pub fn (mut c Checker) infix_expr(mut infix_expr ast.InfixExpr) table.Type {
} }
} }
if infix_expr.op in [.div, .mod] { if infix_expr.op in [.div, .mod] {
if infix_expr.right is ast.IntegerLiteral && infix_expr.right.str() == if (infix_expr.right is ast.IntegerLiteral && infix_expr.right.str() ==
'0' || infix_expr.right is ast.FloatLiteral && infix_expr.right.str().f64() == 0.0 { '0') || (infix_expr.right is ast.FloatLiteral && infix_expr.right.str().f64() == 0.0) {
oper := if infix_expr.op == .div { 'division' } else { 'modulo' } oper := if infix_expr.op == .div { 'division' } else { 'modulo' }
c.error('$oper by zero', right_pos) c.error('$oper by zero', right_pos)
} }
@ -557,7 +557,18 @@ pub fn (mut c Checker) infix_expr(mut infix_expr ast.InfixExpr) table.Type {
} }
return table.bool_type return table.bool_type
} }
else {} else {
// use `()` to make the boolean expression clear error
// for example: `(a && b) || c` instead of `a && b || c`
if infix_expr.op in [.logical_or, .and] {
if infix_expr.left is ast.InfixExpr {
e := infix_expr.left as ast.InfixExpr
if e.op in [.logical_or, .and] && e.op != infix_expr.op {
c.error('use `()` to make the boolean expression clear', infix_expr.pos)
}
}
}
}
} }
// TODO: Absorb this block into the above single side check block to accelerate. // TODO: Absorb this block into the above single side check block to accelerate.
if left_type == table.bool_type && infix_expr.op !in [.eq, .ne, .logical_or, .and] { if left_type == table.bool_type && infix_expr.op !in [.eq, .ne, .logical_or, .and] {
@ -1789,8 +1800,8 @@ pub fn (mut c Checker) expr(node ast.Expr) table.Type {
ast.CastExpr { ast.CastExpr {
it.expr_type = c.expr(it.expr) it.expr_type = c.expr(it.expr)
sym := c.table.get_type_symbol(it.expr_type) sym := c.table.get_type_symbol(it.expr_type)
if it.typ == table.string_type && !(sym.kind in [.byte, .byteptr] || sym.kind == if it.typ == table.string_type && !(sym.kind in [.byte, .byteptr] || (sym.kind ==
.array && sym.name == 'array_byte') { .array && sym.name == 'array_byte')) {
type_name := c.table.type_to_str(it.expr_type) type_name := c.table.type_to_str(it.expr_type)
c.error('cannot cast type `$type_name` to string, use `x.str()` instead', it.pos) c.error('cannot cast type `$type_name` to string, use `x.str()` instead', it.pos)
} }

View File

@ -1915,28 +1915,31 @@ fn (mut g Gen) infix_expr(node ast.InfixExpr) {
g.write(',') g.write(',')
g.expr(node.right) g.expr(node.right)
g.write(')') g.write(')')
} else if node.op in [.plus, .minus, .mul, .div, .mod] && (left_sym.name[0].is_capital() ||
left_sym.name.contains('.')) && left_sym.kind != .alias || left_sym.kind == .alias && (left_sym.info as table.Alias).language ==
.c {
// !left_sym.is_number() {
g.write(g.typ(left_type))
g.write('_')
g.write(util.replace_op(node.op.str()))
g.write('(')
g.expr(node.left)
g.write(', ')
g.expr(node.right)
g.write(')')
} else { } else {
need_par := node.op in [.amp, .pipe, .xor] // `x & y == 0` => `(x & y) == 0` in C a := left_sym.name[0].is_capital() || left_sym.name.contains('.')
if need_par { b := left_sym.kind != .alias
c := left_sym.kind == .alias && (left_sym.info as table.Alias).language == .c
if node.op in [.plus, .minus, .mul, .div, .mod] && ((a && b) || c) {
// Overloaded operators
g.write(g.typ(left_type))
g.write('_')
g.write(util.replace_op(node.op.str()))
g.write('(') g.write('(')
} g.expr(node.left)
g.expr(node.left) g.write(', ')
g.write(' $node.op.str() ') g.expr(node.right)
g.expr(node.right)
if need_par {
g.write(')') g.write(')')
} else {
need_par := node.op in [.amp, .pipe, .xor] // `x & y == 0` => `(x & y) == 0` in C
if need_par {
g.write('(')
}
g.expr(node.left)
g.write(' $node.op.str() ')
g.expr(node.right)
if need_par {
g.write(')')
}
} }
} }
} }
@ -1966,7 +1969,7 @@ fn (mut g Gen) match_expr(node ast.MatchExpr) {
// mut sum_type_str = '' // mut sum_type_str = ''
for j, branch in node.branches { for j, branch in node.branches {
is_last := j == node.branches.len - 1 is_last := j == node.branches.len - 1
if branch.is_else || node.is_expr && is_last { if branch.is_else || (node.is_expr && is_last) {
if node.branches.len > 1 { if node.branches.len > 1 {
if is_expr { if is_expr {
// TODO too many branches. maybe separate ?: matches // TODO too many branches. maybe separate ?: matches
@ -2980,6 +2983,7 @@ fn (mut g Gen) string_inter_literal(node ast.StringInterLiteral) {
fields := fmt.split('.') fields := fmt.split('.')
// validate format // validate format
// only floats should have precision specifier // only floats should have precision specifier
/*
if fields.len > 2 || fields.len == 2 && !(node.expr_types[i].is_float()) || node.expr_types[i].is_signed() && if fields.len > 2 || fields.len == 2 && !(node.expr_types[i].is_float()) || node.expr_types[i].is_signed() &&
fspec !in [`d`, `c`, `x`, `X`, `o`] || node.expr_types[i].is_unsigned() && fspec !in [`u`, `x`, fspec !in [`d`, `c`, `x`, `X`, `o`] || node.expr_types[i].is_unsigned() && fspec !in [`u`, `x`,
`X`, `o`, `c`] || node.expr_types[i].is_any_int() && fspec !in [`d`, `c`, `x`, `X`, `X`, `o`, `c`] || node.expr_types[i].is_any_int() && fspec !in [`d`, `c`, `x`, `X`,
@ -2988,7 +2992,9 @@ fn (mut g Gen) string_inter_literal(node ast.StringInterLiteral) {
`f`, `g`] || node.expr_types[i].is_pointer() && fspec !in [`p`, `x`, `X`] { `f`, `g`] || node.expr_types[i].is_pointer() && fspec !in [`p`, `x`, `X`] {
verror('illegal format specifier ${fspec:c} for type ${g.table.get_type_name(node.expr_types[i])}') verror('illegal format specifier ${fspec:c} for type ${g.table.get_type_name(node.expr_types[i])}')
} }
*/
// make sure that format paramters are valid numbers // make sure that format paramters are valid numbers
/*
for j, f in fields { for j, f in fields {
for k, c in f { for k, c in f {
if (c < `0` || c > `9`) && !(j == 0 && k == 0 && (node.expr_types[i].is_number() && if (c < `0` || c > `9`) && !(j == 0 && k == 0 && (node.expr_types[i].is_number() &&
@ -2997,6 +3003,7 @@ fn (mut g Gen) string_inter_literal(node ast.StringInterLiteral) {
} }
} }
} }
*/
specs << fspec specs << fspec
fieldwidths << if fields.len == 0 { fieldwidths << if fields.len == 0 {
0 0
@ -3466,7 +3473,7 @@ fn (mut g Gen) comp_if_to_ifdef(name string, is_comptime_optional bool) string {
return 'TARGET_ORDER_IS_BIG' return 'TARGET_ORDER_IS_BIG'
} }
else { else {
if is_comptime_optional || g.pref.compile_defines_all.len > 0 && name in g.pref.compile_defines_all { if is_comptime_optional || (g.pref.compile_defines_all.len > 0 && name in g.pref.compile_defines_all) {
return 'CUSTOM_DEFINE_${name}' return 'CUSTOM_DEFINE_${name}'
} }
verror('bad os ifdef name "$name"') verror('bad os ifdef name "$name"')
@ -4009,7 +4016,7 @@ fn (mut g Gen) gen_str_for_array(info table.Array, styp, str_fn_name string) {
// There is a custom .str() method, so use it. // There is a custom .str() method, so use it.
// NB: we need to take account of whether the user has defined // NB: we need to take account of whether the user has defined
// `fn (x T) str() {` or `fn (x &T) str() {`, and convert accordingly // `fn (x T) str() {` or `fn (x &T) str() {`, and convert accordingly
if str_method_expects_ptr && is_elem_ptr || !str_method_expects_ptr && !is_elem_ptr { if (str_method_expects_ptr && is_elem_ptr) || (!str_method_expects_ptr && !is_elem_ptr) {
g.auto_str_funcs.writeln('\t\tstring x = ${elem_str_fn_name}(it);') g.auto_str_funcs.writeln('\t\tstring x = ${elem_str_fn_name}(it);')
} else if str_method_expects_ptr && !is_elem_ptr { } else if str_method_expects_ptr && !is_elem_ptr {
g.auto_str_funcs.writeln('\t\tstring x = ${elem_str_fn_name}(&it);') g.auto_str_funcs.writeln('\t\tstring x = ${elem_str_fn_name}(&it);')
@ -4064,7 +4071,7 @@ fn (mut g Gen) gen_str_for_array_fixed(info table.ArrayFixed, styp, str_fn_name
} else if sym.kind == .string { } else if sym.kind == .string {
g.auto_str_funcs.writeln('\t\tstrings__Builder_write(&sb, _STR("\'%.*s\\000\'", 2, a[i]));') g.auto_str_funcs.writeln('\t\tstrings__Builder_write(&sb, _STR("\'%.*s\\000\'", 2, a[i]));')
} else { } else {
if str_method_expects_ptr && is_elem_ptr || !str_method_expects_ptr && !is_elem_ptr { if (str_method_expects_ptr && is_elem_ptr) || (!str_method_expects_ptr && !is_elem_ptr) {
g.auto_str_funcs.writeln('\t\tstrings__Builder_write(&sb, ${elem_str_fn_name}(a[i]));') g.auto_str_funcs.writeln('\t\tstrings__Builder_write(&sb, ${elem_str_fn_name}(a[i]));')
} else if str_method_expects_ptr && !is_elem_ptr { } else if str_method_expects_ptr && !is_elem_ptr {
g.auto_str_funcs.writeln('\t\tstrings__Builder_write(&sb, ${elem_str_fn_name}(&a[i]));') g.auto_str_funcs.writeln('\t\tstrings__Builder_write(&sb, ${elem_str_fn_name}(&a[i]));')

View File

@ -30,7 +30,8 @@ fn test_if_expression_with_stmts() {
assert b == 24 assert b == 24
} }
fn noop() {} fn noop() {
}
fn test_if_expression_with_function_assign() { fn test_if_expression_with_function_assign() {
a := if true { a := if true {
@ -102,7 +103,7 @@ fn test_simple_nested_if_expressions() {
fn test_complex_nested_if_expressions() { fn test_complex_nested_if_expressions() {
mut a := false mut a := false
a = 1 == 2 || true && if true { a = (1 == 2 || true) && (if true {
g := 6 g := 6
h := if false { 3 } else { 5 } h := if false { 3 } else { 5 }
mut d := false mut d := false
@ -125,7 +126,7 @@ fn test_complex_nested_if_expressions() {
} else { } else {
false false
} }
} })
assert a == false assert a == false
} }

View File

@ -373,13 +373,9 @@ pub fn (tok Token) precedence() int {
.eq, .ne, .lt, .le, .gt, .ge { .eq, .ne, .lt, .le, .gt, .ge {
return int(Precedence.eq) return int(Precedence.eq)
} }
// `&&`
// .and {
// return 3
// }
// `||`
// .logical_or, // .logical_or,
.assign, .plus_assign, .minus_assign, .div_assign, .mod_assign, .or_assign, .and_assign, .assign, .plus_assign, .minus_assign, .div_assign, .mod_assign,
.or_assign, .and_assign,
// //
.left_shift_assign, .right_shift_assign, .mult_assign, .xor_assign { .left_shift_assign, .right_shift_assign, .mult_assign, .xor_assign {
return int(Precedence.assign) return int(Precedence.assign)
@ -390,9 +386,6 @@ pub fn (tok Token) precedence() int {
.logical_or, .and { .logical_or, .and {
return int(Precedence.cond) return int(Precedence.cond)
} }
// /.plus_assign {
// /return 2
// /}
else { else {
return int(Precedence.lowest) return int(Precedence.lowest)
} }