| 1 | import os |
| 2 | import v3.flat |
| 3 | import v3.parser |
| 4 | import v3.pref |
| 5 | import v3.types |
| 6 | |
| 7 | // parse_parser_regression_source reads parse parser regression source input for v3 tests. |
| 8 | fn parse_parser_regression_source(name string, source string) &flat.FlatAst { |
| 9 | src := os.join_path(os.temp_dir(), 'v3_${name}.v') |
| 10 | os.write_file(src, source) or { panic(err) } |
| 11 | mut prefs := pref.new_preferences() |
| 12 | mut p := parser.Parser.new(prefs) |
| 13 | p.parse_into(src) |
| 14 | return p.a |
| 15 | } |
| 16 | |
| 17 | fn parse_parser_regression_sources(name string, sources []string) &flat.FlatAst { |
| 18 | mut prefs := pref.new_preferences() |
| 19 | mut p := parser.Parser.new(prefs) |
| 20 | for i, source in sources { |
| 21 | src := os.join_path(os.temp_dir(), 'v3_${name}_${i}.v') |
| 22 | os.write_file(src, source) or { panic(err) } |
| 23 | p.parse_into(src) |
| 24 | } |
| 25 | return p.a |
| 26 | } |
| 27 | |
| 28 | // interface_method_param_types supports interface method param types handling for v3 tests. |
| 29 | fn interface_method_param_types(a &flat.FlatAst, iface string, method string) []string { |
| 30 | for node in a.nodes { |
| 31 | if node.kind != .interface_decl || node.value != iface { |
| 32 | continue |
| 33 | } |
| 34 | for i in 0 .. node.children_count { |
| 35 | field := a.child_node(&node, i) |
| 36 | if field.kind != .interface_field || field.value != method { |
| 37 | continue |
| 38 | } |
| 39 | mut params := []string{} |
| 40 | for j in 0 .. field.children_count { |
| 41 | param := a.child_node(field, j) |
| 42 | if param.kind == .param { |
| 43 | params << param.typ |
| 44 | } |
| 45 | } |
| 46 | return params |
| 47 | } |
| 48 | } |
| 49 | return []string{} |
| 50 | } |
| 51 | |
| 52 | fn fn_decl_param_pairs(a &flat.FlatAst, kind flat.NodeKind, name string) []string { |
| 53 | for node in a.nodes { |
| 54 | if node.kind != kind || node.value != name { |
| 55 | continue |
| 56 | } |
| 57 | mut pairs := []string{} |
| 58 | for i in 0 .. node.children_count { |
| 59 | param := a.child_node(&node, i) |
| 60 | if param.kind == .param { |
| 61 | pairs << '${param.value}:${param.typ}' |
| 62 | } |
| 63 | } |
| 64 | return pairs |
| 65 | } |
| 66 | return []string{} |
| 67 | } |
| 68 | |
| 69 | // test_interface_method_generic_type_only_param_is_not_parsed_as_name |
| 70 | // validates this v3 regression case. |
| 71 | fn test_interface_method_generic_type_only_param_is_not_parsed_as_name() { |
| 72 | a := parse_parser_regression_source('interface_generic_param', |
| 73 | 'const max_len = 16\nstruct Result[T] {}\nstruct Node {}\n\ninterface Sink {\n\tput(Result[int])\n\tappend(values []int)\n\tvisit(node &Node)\n\tread(buf [max_len]u8)\n}\n') |
| 74 | assert interface_method_param_types(a, 'Sink', 'put') == ['Result[int]'] |
| 75 | assert interface_method_param_types(a, 'Sink', 'append') == ['[]int'] |
| 76 | assert interface_method_param_types(a, 'Sink', 'visit') == ['&Node'] |
| 77 | assert interface_method_param_types(a, 'Sink', 'read') == ['[max_len]u8'] |
| 78 | } |
| 79 | |
| 80 | fn test_c_function_anonymous_params_are_parsed_as_types() { |
| 81 | a := parse_parser_regression_source('c_anon_params', |
| 82 | 'struct T {}\nstruct C.FILE {}\nstruct C.Widget {}\nstruct C.Node {}\ntype MyHandle = voidptr\n\nfn C.anon(&C.FILE, voidptr, int, &&T, [4]&C.Widget, ?&C.Node, !&C.Node, fn (&C.Node) int) int\nfn C.custom(MyHandle, int, MyHandle) int\nfn C.lower(size_t, int, pthread_t) int\nfn C.named(stream &C.FILE, a, b int) int\nfn C.named_custom(handle MyHandle, a, b int) int\nfn C.named_arrays(m [16]f32, r []rune) int\nfn C.variadic(...int) int\nfn JS.js_anon(JS.Number) JS.Number\nfn JS.js_named(x JS.Number) JS.Number\nfn JS.setInterval(any, int, ...any) int\nfn JS.console.dir(any, any)\nfn JS.named_any(x any) any\nfn ordinary(int) {}\n') |
| 83 | assert fn_decl_param_pairs(a, .c_fn_decl, 'anon') == [ |
| 84 | ':&C.FILE', |
| 85 | ':voidptr', |
| 86 | ':int', |
| 87 | ':&&T', |
| 88 | ':[4]&C.Widget', |
| 89 | ':?&C.Node', |
| 90 | ':!&C.Node', |
| 91 | ':fn(&C.Node) int', |
| 92 | ] |
| 93 | assert fn_decl_param_pairs(a, .c_fn_decl, 'custom') == [ |
| 94 | ':MyHandle', |
| 95 | ':int', |
| 96 | ':MyHandle', |
| 97 | ] |
| 98 | assert fn_decl_param_pairs(a, .c_fn_decl, 'lower') == [ |
| 99 | ':size_t', |
| 100 | ':int', |
| 101 | ':pthread_t', |
| 102 | ] |
| 103 | assert fn_decl_param_pairs(a, .c_fn_decl, 'named') == [ |
| 104 | 'stream:&C.FILE', |
| 105 | 'a:int', |
| 106 | 'b:int', |
| 107 | ] |
| 108 | assert fn_decl_param_pairs(a, .c_fn_decl, 'named_custom') == [ |
| 109 | 'handle:MyHandle', |
| 110 | 'a:int', |
| 111 | 'b:int', |
| 112 | ] |
| 113 | assert fn_decl_param_pairs(a, .c_fn_decl, 'named_arrays') == [ |
| 114 | 'm:[16]f32', |
| 115 | 'r:[]rune', |
| 116 | ] |
| 117 | assert fn_decl_param_pairs(a, .c_fn_decl, 'variadic') == [':...int'] |
| 118 | assert fn_decl_param_pairs(a, .c_fn_decl, 'js_anon') == [':JS.Number'] |
| 119 | assert fn_decl_param_pairs(a, .c_fn_decl, 'js_named') == ['x:JS.Number'] |
| 120 | assert fn_decl_param_pairs(a, .c_fn_decl, 'setInterval') == [ |
| 121 | ':any', |
| 122 | ':int', |
| 123 | ':...any', |
| 124 | ] |
| 125 | assert fn_decl_param_pairs(a, .c_fn_decl, 'console.dir') == [ |
| 126 | ':any', |
| 127 | ':any', |
| 128 | ] |
| 129 | assert fn_decl_param_pairs(a, .c_fn_decl, 'named_any') == ['x:any'] |
| 130 | assert fn_decl_param_pairs(a, .fn_decl, 'ordinary') == ['int:'] |
| 131 | } |
| 132 | |
| 133 | fn test_moduleless_file_does_not_inherit_previous_parser_module() { |
| 134 | a := parse_parser_regression_sources('moduleless_export', [ |
| 135 | 'module expmod\n\nfn helper() {}\n', |
| 136 | "@[export: '1bad']\nfn lonely() {}\n", |
| 137 | ]) |
| 138 | mut tc := types.TypeChecker.new(a) |
| 139 | tc.collect(a) |
| 140 | tc.check_semantics() |
| 141 | assert tc.errors.len == 1, tc.errors.str() |
| 142 | assert tc.errors[0].msg.contains('for `lonely`'), tc.errors.str() |
| 143 | assert !tc.errors[0].msg.contains('expmod.lonely'), tc.errors.str() |
| 144 | assert fn_decl_param_pairs(a, .fn_decl, 'lonely') == [] |
| 145 | } |
| 146 | |
| 147 | fn test_sql_identifier_calls_are_not_parsed_as_sql_expr() { |
| 148 | a := parse_parser_regression_source('sql_identifier_call', |
| 149 | 'fn sql(x int) int {\n\treturn x + 1\n}\n\nfn main() {\n\tx := sql(2)\n\tsql := 1\n\t_ := sql + 2\n}\n') |
| 150 | mut sql_expr_count := 0 |
| 151 | mut call_count := 0 |
| 152 | for node in a.nodes { |
| 153 | if node.kind == .sql_expr { |
| 154 | sql_expr_count++ |
| 155 | } |
| 156 | if node.kind == .call { |
| 157 | call_count++ |
| 158 | } |
| 159 | } |
| 160 | assert sql_expr_count == 0 |
| 161 | assert call_count == 1 |
| 162 | } |
| 163 | |