From 9760a9c6b258017ed728d323cbcd3f7016c40c3b Mon Sep 17 00:00:00 2001 From: kbkpbot Date: Mon, 10 Nov 2025 01:21:13 +0800 Subject: [PATCH] checker,transformer: add always true/false branch detection for the `if` and `match` constructs (#25674) --- cmd/tools/changelog_helper.v | 7 - vlib/os/filepath.v | 14 +- vlib/os/os.v | 4 +- vlib/v/checker/checker.v | 14 +- vlib/v/checker/if.v | 11 + vlib/v/checker/match.v | 19 + .../tests/always_true_false_branch.out | 469 ++++++++++++++++++ .../checker/tests/always_true_false_branch.vv | 204 ++++++++ .../v/checker/tests/assign_multi_mismatch.out | 39 ++ .../v/checker/tests/cast_to_interface_err.out | 7 + .../tests/function_missing_return_type.out | 7 + .../tests/if_diff_expected_type_err.out | 7 + vlib/v/checker/tests/if_expr_last_stmt.out | 13 + vlib/v/checker/tests/if_expr_mismatch.out | 6 + vlib/v/checker/tests/if_expr_no_else.out | 6 + .../checker/tests/if_expr_with_none_only.out | 5 + vlib/v/checker/tests/if_match_result.out | 28 ++ vlib/v/checker/tests/if_mismatch_muls_err.out | 7 + .../immutable_array_in_if_expr_index.out | 7 + .../tests/immutable_to_mutable_err.out | 14 + vlib/v/checker/tests/match_else_last_expr.out | 16 +- .../checker/tests/match_expr_empty_branch.out | 12 + .../tests/match_expr_with_none_only.out | 7 + vlib/v/checker/tests/match_missing.out | 6 + .../tests/match_return_mismatch_type_err.out | 7 + vlib/v/checker/tests/none_match_cond_err.out | 6 + .../tests/option_interface_mismatch.out | 10 +- .../tests/return_missing_comp_if_nested.out | 6 +- .../tests/return_missing_comp_if_nested.vv | 6 +- .../tests/return_missing_if_else_simple.out | 20 + .../checker/tests/return_missing_if_match.out | 25 +- .../checker/tests/return_missing_match_if.out | 21 + .../v/checker/tests/return_missing_nested.out | 18 +- .../v/checker/tests/return_missing_simple.out | 11 +- .../tests/return_working_comp_if_nested.vv | 6 +- .../checker/tests/return_working_if_match.out | 21 + .../checker/tests/return_working_match_if.out | 21 + .../v/checker/tests/return_working_nested.out | 14 + .../v/checker/tests/return_working_simple.out | 7 + .../v/checker/tests/return_working_two_if.out | 21 + .../v/checker/tests/shift_ops_expressions.out | 56 +++ vlib/v/checker/tests/struct_scope_err.out | 7 + .../checker/tests/unnecessary_parenthesis.out | 20 + vlib/v/checker/tests/unreachable_code.out | 7 + vlib/v/gen/js/tests/testdata/compare_ints.v | 10 +- vlib/v/gen/js/tests/testdata/match.v | 3 +- .../repl/conditional_blocks/if.repl | 4 +- .../repl/conditional_blocks/if_else.repl | 4 +- .../skip_unused/generic_call_from_json.vv | 4 +- vlib/v/transformer/transformer.v | 124 ++++- 50 files changed, 1340 insertions(+), 48 deletions(-) create mode 100644 vlib/v/checker/tests/always_true_false_branch.out create mode 100644 vlib/v/checker/tests/always_true_false_branch.vv diff --git a/cmd/tools/changelog_helper.v b/cmd/tools/changelog_helper.v index fdb1621a9..d8af93609 100644 --- a/cmd/tools/changelog_helper.v +++ b/cmd/tools/changelog_helper.v @@ -111,9 +111,6 @@ fn main() { } } os.write_file(log_txt, lines.join('\n'))! - if true { - // return - } mut app := &App{ total_lines: lines.len } @@ -122,10 +119,6 @@ fn main() { category_titles) os.write_file('CHANGELOG.md', app.result)! changelog_txt := os.read_file('CHANGELOG.md')!.to_lower() - if true { - // println(changelog_txt) - // return - } // mut counter := 0 // to display how many commits are left for line in lines { s := line.trim_space() diff --git a/vlib/os/filepath.v b/vlib/os/filepath.v index b68ae2b19..6ca73f41b 100644 --- a/vlib/os/filepath.v +++ b/vlib/os/filepath.v @@ -218,18 +218,20 @@ fn clean_path(path string) string { // to_slash returns the result of replacing each separator character in path with a slash (`/`). pub fn to_slash(path string) string { - if path_separator == '/' { - return path + return $if windows { + path.replace(path_separator, '/') + } $else { + path } - return path.replace(path_separator, '/') } // from_slash returns the result of replacing each slash (`/`) character is path with a separator character. pub fn from_slash(path string) string { - if path_separator == '/' { - return path + return $if windows { + path.replace('/', path_separator) + } $else { + path } - return path.replace('/', path_separator) } // win_volume_len returns the length of the diff --git a/vlib/os/os.v b/vlib/os/os.v index 058e68ea2..948e64c70 100644 --- a/vlib/os/os.v +++ b/vlib/os/os.v @@ -44,7 +44,7 @@ fn executable_fallback() string { } } if !is_abs_path(exepath) { - other_separator := if path_separator == '/' { '\\' } else { '/' } + other_separator := $if windows { '/' } $else { '\\' } rexepath := exepath.replace(other_separator, path_separator) if rexepath.contains(path_separator) { exepath = join_path_single(wd_at_startup, exepath) @@ -848,7 +848,7 @@ pub fn mkdir_all(opath string, params MkdirParams) ! { } return error('path `${opath}` already exists, and is not a folder') } - other_separator := if path_separator == '/' { '\\' } else { '/' } + other_separator := $if windows { '/' } $else { '\\' } path := opath.replace(other_separator, path_separator) mut p := if path.starts_with(path_separator) { path_separator } else { '' } path_parts := path.trim_left(path_separator).split(path_separator) diff --git a/vlib/v/checker/checker.v b/vlib/v/checker/checker.v index 2ba73ca2f..953c76898 100644 --- a/vlib/v/checker/checker.v +++ b/vlib/v/checker/checker.v @@ -152,7 +152,8 @@ mut: v_current_commit_hash string // same as old C.V_CURRENT_COMMIT_HASH assign_stmt_attr string // for `x := [1,2,3] @[freed]` - js_string ast.Type = ast.void_type // when `js"string literal"` is used, `js_string` will be equal to `JS.String` + js_string ast.Type = ast.void_type // when `js"string literal"` is used, `js_string` will be equal to `JS.String` + checker_transformer &transformer.Transformer = unsafe { nil } } pub fn new_checker(table &ast.Table, pref_ &pref.Preferences) &Checker { @@ -174,6 +175,7 @@ pub fn new_checker(table &ast.Table, pref_ &pref.Preferences) &Checker { ) match_exhaustive_cutoff_limit: pref_.checker_match_exhaustive_cutoff_limit v_current_commit_hash: v_current_commit_hash + checker_transformer: transformer.new_transformer_with_table(table, pref_) } checker.type_resolver = type_resolver.TypeResolver.new(table, checker) checker.comptime = &checker.type_resolver.info @@ -2109,8 +2111,7 @@ fn (mut c Checker) enum_decl(mut node ast.EnumDecl) { ast.InfixExpr { // Handle `enum Foo { x = 1 + 2 }` c.infix_expr(mut field.expr) - mut t := transformer.new_transformer_with_table(c.table, c.pref) - folded_expr := t.infix_expr(mut field.expr) + folded_expr := c.checker_transformer.infix_expr(mut field.expr) if folded_expr is ast.IntegerLiteral { c.check_enum_field_integer_literal(folded_expr, signed, node.is_multi_allowed, @@ -2120,8 +2121,7 @@ fn (mut c Checker) enum_decl(mut node ast.EnumDecl) { } ast.ParExpr { c.expr(mut field.expr.expr) - mut t := transformer.new_transformer_with_table(c.table, c.pref) - folded_expr := t.expr(mut field.expr.expr) + folded_expr := c.checker_transformer.expr(mut field.expr.expr) if folded_expr is ast.IntegerLiteral { c.check_enum_field_integer_literal(folded_expr, signed, node.is_multi_allowed, @@ -2160,9 +2160,7 @@ fn (mut c Checker) enum_decl(mut node ast.EnumDecl) { if field.expr.kind == .constant && field.expr.obj.typ.is_int() { // accepts int constants as enum value if mut field.expr.obj is ast.ConstField { - mut t := transformer.new_transformer_with_table(c.table, - c.pref) - folded_expr := t.expr(mut field.expr.obj.expr) + folded_expr := c.checker_transformer.expr(mut field.expr.obj.expr) if folded_expr is ast.IntegerLiteral { c.check_enum_field_integer_literal(folded_expr, signed, diff --git a/vlib/v/checker/if.v b/vlib/v/checker/if.v index b81af146a..3986e1859 100644 --- a/vlib/v/checker/if.v +++ b/vlib/v/checker/if.v @@ -156,6 +156,17 @@ fn (mut c Checker) if_expr(mut node ast.IfExpr) ast.Type { c.error('non-bool type `${c.table.type_to_str(cond_typ)}` used as if condition', branch.cond.pos()) } + if !c.pref.translated && !c.file.is_translated { + mut check_expr := branch.cond + t_expr := c.checker_transformer.expr(mut check_expr) + if t_expr is ast.BoolLiteral { + if t_expr.val { + c.note('condition is always true', branch.cond.pos()) + } else { + c.note('condition is always false', branch.cond.pos()) + } + } + } } } else { // else branch diff --git a/vlib/v/checker/match.v b/vlib/v/checker/match.v index bdc82ecc3..ced8201e8 100644 --- a/vlib/v/checker/match.v +++ b/vlib/v/checker/match.v @@ -235,6 +235,25 @@ fn (mut c Checker) match_expr(mut node ast.MatchExpr) ast.Type { } } + if !c.pref.translated && !c.file.is_translated { + // check for always true/false match branch + for mut expr in branch.exprs { + mut check_expr := ast.InfixExpr{ + op: .eq + left: node.cond + right: expr + } + t_expr := c.checker_transformer.expr(mut check_expr) + if t_expr is ast.BoolLiteral { + if t_expr.val { + c.note('match is always true', expr.pos()) + } else { + c.note('match is always false', expr.pos()) + } + } + } + } + if !node.is_comptime || (node.is_comptime && comptime_match_branch_result) { if node.is_expr { c.stmts_ending_with_expression(mut branch.stmts, c.expected_or_type) diff --git a/vlib/v/checker/tests/always_true_false_branch.out b/vlib/v/checker/tests/always_true_false_branch.out new file mode 100644 index 000000000..05e0fa7b5 --- /dev/null +++ b/vlib/v/checker/tests/always_true_false_branch.out @@ -0,0 +1,469 @@ +vlib/v/checker/tests/always_true_false_branch.vv:5:23: notice: condition is always true + 3 | fn main() { + 4 | x := 1 + 5 | if_always_true := if x == x { + | ~~~~~~ + 6 | 1 + 7 | } else if true { +vlib/v/checker/tests/always_true_false_branch.vv:7:12: notice: condition is always true + 5 | if_always_true := if x == x { + 6 | 1 + 7 | } else if true { + | ~~~~ + 8 | 1 + 9 | } else if !false { +vlib/v/checker/tests/always_true_false_branch.vv:11:12: notice: condition is always true + 9 | } else if !false { + 10 | 1 + 11 | } else if true == true { + | ~~~~~~~~~~~~ + 12 | 1 + 13 | } else if true != false { +vlib/v/checker/tests/always_true_false_branch.vv:13:12: notice: condition is always true + 11 | } else if true == true { + 12 | 1 + 13 | } else if true != false { + | ~~~~~~~~~~~~~ + 14 | 1 + 15 | } else if false == false { +vlib/v/checker/tests/always_true_false_branch.vv:15:12: notice: condition is always true + 13 | } else if true != false { + 14 | 1 + 15 | } else if false == false { + | ~~~~~~~~~~~~~~ + 16 | 1 + 17 | } else if false != true { +vlib/v/checker/tests/always_true_false_branch.vv:17:12: notice: condition is always true + 15 | } else if false == false { + 16 | 1 + 17 | } else if false != true { + | ~~~~~~~~~~~~~ + 18 | 1 + 19 | } else if 'a' == 'a' { +vlib/v/checker/tests/always_true_false_branch.vv:19:12: notice: condition is always true + 17 | } else if false != true { + 18 | 1 + 19 | } else if 'a' == 'a' { + | ~~~~~~~~~~ + 20 | 1 + 21 | } else if 'a' != 'b' { +vlib/v/checker/tests/always_true_false_branch.vv:21:12: notice: condition is always true + 19 | } else if 'a' == 'a' { + 20 | 1 + 21 | } else if 'a' != 'b' { + | ~~~~~~~~~~ + 22 | 1 + 23 | } else if 1 == 1 { +vlib/v/checker/tests/always_true_false_branch.vv:23:12: notice: condition is always true + 21 | } else if 'a' != 'b' { + 22 | 1 + 23 | } else if 1 == 1 { + | ~~~~~~ + 24 | 1 + 25 | } else if 1 != 2 { +vlib/v/checker/tests/always_true_false_branch.vv:25:12: notice: condition is always true + 23 | } else if 1 == 1 { + 24 | 1 + 25 | } else if 1 != 2 { + | ~~~~~~ + 26 | 1 + 27 | } else if 1 >= 1 { +vlib/v/checker/tests/always_true_false_branch.vv:27:12: notice: condition is always true + 25 | } else if 1 != 2 { + 26 | 1 + 27 | } else if 1 >= 1 { + | ~~~~~~ + 28 | 1 + 29 | } else if 1 <= 1 { +vlib/v/checker/tests/always_true_false_branch.vv:29:12: notice: condition is always true + 27 | } else if 1 >= 1 { + 28 | 1 + 29 | } else if 1 <= 1 { + | ~~~~~~ + 30 | 1 + 31 | } else if 1 < 2 { +vlib/v/checker/tests/always_true_false_branch.vv:31:12: notice: condition is always true + 29 | } else if 1 <= 1 { + 30 | 1 + 31 | } else if 1 < 2 { + | ~~~~~ + 32 | 1 + 33 | } else if 2 > 1 { +vlib/v/checker/tests/always_true_false_branch.vv:33:12: notice: condition is always true + 31 | } else if 1 < 2 { + 32 | 1 + 33 | } else if 2 > 1 { + | ~~~~~ + 34 | 1 + 35 | } else if 1.5 == 1.5 { +vlib/v/checker/tests/always_true_false_branch.vv:35:12: notice: condition is always true + 33 | } else if 2 > 1 { + 34 | 1 + 35 | } else if 1.5 == 1.5 { + | ~~~~~~~~~~ + 36 | 1 + 37 | } else if 1.5 != 2.5 { +vlib/v/checker/tests/always_true_false_branch.vv:37:12: notice: condition is always true + 35 | } else if 1.5 == 1.5 { + 36 | 1 + 37 | } else if 1.5 != 2.5 { + | ~~~~~~~~~~ + 38 | 1 + 39 | } else if 1.5 >= 1.5 { +vlib/v/checker/tests/always_true_false_branch.vv:39:12: notice: condition is always true + 37 | } else if 1.5 != 2.5 { + 38 | 1 + 39 | } else if 1.5 >= 1.5 { + | ~~~~~~~~~~ + 40 | 1 + 41 | } else if 1.5 <= 1.5 { +vlib/v/checker/tests/always_true_false_branch.vv:41:12: notice: condition is always true + 39 | } else if 1.5 >= 1.5 { + 40 | 1 + 41 | } else if 1.5 <= 1.5 { + | ~~~~~~~~~~ + 42 | 1 + 43 | } else if 1.5 < 2.5 { +vlib/v/checker/tests/always_true_false_branch.vv:43:12: notice: condition is always true + 41 | } else if 1.5 <= 1.5 { + 42 | 1 + 43 | } else if 1.5 < 2.5 { + | ~~~~~~~~~ + 44 | 1 + 45 | } else if 2.5 > 1.5 { +vlib/v/checker/tests/always_true_false_branch.vv:45:12: notice: condition is always true + 43 | } else if 1.5 < 2.5 { + 44 | 1 + 45 | } else if 2.5 > 1.5 { + | ~~~~~~~~~ + 46 | 1 + 47 | } else if `1` == `1` { +vlib/v/checker/tests/always_true_false_branch.vv:47:12: notice: condition is always true + 45 | } else if 2.5 > 1.5 { + 46 | 1 + 47 | } else if `1` == `1` { + | ~~~~~~~~~~ + 48 | 1 + 49 | } else if `1` != `2` { +vlib/v/checker/tests/always_true_false_branch.vv:49:12: notice: condition is always true + 47 | } else if `1` == `1` { + 48 | 1 + 49 | } else if `1` != `2` { + | ~~~~~~~~~~ + 50 | 1 + 51 | } else if `1` >= `1` { +vlib/v/checker/tests/always_true_false_branch.vv:51:12: notice: condition is always true + 49 | } else if `1` != `2` { + 50 | 1 + 51 | } else if `1` >= `1` { + | ~~~~~~~~~~ + 52 | 1 + 53 | } else if `1` <= `1` { +vlib/v/checker/tests/always_true_false_branch.vv:53:12: notice: condition is always true + 51 | } else if `1` >= `1` { + 52 | 1 + 53 | } else if `1` <= `1` { + | ~~~~~~~~~~ + 54 | 1 + 55 | } else if `1` < `2` { +vlib/v/checker/tests/always_true_false_branch.vv:55:12: notice: condition is always true + 53 | } else if `1` <= `1` { + 54 | 1 + 55 | } else if `1` < `2` { + | ~~~~~~~~~ + 56 | 1 + 57 | } else if `2` > `1` { +vlib/v/checker/tests/always_true_false_branch.vv:57:12: notice: condition is always true + 55 | } else if `1` < `2` { + 56 | 1 + 57 | } else if `2` > `1` { + | ~~~~~~~~~ + 58 | 1 + 59 | } else if 1 == 1 && 2 <= 2 && 3 >= 3 && 4 != 5 { +vlib/v/checker/tests/always_true_false_branch.vv:59:1: notice: condition is always true + 57 | } else if `2` > `1` { + 58 | 1 + 59 | } else if 1 == 1 && 2 <= 2 && 3 >= 3 && 4 != 5 { + | ^ + 60 | 1 + 61 | } else if 1 == 1 || 4 > 5 || 6 < 3 || 5 != 5 { +vlib/v/checker/tests/always_true_false_branch.vv:61:1: notice: condition is always true + 59 | } else if 1 == 1 && 2 <= 2 && 3 >= 3 && 4 != 5 { + 60 | 1 + 61 | } else if 1 == 1 || 4 > 5 || 6 < 3 || 5 != 5 { + | ^ + 62 | 1 + 63 | } else { +vlib/v/checker/tests/always_true_false_branch.vv:69:24: notice: condition is always false + 67 | dump(if_always_true) + 68 | + 69 | if_always_false := if x != x { + | ~~~~~~ + 70 | 1 + 71 | } else if false { +vlib/v/checker/tests/always_true_false_branch.vv:71:12: notice: condition is always false + 69 | if_always_false := if x != x { + 70 | 1 + 71 | } else if false { + | ~~~~~ + 72 | 1 + 73 | } else if !true { +vlib/v/checker/tests/always_true_false_branch.vv:75:12: notice: condition is always false + 73 | } else if !true { + 74 | 1 + 75 | } else if true != true { + | ~~~~~~~~~~~~ + 76 | 1 + 77 | } else if true == false { +vlib/v/checker/tests/always_true_false_branch.vv:77:12: notice: condition is always false + 75 | } else if true != true { + 76 | 1 + 77 | } else if true == false { + | ~~~~~~~~~~~~~ + 78 | 1 + 79 | } else if false != false { +vlib/v/checker/tests/always_true_false_branch.vv:79:12: notice: condition is always false + 77 | } else if true == false { + 78 | 1 + 79 | } else if false != false { + | ~~~~~~~~~~~~~~ + 80 | 1 + 81 | } else if false == true { +vlib/v/checker/tests/always_true_false_branch.vv:81:12: notice: condition is always false + 79 | } else if false != false { + 80 | 1 + 81 | } else if false == true { + | ~~~~~~~~~~~~~ + 82 | 1 + 83 | } else if 'a' != 'a' { +vlib/v/checker/tests/always_true_false_branch.vv:83:12: notice: condition is always false + 81 | } else if false == true { + 82 | 1 + 83 | } else if 'a' != 'a' { + | ~~~~~~~~~~ + 84 | 1 + 85 | } else if 'a' == 'b' { +vlib/v/checker/tests/always_true_false_branch.vv:85:12: notice: condition is always false + 83 | } else if 'a' != 'a' { + 84 | 1 + 85 | } else if 'a' == 'b' { + | ~~~~~~~~~~ + 86 | 1 + 87 | } else if 1 != 1 { +vlib/v/checker/tests/always_true_false_branch.vv:87:12: notice: condition is always false + 85 | } else if 'a' == 'b' { + 86 | 1 + 87 | } else if 1 != 1 { + | ~~~~~~ + 88 | 1 + 89 | } else if 1 == 2 { +vlib/v/checker/tests/always_true_false_branch.vv:89:12: notice: condition is always false + 87 | } else if 1 != 1 { + 88 | 1 + 89 | } else if 1 == 2 { + | ~~~~~~ + 90 | 1 + 91 | } else if 1 > 1 { +vlib/v/checker/tests/always_true_false_branch.vv:91:12: notice: condition is always false + 89 | } else if 1 == 2 { + 90 | 1 + 91 | } else if 1 > 1 { + | ~~~~~ + 92 | 1 + 93 | } else if 1 < 1 { +vlib/v/checker/tests/always_true_false_branch.vv:93:12: notice: condition is always false + 91 | } else if 1 > 1 { + 92 | 1 + 93 | } else if 1 < 1 { + | ~~~~~ + 94 | 1 + 95 | } else if 1 >= 2 { +vlib/v/checker/tests/always_true_false_branch.vv:95:12: notice: condition is always false + 93 | } else if 1 < 1 { + 94 | 1 + 95 | } else if 1 >= 2 { + | ~~~~~~ + 96 | 1 + 97 | } else if 2 <= 1 { +vlib/v/checker/tests/always_true_false_branch.vv:97:12: notice: condition is always false + 95 | } else if 1 >= 2 { + 96 | 1 + 97 | } else if 2 <= 1 { + | ~~~~~~ + 98 | 1 + 99 | } else if 1.5 != 1.5 { +vlib/v/checker/tests/always_true_false_branch.vv:99:12: notice: condition is always false + 97 | } else if 2 <= 1 { + 98 | 1 + 99 | } else if 1.5 != 1.5 { + | ~~~~~~~~~~ + 100 | 1 + 101 | } else if 1.5 == 2.5 { +vlib/v/checker/tests/always_true_false_branch.vv:101:12: notice: condition is always false + 99 | } else if 1.5 != 1.5 { + 100 | 1 + 101 | } else if 1.5 == 2.5 { + | ~~~~~~~~~~ + 102 | 1 + 103 | } else if 1.5 > 1.5 { +vlib/v/checker/tests/always_true_false_branch.vv:103:12: notice: condition is always false + 101 | } else if 1.5 == 2.5 { + 102 | 1 + 103 | } else if 1.5 > 1.5 { + | ~~~~~~~~~ + 104 | 1 + 105 | } else if 1.5 < 1.5 { +vlib/v/checker/tests/always_true_false_branch.vv:105:12: notice: condition is always false + 103 | } else if 1.5 > 1.5 { + 104 | 1 + 105 | } else if 1.5 < 1.5 { + | ~~~~~~~~~ + 106 | 1 + 107 | } else if 1.5 >= 2.5 { +vlib/v/checker/tests/always_true_false_branch.vv:107:12: notice: condition is always false + 105 | } else if 1.5 < 1.5 { + 106 | 1 + 107 | } else if 1.5 >= 2.5 { + | ~~~~~~~~~~ + 108 | 1 + 109 | } else if 2.5 <= 1.5 { +vlib/v/checker/tests/always_true_false_branch.vv:109:12: notice: condition is always false + 107 | } else if 1.5 >= 2.5 { + 108 | 1 + 109 | } else if 2.5 <= 1.5 { + | ~~~~~~~~~~ + 110 | 1 + 111 | } else if `1` != `1` { +vlib/v/checker/tests/always_true_false_branch.vv:111:12: notice: condition is always false + 109 | } else if 2.5 <= 1.5 { + 110 | 1 + 111 | } else if `1` != `1` { + | ~~~~~~~~~~ + 112 | 1 + 113 | } else if `1` == `2` { +vlib/v/checker/tests/always_true_false_branch.vv:113:12: notice: condition is always false + 111 | } else if `1` != `1` { + 112 | 1 + 113 | } else if `1` == `2` { + | ~~~~~~~~~~ + 114 | 1 + 115 | } else if `1` < `1` { +vlib/v/checker/tests/always_true_false_branch.vv:115:12: notice: condition is always false + 113 | } else if `1` == `2` { + 114 | 1 + 115 | } else if `1` < `1` { + | ~~~~~~~~~ + 116 | 1 + 117 | } else if `1` > `1` { +vlib/v/checker/tests/always_true_false_branch.vv:117:12: notice: condition is always false + 115 | } else if `1` < `1` { + 116 | 1 + 117 | } else if `1` > `1` { + | ~~~~~~~~~ + 118 | 1 + 119 | } else if `1` >= `2` { +vlib/v/checker/tests/always_true_false_branch.vv:119:12: notice: condition is always false + 117 | } else if `1` > `1` { + 118 | 1 + 119 | } else if `1` >= `2` { + | ~~~~~~~~~~ + 120 | 1 + 121 | } else if `2` <= `1` { +vlib/v/checker/tests/always_true_false_branch.vv:121:12: notice: condition is always false + 119 | } else if `1` >= `2` { + 120 | 1 + 121 | } else if `2` <= `1` { + | ~~~~~~~~~~ + 122 | 1 + 123 | } else if 1 == 2 && 3 >= 3 && 4 <= 5 && 5 != 5 { +vlib/v/checker/tests/always_true_false_branch.vv:123:1: notice: condition is always false + 121 | } else if `2` <= `1` { + 122 | 1 + 123 | } else if 1 == 2 && 3 >= 3 && 4 <= 5 && 5 != 5 { + | ^ + 124 | 1 + 125 | } else if 1 == 2 || 2 > 3 || 5 < 4 { +vlib/v/checker/tests/always_true_false_branch.vv:125:1: notice: condition is always false + 123 | } else if 1 == 2 && 3 >= 3 && 4 <= 5 && 5 != 5 { + 124 | 1 + 125 | } else if 1 == 2 || 2 > 3 || 5 < 4 { + | ^ + 126 | 0 + 127 | } else { +vlib/v/checker/tests/always_true_false_branch.vv:134:3: notice: match is always true + 132 | + 133 | match_always_true_var := match x { + 134 | x { + | ^ + 135 | 'haha' + 136 | } +vlib/v/checker/tests/always_true_false_branch.vv:144:3: notice: match is always true + 142 | + 143 | match_always_true_false_bool := match true { + 144 | true { + | ~~~~ + 145 | 'haha' + 146 | } +vlib/v/checker/tests/always_true_false_branch.vv:147:3: notice: match is always false + 145 | 'haha' + 146 | } + 147 | false { + | ~~~~~ + 148 | 'cc' + 149 | } +vlib/v/checker/tests/always_true_false_branch.vv:154:3: notice: match is always true + 152 | + 153 | match_always_true_false_int := match 1 { + 154 | 1 { + | ^ + 155 | 'haha' + 156 | } +vlib/v/checker/tests/always_true_false_branch.vv:157:3: notice: match is always false + 155 | 'haha' + 156 | } + 157 | 2 { + | ^ + 158 | 'cc' + 159 | } +vlib/v/checker/tests/always_true_false_branch.vv:167:3: notice: match is always true + 165 | + 166 | match_always_true_false_f64 := match 1.5 { + 167 | 1.5 { + | ~~~ + 168 | 'haha' + 169 | } +vlib/v/checker/tests/always_true_false_branch.vv:170:3: notice: match is always false + 168 | 'haha' + 169 | } + 170 | 2.5 { + | ~~~ + 171 | 'cc' + 172 | } +vlib/v/checker/tests/always_true_false_branch.vv:180:3: notice: match is always true + 178 | + 179 | match_always_true_false_string := match 'a' { + 180 | 'a' { + | ~~~ + 181 | 'haha' + 182 | } +vlib/v/checker/tests/always_true_false_branch.vv:183:3: notice: match is always false + 181 | 'haha' + 182 | } + 183 | 'b' { + | ~~~ + 184 | 'cc' + 185 | } +vlib/v/checker/tests/always_true_false_branch.vv:193:3: notice: match is always true + 191 | + 192 | match_always_true_false_rune := match `a` { + 193 | `a` { + | ~~~ + 194 | 'haha' + 195 | } +vlib/v/checker/tests/always_true_false_branch.vv:196:3: notice: match is always false + 194 | 'haha' + 195 | } + 196 | `b` { + | ~~~ + 197 | 'cc' + 198 | } diff --git a/vlib/v/checker/tests/always_true_false_branch.vv b/vlib/v/checker/tests/always_true_false_branch.vv new file mode 100644 index 000000000..05c4e8477 --- /dev/null +++ b/vlib/v/checker/tests/always_true_false_branch.vv @@ -0,0 +1,204 @@ +module main + +fn main() { + x := 1 + if_always_true := if x == x { + 1 + } else if true { + 1 + } else if !false { + 1 + } else if true == true { + 1 + } else if true != false { + 1 + } else if false == false { + 1 + } else if false != true { + 1 + } else if 'a' == 'a' { + 1 + } else if 'a' != 'b' { + 1 + } else if 1 == 1 { + 1 + } else if 1 != 2 { + 1 + } else if 1 >= 1 { + 1 + } else if 1 <= 1 { + 1 + } else if 1 < 2 { + 1 + } else if 2 > 1 { + 1 + } else if 1.5 == 1.5 { + 1 + } else if 1.5 != 2.5 { + 1 + } else if 1.5 >= 1.5 { + 1 + } else if 1.5 <= 1.5 { + 1 + } else if 1.5 < 2.5 { + 1 + } else if 2.5 > 1.5 { + 1 + } else if `1` == `1` { + 1 + } else if `1` != `2` { + 1 + } else if `1` >= `1` { + 1 + } else if `1` <= `1` { + 1 + } else if `1` < `2` { + 1 + } else if `2` > `1` { + 1 + } else if 1 == 1 && 2 <= 2 && 3 >= 3 && 4 != 5 { + 1 + } else if 1 == 1 || 4 > 5 || 6 < 3 || 5 != 5 { + 1 + } else { + 0 + } + + dump(if_always_true) + + if_always_false := if x != x { + 1 + } else if false { + 1 + } else if !true { + 1 + } else if true != true { + 1 + } else if true == false { + 1 + } else if false != false { + 1 + } else if false == true { + 1 + } else if 'a' != 'a' { + 1 + } else if 'a' == 'b' { + 1 + } else if 1 != 1 { + 1 + } else if 1 == 2 { + 1 + } else if 1 > 1 { + 1 + } else if 1 < 1 { + 1 + } else if 1 >= 2 { + 1 + } else if 2 <= 1 { + 1 + } else if 1.5 != 1.5 { + 1 + } else if 1.5 == 2.5 { + 1 + } else if 1.5 > 1.5 { + 1 + } else if 1.5 < 1.5 { + 1 + } else if 1.5 >= 2.5 { + 1 + } else if 2.5 <= 1.5 { + 1 + } else if `1` != `1` { + 1 + } else if `1` == `2` { + 1 + } else if `1` < `1` { + 1 + } else if `1` > `1` { + 1 + } else if `1` >= `2` { + 1 + } else if `2` <= `1` { + 1 + } else if 1 == 2 && 3 >= 3 && 4 <= 5 && 5 != 5 { + 1 + } else if 1 == 2 || 2 > 3 || 5 < 4 { + 0 + } else { + 0 + } + + dump(if_always_false) + + match_always_true_var := match x { + x { + 'haha' + } + else { + 'cc' + } + } + dump(match_always_true_var) + + match_always_true_false_bool := match true { + true { + 'haha' + } + false { + 'cc' + } + } + dump(match_always_true_false_bool) + + match_always_true_false_int := match 1 { + 1 { + 'haha' + } + 2 { + 'cc' + } + else { + 'bb' + } + } + dump(match_always_true_false_int) + + match_always_true_false_f64 := match 1.5 { + 1.5 { + 'haha' + } + 2.5 { + 'cc' + } + else { + 'bb' + } + } + dump(match_always_true_false_f64) + + match_always_true_false_string := match 'a' { + 'a' { + 'haha' + } + 'b' { + 'cc' + } + else { + 'bb' + } + } + dump(match_always_true_false_string) + + match_always_true_false_rune := match `a` { + `a` { + 'haha' + } + `b` { + 'cc' + } + else { + 'bb' + } + } + dump(match_always_true_false_rune) +} diff --git a/vlib/v/checker/tests/assign_multi_mismatch.out b/vlib/v/checker/tests/assign_multi_mismatch.out index ab078c0d0..16367b912 100644 --- a/vlib/v/checker/tests/assign_multi_mismatch.out +++ b/vlib/v/checker/tests/assign_multi_mismatch.out @@ -1,3 +1,42 @@ +vlib/v/checker/tests/assign_multi_mismatch.vv:16:2: notice: match is always false + 14 | + 15 | _, _ := 0, match 4 { + 16 | 1 { 0 } + | ^ + 17 | else { 1 } + 18 | } +vlib/v/checker/tests/assign_multi_mismatch.vv:20:2: notice: match is always false + 18 | } + 19 | _ := match 4 { + 20 | 1 { f() } + | ^ + 21 | else { f() } + 22 | } +vlib/v/checker/tests/assign_multi_mismatch.vv:24:2: notice: match is always false + 22 | } + 23 | _, _ := 0, match 4 { + 24 | 1 { f() } + | ^ + 25 | else { f() } + 26 | } +vlib/v/checker/tests/assign_multi_mismatch.vv:28:15: notice: condition is always true + 26 | } + 27 | + 28 | _, _ := 0, if true { 0 } else { 1 } + | ~~~~ + 29 | _ := if true { f() } else { f() } + 30 | _, _ := 0, if true { f() } else { f() } +vlib/v/checker/tests/assign_multi_mismatch.vv:29:9: notice: condition is always true + 27 | + 28 | _, _ := 0, if true { 0 } else { 1 } + 29 | _ := if true { f() } else { f() } + | ~~~~ + 30 | _, _ := 0, if true { f() } else { f() } +vlib/v/checker/tests/assign_multi_mismatch.vv:30:15: notice: condition is always true + 28 | _, _ := 0, if true { 0 } else { 1 } + 29 | _ := if true { f() } else { f() } + 30 | _, _ := 0, if true { f() } else { f() } + | ~~~~ vlib/v/checker/tests/assign_multi_mismatch.vv:5:3: error: assignment mismatch: 1 variable 2 values 3 | } 4 | diff --git a/vlib/v/checker/tests/cast_to_interface_err.out b/vlib/v/checker/tests/cast_to_interface_err.out index 06cb0184b..9e3525a71 100644 --- a/vlib/v/checker/tests/cast_to_interface_err.out +++ b/vlib/v/checker/tests/cast_to_interface_err.out @@ -1,3 +1,10 @@ +vlib/v/checker/tests/cast_to_interface_err.vv:7:5: notice: condition is always true + 5 | fn foo() !int { + 6 | // do something + 7 | if true { + | ~~~~ + 8 | return 1 + 9 | } vlib/v/checker/tests/cast_to_interface_err.vv:20:9: error: `CustomError` doesn't implement method `msg` of interface `IError` 18 | 19 | fn my_error() IError { diff --git a/vlib/v/checker/tests/function_missing_return_type.out b/vlib/v/checker/tests/function_missing_return_type.out index 73292af5e..45c8bf59a 100644 --- a/vlib/v/checker/tests/function_missing_return_type.out +++ b/vlib/v/checker/tests/function_missing_return_type.out @@ -1,3 +1,10 @@ +vlib/v/checker/tests/function_missing_return_type.vv:13:5: notice: condition is always true + 11 | + 12 | fn (s Abc) abc() &int { + 13 | if true { + | ~~~~ + 14 | return &s.x + 15 | } vlib/v/checker/tests/function_missing_return_type.vv:1:1: error: missing return at end of function `h` 1 | fn h() int { | ~~~~~~~~~~ diff --git a/vlib/v/checker/tests/if_diff_expected_type_err.out b/vlib/v/checker/tests/if_diff_expected_type_err.out index db5bc60ca..07f639e64 100644 --- a/vlib/v/checker/tests/if_diff_expected_type_err.out +++ b/vlib/v/checker/tests/if_diff_expected_type_err.out @@ -1,3 +1,10 @@ +vlib/v/checker/tests/if_diff_expected_type_err.vv:5:14: notice: condition is always true + 3 | + 4 | fn main() { + 5 | runes := if true { &some_runes } else { some_other_runes } + | ~~~~ + 6 | println(runes) + 7 | } vlib/v/checker/tests/if_diff_expected_type_err.vv:5:11: error: mismatched types `&[]rune` and `[]rune` 3 | 4 | fn main() { diff --git a/vlib/v/checker/tests/if_expr_last_stmt.out b/vlib/v/checker/tests/if_expr_last_stmt.out index 67100d7b9..4afff19cc 100644 --- a/vlib/v/checker/tests/if_expr_last_stmt.out +++ b/vlib/v/checker/tests/if_expr_last_stmt.out @@ -1,3 +1,16 @@ +vlib/v/checker/tests/if_expr_last_stmt.vv:2:9: notice: condition is always true + 1 | fn main() { + 2 | _ = if true { + | ~~~~ + 3 | 1 + 4 | } else if false { +vlib/v/checker/tests/if_expr_last_stmt.vv:4:12: notice: condition is always false + 2 | _ = if true { + 3 | 1 + 4 | } else if false { + | ~~~~~ + 5 | } else { + 6 | } vlib/v/checker/tests/if_expr_last_stmt.vv:4:4: error: `if` expression requires an expression as the last statement of every branch 2 | _ = if true { 3 | 1 diff --git a/vlib/v/checker/tests/if_expr_mismatch.out b/vlib/v/checker/tests/if_expr_mismatch.out index 0c45b8883..6b100bd0e 100644 --- a/vlib/v/checker/tests/if_expr_mismatch.out +++ b/vlib/v/checker/tests/if_expr_mismatch.out @@ -1,3 +1,9 @@ +vlib/v/checker/tests/if_expr_mismatch.vv:2:10: notice: condition is always true + 1 | fn main() { + 2 | s := if true { '12' } else { 12 } + | ~~~~ + 3 | println(s) + 4 | } vlib/v/checker/tests/if_expr_mismatch.vv:2:7: error: mismatched types `string` and `int literal` 1 | fn main() { 2 | s := if true { '12' } else { 12 } diff --git a/vlib/v/checker/tests/if_expr_no_else.out b/vlib/v/checker/tests/if_expr_no_else.out index 8ccea6c68..02b0460ab 100644 --- a/vlib/v/checker/tests/if_expr_no_else.out +++ b/vlib/v/checker/tests/if_expr_no_else.out @@ -1,3 +1,9 @@ +vlib/v/checker/tests/if_expr_no_else.vv:2:9: notice: condition is always true + 1 | fn main() { + 2 | _ = if true { + | ~~~~ + 3 | 1 + 4 | } vlib/v/checker/tests/if_expr_no_else.vv:2:6: error: `if` expression needs `else` clause 1 | fn main() { 2 | _ = if true { diff --git a/vlib/v/checker/tests/if_expr_with_none_only.out b/vlib/v/checker/tests/if_expr_with_none_only.out index b0350ccb7..b4a286f79 100644 --- a/vlib/v/checker/tests/if_expr_with_none_only.out +++ b/vlib/v/checker/tests/if_expr_with_none_only.out @@ -1,3 +1,8 @@ +vlib/v/checker/tests/if_expr_with_none_only.vv:2:10: notice: condition is always true + 1 | fn main() { + 2 | a := if true { none } else { none } + | ~~~~ + 3 | } vlib/v/checker/tests/if_expr_with_none_only.vv:2:2: warning: unused variable: `a` 1 | fn main() { 2 | a := if true { none } else { none } diff --git a/vlib/v/checker/tests/if_match_result.out b/vlib/v/checker/tests/if_match_result.out index e0e0f4961..e320d08c1 100644 --- a/vlib/v/checker/tests/if_match_result.out +++ b/vlib/v/checker/tests/if_match_result.out @@ -1,3 +1,31 @@ +vlib/v/checker/tests/if_match_result.vv:3:2: notice: match is always false + 1 | // missing results + 2 | _ = match 4 { + 3 | 1 {} + | ^ + 4 | else {} + 5 | } +vlib/v/checker/tests/if_match_result.vv:6:8: notice: condition is always true + 4 | else {} + 5 | } + 6 | _ = if true { + | ~~~~ + 7 | } else { + 8 | } +vlib/v/checker/tests/if_match_result.vv:12:2: notice: match is always false + 10 | // void results + 11 | _ = match 4 { + 12 | 1 { println('') } + | ^ + 13 | else { exit(0) } + 14 | } +vlib/v/checker/tests/if_match_result.vv:15:8: notice: condition is always true + 13 | else { exit(0) } + 14 | } + 15 | _ = if true { + | ~~~~ + 16 | println('') + 17 | } else { vlib/v/checker/tests/if_match_result.vv:2:3: error: assignment mismatch: 1 variable 0 values 1 | // missing results 2 | _ = match 4 { diff --git a/vlib/v/checker/tests/if_mismatch_muls_err.out b/vlib/v/checker/tests/if_mismatch_muls_err.out index a07132c7a..e2c178061 100644 --- a/vlib/v/checker/tests/if_mismatch_muls_err.out +++ b/vlib/v/checker/tests/if_mismatch_muls_err.out @@ -1,3 +1,10 @@ +vlib/v/checker/tests/if_mismatch_muls_err.vv:17:14: notice: condition is always false + 15 | + 16 | fn main() { + 17 | dummy := if false { + | ~~~~~ + 18 | res := &Dum{ + 19 | v: 2 vlib/v/checker/tests/if_mismatch_muls_err.vv:17:11: error: mismatched types `&&Dum` and `&Dum` 15 | 16 | fn main() { diff --git a/vlib/v/checker/tests/immutable_array_in_if_expr_index.out b/vlib/v/checker/tests/immutable_array_in_if_expr_index.out index 924921e54..4b2f2c2ea 100644 --- a/vlib/v/checker/tests/immutable_array_in_if_expr_index.out +++ b/vlib/v/checker/tests/immutable_array_in_if_expr_index.out @@ -1,3 +1,10 @@ +vlib/v/checker/tests/immutable_array_in_if_expr_index.vv:4:6: notice: condition is always true + 2 | array_a := [1, 2, 3] + 3 | array_b := [4, 5, 6] + 4 | (if true { array_a } else { array_b })[0] = 999 + | ~~~~ + 5 | println(array_a) + 6 | } vlib/v/checker/tests/immutable_array_in_if_expr_index.vv:4:13: error: `array_a` is immutable, declare it with `mut` to make it mutable 2 | array_a := [1, 2, 3] 3 | array_b := [4, 5, 6] diff --git a/vlib/v/checker/tests/immutable_to_mutable_err.out b/vlib/v/checker/tests/immutable_to_mutable_err.out index c8f49dbab..b3d4c1edd 100644 --- a/vlib/v/checker/tests/immutable_to_mutable_err.out +++ b/vlib/v/checker/tests/immutable_to_mutable_err.out @@ -1,3 +1,10 @@ +vlib/v/checker/tests/immutable_to_mutable_err.vv:10:20: notice: condition is always true + 8 | fn main() { + 9 | arr := [1, 2, 3] // declared as immutable! + 10 | mut arr_mut := if true { arr } else { []int{} } + | ~~~~ + 11 | mut arr_mut2 := match true { + 12 | true { arr } vlib/v/checker/tests/immutable_to_mutable_err.vv:10:27: notice: left-side of assignment expects a mutable reference, but variable `arr` is immutable, declare it with `mut` to make it mutable or clone it 8 | fn main() { 9 | arr := [1, 2, 3] // declared as immutable! @@ -5,6 +12,13 @@ vlib/v/checker/tests/immutable_to_mutable_err.vv:10:27: notice: left-side of ass | ~~~ 11 | mut arr_mut2 := match true { 12 | true { arr } +vlib/v/checker/tests/immutable_to_mutable_err.vv:12:3: notice: match is always true + 10 | mut arr_mut := if true { arr } else { []int{} } + 11 | mut arr_mut2 := match true { + 12 | true { arr } + | ~~~~ + 13 | else { [0] } + 14 | } vlib/v/checker/tests/immutable_to_mutable_err.vv:12:10: notice: left-side of assignment expects a mutable reference, but variable `arr` is immutable, declare it with `mut` to make it mutable or clone it 10 | mut arr_mut := if true { arr } else { []int{} } 11 | mut arr_mut2 := match true { diff --git a/vlib/v/checker/tests/match_else_last_expr.out b/vlib/v/checker/tests/match_else_last_expr.out index c3331f18e..135af6aa9 100644 --- a/vlib/v/checker/tests/match_else_last_expr.out +++ b/vlib/v/checker/tests/match_else_last_expr.out @@ -1,7 +1,21 @@ +vlib/v/checker/tests/match_else_last_expr.vv:3:3: notice: match is always true + 1 | fn main() { + 2 | match 1 { + 3 | 1 { println('1') } + | ^ + 4 | else { println('else') } + 5 | 4 { println('4') } +vlib/v/checker/tests/match_else_last_expr.vv:5:3: notice: match is always false + 3 | 1 { println('1') } + 4 | else { println('else') } + 5 | 4 { println('4') } + | ^ + 6 | } + 7 | } vlib/v/checker/tests/match_else_last_expr.vv:4:3: error: `else` must be the last branch of `match` 2 | match 1 { 3 | 1 { println('1') } 4 | else { println('else') } | ~~~~ 5 | 4 { println('4') } - 6 | } \ No newline at end of file + 6 | } diff --git a/vlib/v/checker/tests/match_expr_empty_branch.out b/vlib/v/checker/tests/match_expr_empty_branch.out index 927da5aed..290056536 100644 --- a/vlib/v/checker/tests/match_expr_empty_branch.out +++ b/vlib/v/checker/tests/match_expr_empty_branch.out @@ -1,3 +1,15 @@ +vlib/v/checker/tests/match_expr_empty_branch.vv:2:2: notice: match is always true + 1 | _ := match true { + 2 | true { 0 } + | ~~~~ + 3 | false {} + 4 | } +vlib/v/checker/tests/match_expr_empty_branch.vv:3:2: notice: match is always false + 1 | _ := match true { + 2 | true { 0 } + 3 | false {} + | ~~~~~ + 4 | } vlib/v/checker/tests/match_expr_empty_branch.vv:3:2: error: `match` expression requires an expression as the last statement of every branch 1 | _ := match true { 2 | true { 0 } diff --git a/vlib/v/checker/tests/match_expr_with_none_only.out b/vlib/v/checker/tests/match_expr_with_none_only.out index c1226c5ff..41fd5f874 100644 --- a/vlib/v/checker/tests/match_expr_with_none_only.out +++ b/vlib/v/checker/tests/match_expr_with_none_only.out @@ -1,3 +1,10 @@ +vlib/v/checker/tests/match_expr_with_none_only.vv:3:3: notice: match is always true + 1 | fn main() { + 2 | a := match 1 { + 3 | 1 { none } + | ^ + 4 | else { none } + 5 | } vlib/v/checker/tests/match_expr_with_none_only.vv:2:2: warning: unused variable: `a` 1 | fn main() { 2 | a := match 1 { diff --git a/vlib/v/checker/tests/match_missing.out b/vlib/v/checker/tests/match_missing.out index 85f831c7b..7f30b81fc 100644 --- a/vlib/v/checker/tests/match_missing.out +++ b/vlib/v/checker/tests/match_missing.out @@ -1,3 +1,9 @@ +vlib/v/checker/tests/match_missing.vv:10:2: notice: match is always false + 8 | + 9 | match true { + 10 | 'foo' == 'bar' {} + | ~~~~~~~~~~~~~~ + 11 | } vlib/v/checker/tests/match_missing.vv:1:1: error: `match` must have at least two branches including `else`, or an exhaustive set of branches 1 | match true { | ~~~~~ diff --git a/vlib/v/checker/tests/match_return_mismatch_type_err.out b/vlib/v/checker/tests/match_return_mismatch_type_err.out index ca6cf7889..a81d56a23 100644 --- a/vlib/v/checker/tests/match_return_mismatch_type_err.out +++ b/vlib/v/checker/tests/match_return_mismatch_type_err.out @@ -1,3 +1,10 @@ +vlib/v/checker/tests/match_return_mismatch_type_err.vv:3:3: notice: match is always true + 1 | fn main() { + 2 | a := match 1 { + 3 | 1 { 'aa' } + | ^ + 4 | else { 22 } + 5 | } vlib/v/checker/tests/match_return_mismatch_type_err.vv:27:6: warning: cannot assign a reference to a value (this will be an error soon) left=string false right=string true ptr=true 25 | 26 | mut res := '' diff --git a/vlib/v/checker/tests/none_match_cond_err.out b/vlib/v/checker/tests/none_match_cond_err.out index 9853c66e4..032f8b9d5 100644 --- a/vlib/v/checker/tests/none_match_cond_err.out +++ b/vlib/v/checker/tests/none_match_cond_err.out @@ -1,3 +1,9 @@ +vlib/v/checker/tests/none_match_cond_err.vv:2:2: notice: match is always true + 1 | match none { + 2 | none {} + | ~~~~ + 3 | else {} + 4 | } vlib/v/checker/tests/none_match_cond_err.vv:1:1: error: `none` cannot be a match condition 1 | match none { | ~~~~~~~~~~~~ diff --git a/vlib/v/checker/tests/option_interface_mismatch.out b/vlib/v/checker/tests/option_interface_mismatch.out index af522fdb2..c3ed1fb85 100644 --- a/vlib/v/checker/tests/option_interface_mismatch.out +++ b/vlib/v/checker/tests/option_interface_mismatch.out @@ -1,6 +1,12 @@ +vlib/v/checker/tests/option_interface_mismatch.vv:11:12: notice: condition is always true + 9 | + 10 | fn give_string(line string) ?MObject { + 11 | return if true { 'string' } else { 'string' } + | ~~~~ + 12 | } vlib/v/checker/tests/option_interface_mismatch.vv:11:9: error: mismatched types `?MObject` and `string` - 9 | + 9 | 10 | fn give_string(line string) ?MObject { 11 | return if true { 'string' } else { 'string' } | ~~ - 12 | } \ No newline at end of file + 12 | } diff --git a/vlib/v/checker/tests/return_missing_comp_if_nested.out b/vlib/v/checker/tests/return_missing_comp_if_nested.out index 4c24e859e..97cf99d7d 100644 --- a/vlib/v/checker/tests/return_missing_comp_if_nested.out +++ b/vlib/v/checker/tests/return_missing_comp_if_nested.out @@ -1,7 +1,7 @@ vlib/v/checker/tests/return_missing_comp_if_nested.vv:3:1: error: missing return at end of function `foo` 1 | fn main() {} - 2 | + 2 | 3 | fn foo() string { | ~~~~~~~~~~~~~~~ - 4 | $if windows { - 5 | if true { \ No newline at end of file + 4 | a := 1 + 5 | b := 1 diff --git a/vlib/v/checker/tests/return_missing_comp_if_nested.vv b/vlib/v/checker/tests/return_missing_comp_if_nested.vv index afd02d76b..285034acb 100644 --- a/vlib/v/checker/tests/return_missing_comp_if_nested.vv +++ b/vlib/v/checker/tests/return_missing_comp_if_nested.vv @@ -1,13 +1,15 @@ fn main() {} fn foo() string { + a := 1 + b := 1 $if windows { - if true { + if a == b { } else { return '' } } $else { - if true { + if a == b { } else { return '' } diff --git a/vlib/v/checker/tests/return_missing_if_else_simple.out b/vlib/v/checker/tests/return_missing_if_else_simple.out index 41eb21bee..c1a6591dc 100644 --- a/vlib/v/checker/tests/return_missing_if_else_simple.out +++ b/vlib/v/checker/tests/return_missing_if_else_simple.out @@ -1,3 +1,23 @@ +vlib/v/checker/tests/return_missing_if_else_simple.vv:2:5: notice: condition is always true + 1 | fn if_no_returns() int { + 2 | if true { + | ~~~~ + 3 | println('no return in then') + 4 | } else { +vlib/v/checker/tests/return_missing_if_else_simple.vv:11:5: notice: condition is always true + 9 | + 10 | fn if_no_return_in_else() int { + 11 | if true { + | ~~~~ + 12 | return 123 + 13 | } else { +vlib/v/checker/tests/return_missing_if_else_simple.vv:22:5: notice: condition is always true + 20 | // The if here always returns, so the function + 21 | // returns too: + 22 | if true { + | ~~~~ + 23 | return 123 + 24 | } else { vlib/v/checker/tests/return_missing_if_else_simple.vv:1:1: error: missing return at end of function `if_no_returns` 1 | fn if_no_returns() int { | ~~~~~~~~~~~~~~~~~~~~~~ diff --git a/vlib/v/checker/tests/return_missing_if_match.out b/vlib/v/checker/tests/return_missing_if_match.out index d4877d874..179de29f3 100644 --- a/vlib/v/checker/tests/return_missing_if_match.out +++ b/vlib/v/checker/tests/return_missing_if_match.out @@ -1,7 +1,28 @@ +vlib/v/checker/tests/return_missing_if_match.vv:4:5: notice: condition is always true + 2 | + 3 | fn foo() string { + 4 | if true { + | ~~~~ + 5 | match 1 { + 6 | 1 { return '' } +vlib/v/checker/tests/return_missing_if_match.vv:6:4: notice: match is always true + 4 | if true { + 5 | match 1 { + 6 | 1 { return '' } + | ^ + 7 | 2 {} + 8 | else { return '' } +vlib/v/checker/tests/return_missing_if_match.vv:7:4: notice: match is always false + 5 | match 1 { + 6 | 1 { return '' } + 7 | 2 {} + | ^ + 8 | else { return '' } + 9 | } vlib/v/checker/tests/return_missing_if_match.vv:3:1: error: missing return at end of function `foo` 1 | fn main() {} - 2 | + 2 | 3 | fn foo() string { | ~~~~~~~~~~~~~~~ 4 | if true { - 5 | match 1 { \ No newline at end of file + 5 | match 1 { diff --git a/vlib/v/checker/tests/return_missing_match_if.out b/vlib/v/checker/tests/return_missing_match_if.out index 2e7e026f7..f59e9e689 100644 --- a/vlib/v/checker/tests/return_missing_match_if.out +++ b/vlib/v/checker/tests/return_missing_match_if.out @@ -1,3 +1,24 @@ +vlib/v/checker/tests/return_missing_match_if.vv:5:3: notice: match is always true + 3 | fn foo() string { + 4 | match 1 { + 5 | 1 { + | ^ + 6 | return '' + 7 | } +vlib/v/checker/tests/return_missing_match_if.vv:8:3: notice: match is always false + 6 | return '' + 7 | } + 8 | 2 { + | ^ + 9 | if true { + 10 | } else { +vlib/v/checker/tests/return_missing_match_if.vv:9:7: notice: condition is always true + 7 | } + 8 | 2 { + 9 | if true { + | ~~~~ + 10 | } else { + 11 | return '' vlib/v/checker/tests/return_missing_match_if.vv:3:1: error: missing return at end of function `foo` 1 | fn main() {} 2 | diff --git a/vlib/v/checker/tests/return_missing_nested.out b/vlib/v/checker/tests/return_missing_nested.out index fce3245d9..be90cc346 100644 --- a/vlib/v/checker/tests/return_missing_nested.out +++ b/vlib/v/checker/tests/return_missing_nested.out @@ -1,7 +1,21 @@ +vlib/v/checker/tests/return_missing_nested.vv:4:5: notice: condition is always true + 2 | + 3 | fn foo() string { + 4 | if true { + | ~~~~ + 5 | return '' + 6 | } else { +vlib/v/checker/tests/return_missing_nested.vv:7:6: notice: condition is always true + 5 | return '' + 6 | } else { + 7 | if true { + | ~~~~ + 8 | return '' + 9 | } vlib/v/checker/tests/return_missing_nested.vv:3:1: error: missing return at end of function `foo` 1 | fn main() {} - 2 | + 2 | 3 | fn foo() string { | ~~~~~~~~~~~~~~~ 4 | if true { - 5 | return '' \ No newline at end of file + 5 | return '' diff --git a/vlib/v/checker/tests/return_missing_simple.out b/vlib/v/checker/tests/return_missing_simple.out index 80163453c..cd954a258 100644 --- a/vlib/v/checker/tests/return_missing_simple.out +++ b/vlib/v/checker/tests/return_missing_simple.out @@ -1,7 +1,14 @@ +vlib/v/checker/tests/return_missing_simple.vv:4:5: notice: condition is always true + 2 | + 3 | fn foo() string { + 4 | if true { + | ~~~~ + 5 | return '' + 6 | } else { vlib/v/checker/tests/return_missing_simple.vv:3:1: error: missing return at end of function `foo` 1 | fn main() {} - 2 | + 2 | 3 | fn foo() string { | ~~~~~~~~~~~~~~~ 4 | if true { - 5 | return '' \ No newline at end of file + 5 | return '' diff --git a/vlib/v/checker/tests/return_working_comp_if_nested.vv b/vlib/v/checker/tests/return_working_comp_if_nested.vv index fa44a40e1..0c7c61651 100644 --- a/vlib/v/checker/tests/return_working_comp_if_nested.vv +++ b/vlib/v/checker/tests/return_working_comp_if_nested.vv @@ -1,14 +1,16 @@ fn main() {} fn foo() string { + a := 1 + b := 1 $if windows { - if true { + if a == b { return '' } else { return '' } } $else { - if true { + if a == b { return '' } else { return '' diff --git a/vlib/v/checker/tests/return_working_if_match.out b/vlib/v/checker/tests/return_working_if_match.out index e69de29bb..f7e84c03a 100644 --- a/vlib/v/checker/tests/return_working_if_match.out +++ b/vlib/v/checker/tests/return_working_if_match.out @@ -0,0 +1,21 @@ +vlib/v/checker/tests/return_working_if_match.vv:4:5: notice: condition is always true + 2 | + 3 | fn foo() string { + 4 | if true { + | ~~~~ + 5 | match 1 { + 6 | 1 { return '' } +vlib/v/checker/tests/return_working_if_match.vv:6:4: notice: match is always true + 4 | if true { + 5 | match 1 { + 6 | 1 { return '' } + | ^ + 7 | 2 { return '' } + 8 | else { return '' } +vlib/v/checker/tests/return_working_if_match.vv:7:4: notice: match is always false + 5 | match 1 { + 6 | 1 { return '' } + 7 | 2 { return '' } + | ^ + 8 | else { return '' } + 9 | } diff --git a/vlib/v/checker/tests/return_working_match_if.out b/vlib/v/checker/tests/return_working_match_if.out index e69de29bb..0c157486c 100644 --- a/vlib/v/checker/tests/return_working_match_if.out +++ b/vlib/v/checker/tests/return_working_match_if.out @@ -0,0 +1,21 @@ +vlib/v/checker/tests/return_working_match_if.vv:5:3: notice: match is always true + 3 | fn foo() string { + 4 | match 1 { + 5 | 1 { + | ^ + 6 | return '' + 7 | } +vlib/v/checker/tests/return_working_match_if.vv:8:3: notice: match is always false + 6 | return '' + 7 | } + 8 | 2 { + | ^ + 9 | if true { + 10 | return '' +vlib/v/checker/tests/return_working_match_if.vv:9:7: notice: condition is always true + 7 | } + 8 | 2 { + 9 | if true { + | ~~~~ + 10 | return '' + 11 | } else { diff --git a/vlib/v/checker/tests/return_working_nested.out b/vlib/v/checker/tests/return_working_nested.out index e69de29bb..667144227 100644 --- a/vlib/v/checker/tests/return_working_nested.out +++ b/vlib/v/checker/tests/return_working_nested.out @@ -0,0 +1,14 @@ +vlib/v/checker/tests/return_working_nested.vv:4:5: notice: condition is always true + 2 | + 3 | fn foo() string { + 4 | if true { + | ~~~~ + 5 | return '' + 6 | } else { +vlib/v/checker/tests/return_working_nested.vv:7:6: notice: condition is always true + 5 | return '' + 6 | } else { + 7 | if true { + | ~~~~ + 8 | return '' + 9 | } diff --git a/vlib/v/checker/tests/return_working_simple.out b/vlib/v/checker/tests/return_working_simple.out index e69de29bb..6000815f2 100644 --- a/vlib/v/checker/tests/return_working_simple.out +++ b/vlib/v/checker/tests/return_working_simple.out @@ -0,0 +1,7 @@ +vlib/v/checker/tests/return_working_simple.vv:4:5: notice: condition is always true + 2 | + 3 | fn foo() string { + 4 | if true { + | ~~~~ + 5 | return '' + 6 | } else { diff --git a/vlib/v/checker/tests/return_working_two_if.out b/vlib/v/checker/tests/return_working_two_if.out index e69de29bb..49ca3dd4d 100644 --- a/vlib/v/checker/tests/return_working_two_if.out +++ b/vlib/v/checker/tests/return_working_two_if.out @@ -0,0 +1,21 @@ +vlib/v/checker/tests/return_working_two_if.vv:4:5: notice: condition is always true + 2 | + 3 | fn foo() string { + 4 | if true { + | ~~~~ + 5 | if true { + 6 | return '' +vlib/v/checker/tests/return_working_two_if.vv:5:6: notice: condition is always true + 3 | fn foo() string { + 4 | if true { + 5 | if true { + | ~~~~ + 6 | return '' + 7 | } +vlib/v/checker/tests/return_working_two_if.vv:9:6: notice: condition is always true + 7 | } + 8 | + 9 | if true { + | ~~~~ + 10 | return '' + 11 | } else { diff --git a/vlib/v/checker/tests/shift_ops_expressions.out b/vlib/v/checker/tests/shift_ops_expressions.out index cad80513e..447106cc2 100644 --- a/vlib/v/checker/tests/shift_ops_expressions.out +++ b/vlib/v/checker/tests/shift_ops_expressions.out @@ -5,6 +5,13 @@ vlib/v/checker/tests/shift_ops_expressions.vv:4:2: notice: shifting a value from | ^ 5 | if true { 6 | a << 2 +vlib/v/checker/tests/shift_ops_expressions.vv:5:5: notice: condition is always true + 3 | mut arr := []int{} + 4 | a << 1 + 5 | if true { + | ~~~~ + 6 | a << 2 + 7 | } vlib/v/checker/tests/shift_ops_expressions.vv:6:3: notice: shifting a value from a signed type `int` can change the sign 4 | a << 1 5 | if true { @@ -12,6 +19,13 @@ vlib/v/checker/tests/shift_ops_expressions.vv:6:3: notice: shifting a value from | ^ 7 | } 8 | c := if true { a << 111 } else { a << 333 } +vlib/v/checker/tests/shift_ops_expressions.vv:8:10: notice: condition is always true + 6 | a << 2 + 7 | } + 8 | c := if true { a << 111 } else { a << 333 } + | ~~~~ + 9 | println(c) + 10 | a << 1 vlib/v/checker/tests/shift_ops_expressions.vv:8:17: notice: shifting a value from a signed type `int` can change the sign 6 | a << 2 7 | } @@ -26,6 +40,27 @@ vlib/v/checker/tests/shift_ops_expressions.vv:10:2: notice: shifting a value fro | ^ 11 | println(a) 12 | 5 << 9 +vlib/v/checker/tests/shift_ops_expressions.vv:20:5: notice: condition is always true + 18 | // + 19 | arr << 1 + 20 | if true { + | ~~~~ + 21 | arr << 2 + 22 | } +vlib/v/checker/tests/shift_ops_expressions.vv:23:10: notice: condition is always true + 21 | arr << 2 + 22 | } + 23 | d := if true { + | ~~~~ + 24 | arr << 111 + 25 | 777 +vlib/v/checker/tests/shift_ops_expressions.vv:32:10: notice: condition is always true + 30 | println(d) + 31 | // + 32 | x := if true { + | ~~~~ + 33 | a << 1 + 34 | 999 vlib/v/checker/tests/shift_ops_expressions.vv:33:3: notice: shifting a value from a signed type `int` can change the sign 31 | // 32 | x := if true { @@ -40,6 +75,27 @@ vlib/v/checker/tests/shift_ops_expressions.vv:37:3: notice: shifting a value fro | ^ 38 | println('---') 39 | 555 +vlib/v/checker/tests/shift_ops_expressions.vv:47:5: notice: condition is always true + 45 | mut rr := 12 + 46 | rr >> 1 + 47 | if true { + | ~~~~ + 48 | rr >> 2 + 49 | } +vlib/v/checker/tests/shift_ops_expressions.vv:50:10: notice: condition is always true + 48 | rr >> 2 + 49 | } + 50 | c := if true { rr >> 111 } else { rr >> 333 } + | ~~~~ + 51 | println(c) + 52 | rr >> 1 +vlib/v/checker/tests/shift_ops_expressions.vv:61:10: notice: condition is always true + 59 | } + 60 | // + 61 | x := if true { + | ~~~~ + 62 | rr >> 1 + 63 | 999 vlib/v/checker/tests/shift_ops_expressions.vv:4:2: error: unused expression 2 | mut a := 12 3 | mut arr := []int{} diff --git a/vlib/v/checker/tests/struct_scope_err.out b/vlib/v/checker/tests/struct_scope_err.out index c1f20f41d..5aa90af53 100644 --- a/vlib/v/checker/tests/struct_scope_err.out +++ b/vlib/v/checker/tests/struct_scope_err.out @@ -1,3 +1,10 @@ +vlib/v/checker/tests/struct_scope_err.vv:8:5: notice: condition is always true + 6 | } + 7 | + 8 | if true { + | ~~~~ + 9 | fb := Foobar{5, 6} + 10 | println(fb.foo) vlib/v/checker/tests/struct_scope_err.vv:16:8: error: unknown type `Foobar` 14 | 15 | fn x() { diff --git a/vlib/v/checker/tests/unnecessary_parenthesis.out b/vlib/v/checker/tests/unnecessary_parenthesis.out index 3f1a34036..3fb5171d8 100644 --- a/vlib/v/checker/tests/unnecessary_parenthesis.out +++ b/vlib/v/checker/tests/unnecessary_parenthesis.out @@ -1,3 +1,23 @@ +vlib/v/checker/tests/unnecessary_parenthesis.vv:2:5: notice: condition is always true + 1 | fn main() { + 2 | if (1 == 1) { + | ~~~~~~~~ + 3 | println('yeay') + 4 | } else if (1 == 2) { +vlib/v/checker/tests/unnecessary_parenthesis.vv:4:12: notice: condition is always false + 2 | if (1 == 1) { + 3 | println('yeay') + 4 | } else if (1 == 2) { + | ~~~~~~~~ + 5 | println("oh no :'(") + 6 | } else if (1 == 3) { +vlib/v/checker/tests/unnecessary_parenthesis.vv:6:12: notice: condition is always false + 4 | } else if (1 == 2) { + 5 | println("oh no :'(") + 6 | } else if (1 == 3) { + | ~~~~~~~~ + 7 | println("what's wrong with physics ????") + 8 | } vlib/v/checker/tests/unnecessary_parenthesis.vv:2:2: warning: unnecessary `()` in `if` condition, use `if expr {` instead of `if (expr) {`. 1 | fn main() { 2 | if (1 == 1) { diff --git a/vlib/v/checker/tests/unreachable_code.out b/vlib/v/checker/tests/unreachable_code.out index 3c00f08cc..627a65ded 100644 --- a/vlib/v/checker/tests/unreachable_code.out +++ b/vlib/v/checker/tests/unreachable_code.out @@ -1,3 +1,10 @@ +vlib/v/checker/tests/unreachable_code.vv:27:12: notice: condition is always true + 25 | return 3 + 26 | } + 27 | return if 1 == 1 { 1 } else { 2 } + | ~~~~~~ + 28 | a := 1 + 29 | println(a) vlib/v/checker/tests/unreachable_code.vv:15:3: error: unreachable code 13 | continue 14 | // ... diff --git a/vlib/v/gen/js/tests/testdata/compare_ints.v b/vlib/v/gen/js/tests/testdata/compare_ints.v index f2144661b..e32416a70 100644 --- a/vlib/v/gen/js/tests/testdata/compare_ints.v +++ b/vlib/v/gen/js/tests/testdata/compare_ints.v @@ -1,15 +1,17 @@ -if 2 > 1 { +a := 2 +b := 1 +if a > b { println('2 > 1') } -if 2 < 1 { +if a < b { println('2 < 1') } -if 2 == 1 { +if a == b { println('2 == 1') } -if 2 == 0 { +if a == 0 { println('2 == 0') } diff --git a/vlib/v/gen/js/tests/testdata/match.v b/vlib/v/gen/js/tests/testdata/match.v index 61796f1b1..e197b2d4f 100644 --- a/vlib/v/gen/js/tests/testdata/match.v +++ b/vlib/v/gen/js/tests/testdata/match.v @@ -24,7 +24,8 @@ fn match_vec(v Vec) { } fn match_classic_num() { - match 42 { + a := 42 + match a { 0 { assert false (false) diff --git a/vlib/v/slow_tests/repl/conditional_blocks/if.repl b/vlib/v/slow_tests/repl/conditional_blocks/if.repl index 9b3b2d6c5..2b17e8974 100644 --- a/vlib/v/slow_tests/repl/conditional_blocks/if.repl +++ b/vlib/v/slow_tests/repl/conditional_blocks/if.repl @@ -1,4 +1,6 @@ -if true { +a := 5 +b := 5 +if a == b { println('foo') } ===output=== diff --git a/vlib/v/slow_tests/repl/conditional_blocks/if_else.repl b/vlib/v/slow_tests/repl/conditional_blocks/if_else.repl index b44b66eed..a51c5b9e5 100644 --- a/vlib/v/slow_tests/repl/conditional_blocks/if_else.repl +++ b/vlib/v/slow_tests/repl/conditional_blocks/if_else.repl @@ -1,4 +1,6 @@ -if false { +a := 6 +b := 5 +if a == b { println('foo') } else { println('bar') diff --git a/vlib/v/tests/skip_unused/generic_call_from_json.vv b/vlib/v/tests/skip_unused/generic_call_from_json.vv index e29d27112..de9a739ff 100644 --- a/vlib/v/tests/skip_unused/generic_call_from_json.vv +++ b/vlib/v/tests/skip_unused/generic_call_from_json.vv @@ -16,7 +16,9 @@ fn f() ! { } fn main() { - if false { + a := 5 + b := 6 + if a == b { f()! } println('ok') diff --git a/vlib/v/transformer/transformer.v b/vlib/v/transformer/transformer.v index b0d187304..c2c0a32fd 100644 --- a/vlib/v/transformer/transformer.v +++ b/vlib/v/transformer/transformer.v @@ -1024,7 +1024,129 @@ pub fn (mut t Transformer) infix_expr(mut node ast.InfixExpr) ast.Expr { else {} } } - else {} + ast.CharLiteral { + match mut node.right { + ast.CharLiteral { + left_val := node.left.val.runes()[0] + right_val := node.right.val.runes()[0] + + match node.op { + .eq { + return ast.BoolLiteral{ + val: left_val == right_val + } + } + .ne { + return ast.BoolLiteral{ + val: left_val != right_val + } + } + .gt { + return ast.BoolLiteral{ + val: left_val > right_val + } + } + .ge { + return ast.BoolLiteral{ + val: left_val >= right_val + } + } + .lt { + return ast.BoolLiteral{ + val: left_val < right_val + } + } + .le { + return ast.BoolLiteral{ + val: left_val <= right_val + } + } + .plus { + return ast.CharLiteral{ + val: (left_val + right_val).str() + pos: pos + } + } + .mul { + return ast.CharLiteral{ + val: (left_val * right_val).str() + pos: pos + } + } + .minus { + return ast.CharLiteral{ + val: (left_val - right_val).str() + pos: pos + } + } + .div { + return ast.CharLiteral{ + val: (left_val / right_val).str() + pos: pos + } + } + .mod { + return ast.CharLiteral{ + val: (left_val % right_val).str() + pos: pos + } + } + .xor { + return ast.CharLiteral{ + val: (left_val ^ right_val).str() + pos: pos + } + } + .pipe { + return ast.CharLiteral{ + val: (left_val | right_val).str() + pos: pos + } + } + .amp { + return ast.CharLiteral{ + val: (left_val & right_val).str() + pos: pos + } + } + .left_shift { + return ast.CharLiteral{ + val: (unsafe { left_val << right_val }).str() + pos: pos + } + } + .right_shift { + return ast.CharLiteral{ + val: (left_val >> right_val).str() + pos: pos + } + } + .unsigned_right_shift { + return ast.CharLiteral{ + val: (u64(left_val) >>> right_val).str() + pos: pos + } + } + else {} + } + } + else {} + } + } + else { + // for `a == a`, `a != a`, `struct.f != struct.f` + // Note: can't compare `f32` or `f64` here, as `NaN != NaN` will return true in IEEE 754 + if node.left.type_name() == node.right.type_name() + && node.left_type !in [ast.f32_type, ast.f64_type] && node.op in [.eq, .ne] { + left_name := '${node.left}' + right_name := '${node.right}' + if left_name == right_name { + return ast.BoolLiteral{ + val: if node.op == .eq { true } else { false } + } + } + } + } } return node } -- 2.39.5