| author | |
| committer | |
| log | 6fb689e97a4ec05fd97dfa05cc203e9bcb7cffa1 |
| tree | 85f80dde57f698b4e84be75756a33eaf19c5d276 |
| parent | 32ce2f91a92c23d46c6836a6dd68ae0f08bb04c5 |
4 files changed, 209 insertions(+), 151 deletions(-)
lib/std/zig/Ast.zig+21-9| ... | @@ -559,6 +559,7 @@ pub fn firstToken(tree: Ast, node: Node.Index) TokenIndex { | ... | @@ -559,6 +559,7 @@ pub fn firstToken(tree: Ast, node: Node.Index) TokenIndex { |
| 559 | .container_field, | 559 | .container_field, |
| 560 | => { | 560 | => { |
| 561 | const name_token = main_tokens[n]; | 561 | const name_token = main_tokens[n]; |
| 562 | if (token_tags[name_token + 1] != .colon) return name_token - end_offset; | ||
| 562 | if (name_token > 0 and token_tags[name_token - 1] == .keyword_comptime) { | 563 | if (name_token > 0 and token_tags[name_token - 1] == .keyword_comptime) { |
| 563 | end_offset += 1; | 564 | end_offset += 1; |
| 564 | } | 565 | } |
| ... | @@ -1320,33 +1321,39 @@ pub fn containerField(tree: Ast, node: Node.Index) full.ContainerField { | ... | @@ -1320,33 +1321,39 @@ pub fn containerField(tree: Ast, node: Node.Index) full.ContainerField { |
| 1320 | assert(tree.nodes.items(.tag)[node] == .container_field); | 1321 | assert(tree.nodes.items(.tag)[node] == .container_field); |
| 1321 | const data = tree.nodes.items(.data)[node]; | 1322 | const data = tree.nodes.items(.data)[node]; |
| 1322 | const extra = tree.extraData(data.rhs, Node.ContainerField); | 1323 | const extra = tree.extraData(data.rhs, Node.ContainerField); |
| 1324 | const main_token = tree.nodes.items(.main_token)[node]; | ||
| 1323 | return tree.fullContainerField(.{ | 1325 | return tree.fullContainerField(.{ |
| 1324 | .name_token = tree.nodes.items(.main_token)[node], | 1326 | .main_token = main_token, |
| 1325 | .type_expr = data.lhs, | 1327 | .type_expr = data.lhs, |
| 1326 | .value_expr = extra.value_expr, | 1328 | .value_expr = extra.value_expr, |
| 1327 | .align_expr = extra.align_expr, | 1329 | .align_expr = extra.align_expr, |
| 1330 | .tuple_like = tree.tokens.items(.tag)[main_token + 1] != .colon, | ||
| 1328 | }); | 1331 | }); |
| 1329 | } | 1332 | } |
| 1330 | 1333 | ||
| 1331 | pub fn containerFieldInit(tree: Ast, node: Node.Index) full.ContainerField { | 1334 | pub fn containerFieldInit(tree: Ast, node: Node.Index) full.ContainerField { |
| 1332 | assert(tree.nodes.items(.tag)[node] == .container_field_init); | 1335 | assert(tree.nodes.items(.tag)[node] == .container_field_init); |
| 1333 | const data = tree.nodes.items(.data)[node]; | 1336 | const data = tree.nodes.items(.data)[node]; |
| 1337 | const main_token = tree.nodes.items(.main_token)[node]; | ||
| 1334 | return tree.fullContainerField(.{ | 1338 | return tree.fullContainerField(.{ |
| 1335 | .name_token = tree.nodes.items(.main_token)[node], | 1339 | .main_token = main_token, |
| 1336 | .type_expr = data.lhs, | 1340 | .type_expr = data.lhs, |
| 1337 | .value_expr = data.rhs, | 1341 | .value_expr = data.rhs, |
| 1338 | .align_expr = 0, | 1342 | .align_expr = 0, |
| 1343 | .tuple_like = tree.tokens.items(.tag)[main_token + 1] != .colon, | ||
| 1339 | }); | 1344 | }); |
| 1340 | } | 1345 | } |
| 1341 | 1346 | ||
| 1342 | pub fn containerFieldAlign(tree: Ast, node: Node.Index) full.ContainerField { | 1347 | pub fn containerFieldAlign(tree: Ast, node: Node.Index) full.ContainerField { |
| 1343 | assert(tree.nodes.items(.tag)[node] == .container_field_align); | 1348 | assert(tree.nodes.items(.tag)[node] == .container_field_align); |
| 1344 | const data = tree.nodes.items(.data)[node]; | 1349 | const data = tree.nodes.items(.data)[node]; |
| 1350 | const main_token = tree.nodes.items(.main_token)[node]; | ||
| 1345 | return tree.fullContainerField(.{ | 1351 | return tree.fullContainerField(.{ |
| 1346 | .name_token = tree.nodes.items(.main_token)[node], | 1352 | .main_token = main_token, |
| 1347 | .type_expr = data.lhs, | 1353 | .type_expr = data.lhs, |
| 1348 | .value_expr = 0, | 1354 | .value_expr = 0, |
| 1349 | .align_expr = data.rhs, | 1355 | .align_expr = data.rhs, |
| 1356 | .tuple_like = tree.tokens.items(.tag)[main_token + 1] != .colon, | ||
| 1350 | }); | 1357 | }); |
| 1351 | } | 1358 | } |
| 1352 | 1359 | ||
| ... | @@ -1944,10 +1951,14 @@ fn fullContainerField(tree: Ast, info: full.ContainerField.Components) full.Cont | ... | @@ -1944,10 +1951,14 @@ fn fullContainerField(tree: Ast, info: full.ContainerField.Components) full.Cont |
| 1944 | .ast = info, | 1951 | .ast = info, |
| 1945 | .comptime_token = null, | 1952 | .comptime_token = null, |
| 1946 | }; | 1953 | }; |
| 1947 | // comptime name: type = init, | 1954 | if (token_tags[info.main_token] == .keyword_comptime) { |
| 1948 | // ^ | 1955 | // comptime type = init, |
| 1949 | if (info.name_token > 0 and token_tags[info.name_token - 1] == .keyword_comptime) { | 1956 | // ^ |
| 1950 | result.comptime_token = info.name_token - 1; | 1957 | result.comptime_token = info.main_token; |
| 1958 | } else if (info.main_token > 0 and token_tags[info.main_token - 1] == .keyword_comptime) { | ||
| 1959 | // comptime name: type = init, | ||
| 1960 | // ^ | ||
| 1961 | result.comptime_token = info.main_token - 1; | ||
| 1951 | } | 1962 | } |
| 1952 | return result; | 1963 | return result; |
| 1953 | } | 1964 | } |
| ... | @@ -2256,14 +2267,15 @@ pub const full = struct { | ... | @@ -2256,14 +2267,15 @@ pub const full = struct { |
| 2256 | ast: Components, | 2267 | ast: Components, |
| 2257 | 2268 | ||
| 2258 | pub const Components = struct { | 2269 | pub const Components = struct { |
| 2259 | name_token: TokenIndex, | 2270 | main_token: TokenIndex, |
| 2260 | type_expr: Node.Index, | 2271 | type_expr: Node.Index, |
| 2261 | value_expr: Node.Index, | 2272 | value_expr: Node.Index, |
| 2262 | align_expr: Node.Index, | 2273 | align_expr: Node.Index, |
| 2274 | tuple_like: bool, | ||
| 2263 | }; | 2275 | }; |
| 2264 | 2276 | ||
| 2265 | pub fn firstToken(cf: ContainerField) TokenIndex { | 2277 | pub fn firstToken(cf: ContainerField) TokenIndex { |
| 2266 | return cf.comptime_token orelse cf.ast.name_token; | 2278 | return cf.comptime_token orelse cf.ast.main_token; |
| 2267 | } | 2279 | } |
| 2268 | }; | 2280 | }; |
| 2269 | 2281 |
lib/std/zig/parse.zig+107-113| ... | @@ -272,53 +272,6 @@ const Parser = struct { | ... | @@ -272,53 +272,6 @@ const Parser = struct { |
| 272 | trailing = false; | 272 | trailing = false; |
| 273 | }, | 273 | }, |
| 274 | .keyword_comptime => switch (p.token_tags[p.tok_i + 1]) { | 274 | .keyword_comptime => switch (p.token_tags[p.tok_i + 1]) { |
| 275 | .identifier => { | ||
| 276 | p.tok_i += 1; | ||
| 277 | const identifier = p.tok_i; | ||
| 278 | defer last_field = identifier; | ||
| 279 | const container_field = try p.expectContainerFieldRecoverable(); | ||
| 280 | if (container_field != 0) { | ||
| 281 | switch (field_state) { | ||
| 282 | .none => field_state = .seen, | ||
| 283 | .err, .seen => {}, | ||
| 284 | .end => |node| { | ||
| 285 | try p.warnMsg(.{ | ||
| 286 | .tag = .decl_between_fields, | ||
| 287 | .token = p.nodes.items(.main_token)[node], | ||
| 288 | }); | ||
| 289 | try p.warnMsg(.{ | ||
| 290 | .tag = .previous_field, | ||
| 291 | .is_note = true, | ||
| 292 | .token = last_field, | ||
| 293 | }); | ||
| 294 | try p.warnMsg(.{ | ||
| 295 | .tag = .next_field, | ||
| 296 | .is_note = true, | ||
| 297 | .token = identifier, | ||
| 298 | }); | ||
| 299 | // Continue parsing; error will be reported later. | ||
| 300 | field_state = .err; | ||
| 301 | }, | ||
| 302 | } | ||
| 303 | try p.scratch.append(p.gpa, container_field); | ||
| 304 | switch (p.token_tags[p.tok_i]) { | ||
| 305 | .comma => { | ||
| 306 | p.tok_i += 1; | ||
| 307 | trailing = true; | ||
| 308 | continue; | ||
| 309 | }, | ||
| 310 | .r_brace, .eof => { | ||
| 311 | trailing = false; | ||
| 312 | break; | ||
| 313 | }, | ||
| 314 | else => {}, | ||
| 315 | } | ||
| 316 | // There is not allowed to be a decl after a field with no comma. | ||
| 317 | // Report error but recover parser. | ||
| 318 | try p.warn(.expected_comma_after_field); | ||
| 319 | p.findNextContainerMember(); | ||
| 320 | } | ||
| 321 | }, | ||
| 322 | .l_brace => { | 275 | .l_brace => { |
| 323 | if (doc_comment) |some| { | 276 | if (doc_comment) |some| { |
| 324 | try p.warnMsg(.{ .tag = .test_doc_comment, .token = some }); | 277 | try p.warnMsg(.{ .tag = .test_doc_comment, .token = some }); |
| ... | @@ -349,53 +302,15 @@ const Parser = struct { | ... | @@ -349,53 +302,15 @@ const Parser = struct { |
| 349 | }, | 302 | }, |
| 350 | else => { | 303 | else => { |
| 351 | p.tok_i += 1; | 304 | p.tok_i += 1; |
| 352 | try p.warn(.expected_block_or_field); | 305 | const identifier = p.tok_i; |
| 353 | }, | 306 | defer last_field = identifier; |
| 354 | }, | 307 | const container_field = p.expectContainerField() catch |err| switch (err) { |
| 355 | .keyword_pub => { | 308 | error.OutOfMemory => return error.OutOfMemory, |
| 356 | p.tok_i += 1; | 309 | error.ParseError => { |
| 357 | const top_level_decl = try p.expectTopLevelDeclRecoverable(); | 310 | p.findNextContainerMember(); |
| 358 | if (top_level_decl != 0) { | 311 | continue; |
| 359 | if (field_state == .seen) { | 312 | }, |
| 360 | field_state = .{ .end = top_level_decl }; | 313 | }; |
| 361 | } | ||
| 362 | try p.scratch.append(p.gpa, top_level_decl); | ||
| 363 | } | ||
| 364 | trailing = p.token_tags[p.tok_i - 1] == .semicolon; | ||
| 365 | }, | ||
| 366 | .keyword_usingnamespace => { | ||
| 367 | const node = try p.expectUsingNamespaceRecoverable(); | ||
| 368 | if (node != 0) { | ||
| 369 | if (field_state == .seen) { | ||
| 370 | field_state = .{ .end = node }; | ||
| 371 | } | ||
| 372 | try p.scratch.append(p.gpa, node); | ||
| 373 | } | ||
| 374 | trailing = p.token_tags[p.tok_i - 1] == .semicolon; | ||
| 375 | }, | ||
| 376 | .keyword_const, | ||
| 377 | .keyword_var, | ||
| 378 | .keyword_threadlocal, | ||
| 379 | .keyword_export, | ||
| 380 | .keyword_extern, | ||
| 381 | .keyword_inline, | ||
| 382 | .keyword_noinline, | ||
| 383 | .keyword_fn, | ||
| 384 | => { | ||
| 385 | const top_level_decl = try p.expectTopLevelDeclRecoverable(); | ||
| 386 | if (top_level_decl != 0) { | ||
| 387 | if (field_state == .seen) { | ||
| 388 | field_state = .{ .end = top_level_decl }; | ||
| 389 | } | ||
| 390 | try p.scratch.append(p.gpa, top_level_decl); | ||
| 391 | } | ||
| 392 | trailing = p.token_tags[p.tok_i - 1] == .semicolon; | ||
| 393 | }, | ||
| 394 | .identifier => { | ||
| 395 | const identifier = p.tok_i; | ||
| 396 | defer last_field = identifier; | ||
| 397 | const container_field = try p.expectContainerFieldRecoverable(); | ||
| 398 | if (container_field != 0) { | ||
| 399 | switch (field_state) { | 314 | switch (field_state) { |
| 400 | .none => field_state = .seen, | 315 | .none => field_state = .seen, |
| 401 | .err, .seen => {}, | 316 | .err, .seen => {}, |
| ... | @@ -435,7 +350,46 @@ const Parser = struct { | ... | @@ -435,7 +350,46 @@ const Parser = struct { |
| 435 | // Report error but recover parser. | 350 | // Report error but recover parser. |
| 436 | try p.warn(.expected_comma_after_field); | 351 | try p.warn(.expected_comma_after_field); |
| 437 | p.findNextContainerMember(); | 352 | p.findNextContainerMember(); |
| 353 | }, | ||
| 354 | }, | ||
| 355 | .keyword_pub => { | ||
| 356 | p.tok_i += 1; | ||
| 357 | const top_level_decl = try p.expectTopLevelDeclRecoverable(); | ||
| 358 | if (top_level_decl != 0) { | ||
| 359 | if (field_state == .seen) { | ||
| 360 | field_state = .{ .end = top_level_decl }; | ||
| 361 | } | ||
| 362 | try p.scratch.append(p.gpa, top_level_decl); | ||
| 363 | } | ||
| 364 | trailing = p.token_tags[p.tok_i - 1] == .semicolon; | ||
| 365 | }, | ||
| 366 | .keyword_usingnamespace => { | ||
| 367 | const node = try p.expectUsingNamespaceRecoverable(); | ||
| 368 | if (node != 0) { | ||
| 369 | if (field_state == .seen) { | ||
| 370 | field_state = .{ .end = node }; | ||
| 371 | } | ||
| 372 | try p.scratch.append(p.gpa, node); | ||
| 373 | } | ||
| 374 | trailing = p.token_tags[p.tok_i - 1] == .semicolon; | ||
| 375 | }, | ||
| 376 | .keyword_const, | ||
| 377 | .keyword_var, | ||
| 378 | .keyword_threadlocal, | ||
| 379 | .keyword_export, | ||
| 380 | .keyword_extern, | ||
| 381 | .keyword_inline, | ||
| 382 | .keyword_noinline, | ||
| 383 | .keyword_fn, | ||
| 384 | => { | ||
| 385 | const top_level_decl = try p.expectTopLevelDeclRecoverable(); | ||
| 386 | if (top_level_decl != 0) { | ||
| 387 | if (field_state == .seen) { | ||
| 388 | field_state = .{ .end = top_level_decl }; | ||
| 389 | } | ||
| 390 | try p.scratch.append(p.gpa, top_level_decl); | ||
| 438 | } | 391 | } |
| 392 | trailing = p.token_tags[p.tok_i - 1] == .semicolon; | ||
| 439 | }, | 393 | }, |
| 440 | .eof, .r_brace => { | 394 | .eof, .r_brace => { |
| 441 | if (doc_comment) |tok| { | 395 | if (doc_comment) |tok| { |
| ... | @@ -451,11 +405,57 @@ const Parser = struct { | ... | @@ -451,11 +405,57 @@ const Parser = struct { |
| 451 | error.OutOfMemory => return error.OutOfMemory, | 405 | error.OutOfMemory => return error.OutOfMemory, |
| 452 | error.ParseError => false, | 406 | error.ParseError => false, |
| 453 | }; | 407 | }; |
| 454 | if (!c_container) { | 408 | if (c_container) continue; |
| 455 | try p.warn(.expected_container_members); | 409 | |
| 456 | // This was likely not supposed to end yet; try to find the next declaration. | 410 | const identifier = p.tok_i; |
| 457 | p.findNextContainerMember(); | 411 | defer last_field = identifier; |
| 412 | const container_field = p.expectContainerField() catch |err| switch (err) { | ||
| 413 | error.OutOfMemory => return error.OutOfMemory, | ||
| 414 | error.ParseError => { | ||
| 415 | p.findNextContainerMember(); | ||
| 416 | continue; | ||
| 417 | }, | ||
| 418 | }; | ||
| 419 | switch (field_state) { | ||
| 420 | .none => field_state = .seen, | ||
| 421 | .err, .seen => {}, | ||
| 422 | .end => |node| { | ||
| 423 | try p.warnMsg(.{ | ||
| 424 | .tag = .decl_between_fields, | ||
| 425 | .token = p.nodes.items(.main_token)[node], | ||
| 426 | }); | ||
| 427 | try p.warnMsg(.{ | ||
| 428 | .tag = .previous_field, | ||
| 429 | .is_note = true, | ||
| 430 | .token = last_field, | ||
| 431 | }); | ||
| 432 | try p.warnMsg(.{ | ||
| 433 | .tag = .next_field, | ||
| 434 | .is_note = true, | ||
| 435 | .token = identifier, | ||
| 436 | }); | ||
| 437 | // Continue parsing; error will be reported later. | ||
| 438 | field_state = .err; | ||
| 439 | }, | ||
| 458 | } | 440 | } |
| 441 | try p.scratch.append(p.gpa, container_field); | ||
| 442 | switch (p.token_tags[p.tok_i]) { | ||
| 443 | .comma => { | ||
| 444 | p.tok_i += 1; | ||
| 445 | trailing = true; | ||
| 446 | continue; | ||
| 447 | }, | ||
| 448 | .r_brace, .eof => { | ||
| 449 | trailing = false; | ||
| 450 | break; | ||
| 451 | }, | ||
| 452 | else => {}, | ||
| 453 | } | ||
| 454 | // There is not allowed to be a decl after a field with no comma. | ||
| 455 | // Report error but recover parser. | ||
| 456 | try p.warn(.expected_comma_after_field); | ||
| 457 | p.findNextContainerMember(); | ||
| 458 | continue; | ||
| 459 | }, | 459 | }, |
| 460 | } | 460 | } |
| 461 | } | 461 | } |
| ... | @@ -875,12 +875,16 @@ const Parser = struct { | ... | @@ -875,12 +875,16 @@ const Parser = struct { |
| 875 | 875 | ||
| 876 | /// ContainerField <- KEYWORD_comptime? IDENTIFIER (COLON TypeExpr ByteAlign?)? (EQUAL Expr)? | 876 | /// ContainerField <- KEYWORD_comptime? IDENTIFIER (COLON TypeExpr ByteAlign?)? (EQUAL Expr)? |
| 877 | fn expectContainerField(p: *Parser) !Node.Index { | 877 | fn expectContainerField(p: *Parser) !Node.Index { |
| 878 | var main_token = p.tok_i; | ||
| 878 | _ = p.eatToken(.keyword_comptime); | 879 | _ = p.eatToken(.keyword_comptime); |
| 879 | const name_token = p.assertToken(.identifier); | 880 | const tuple_like = p.token_tags[p.tok_i] != .identifier or p.token_tags[p.tok_i + 1] != .colon; |
| 881 | if (!tuple_like) { | ||
| 882 | main_token = p.assertToken(.identifier); | ||
| 883 | } | ||
| 880 | 884 | ||
| 881 | var align_expr: Node.Index = 0; | 885 | var align_expr: Node.Index = 0; |
| 882 | var type_expr: Node.Index = 0; | 886 | var type_expr: Node.Index = 0; |
| 883 | if (p.eatToken(.colon)) |_| { | 887 | if (p.eatToken(.colon) != null or tuple_like) |_| { |
| 884 | type_expr = try p.expectTypeExpr(); | 888 | type_expr = try p.expectTypeExpr(); |
| 885 | align_expr = try p.parseByteAlign(); | 889 | align_expr = try p.parseByteAlign(); |
| 886 | } | 890 | } |
| ... | @@ -890,7 +894,7 @@ const Parser = struct { | ... | @@ -890,7 +894,7 @@ const Parser = struct { |
| 890 | if (align_expr == 0) { | 894 | if (align_expr == 0) { |
| 891 | return p.addNode(.{ | 895 | return p.addNode(.{ |
| 892 | .tag = .container_field_init, | 896 | .tag = .container_field_init, |
| 893 | .main_token = name_token, | 897 | .main_token = main_token, |
| 894 | .data = .{ | 898 | .data = .{ |
| 895 | .lhs = type_expr, | 899 | .lhs = type_expr, |
| 896 | .rhs = value_expr, | 900 | .rhs = value_expr, |
| ... | @@ -899,7 +903,7 @@ const Parser = struct { | ... | @@ -899,7 +903,7 @@ const Parser = struct { |
| 899 | } else if (value_expr == 0) { | 903 | } else if (value_expr == 0) { |
| 900 | return p.addNode(.{ | 904 | return p.addNode(.{ |
| 901 | .tag = .container_field_align, | 905 | .tag = .container_field_align, |
| 902 | .main_token = name_token, | 906 | .main_token = main_token, |
| 903 | .data = .{ | 907 | .data = .{ |
| 904 | .lhs = type_expr, | 908 | .lhs = type_expr, |
| 905 | .rhs = align_expr, | 909 | .rhs = align_expr, |
| ... | @@ -908,7 +912,7 @@ const Parser = struct { | ... | @@ -908,7 +912,7 @@ const Parser = struct { |
| 908 | } else { | 912 | } else { |
| 909 | return p.addNode(.{ | 913 | return p.addNode(.{ |
| 910 | .tag = .container_field, | 914 | .tag = .container_field, |
| 911 | .main_token = name_token, | 915 | .main_token = main_token, |
| 912 | .data = .{ | 916 | .data = .{ |
| 913 | .lhs = type_expr, | 917 | .lhs = type_expr, |
| 914 | .rhs = try p.addExtra(Node.ContainerField{ | 918 | .rhs = try p.addExtra(Node.ContainerField{ |
| ... | @@ -920,16 +924,6 @@ const Parser = struct { | ... | @@ -920,16 +924,6 @@ const Parser = struct { |
| 920 | } | 924 | } |
| 921 | } | 925 | } |
| 922 | 926 | ||
| 923 | fn expectContainerFieldRecoverable(p: *Parser) error{OutOfMemory}!Node.Index { | ||
| 924 | return p.expectContainerField() catch |err| switch (err) { | ||
| 925 | error.OutOfMemory => return error.OutOfMemory, | ||
| 926 | error.ParseError => { | ||
| 927 | p.findNextContainerMember(); | ||
| 928 | return null_node; | ||
| 929 | }, | ||
| 930 | }; | ||
| 931 | } | ||
| 932 | |||
| 933 | /// Statement | 927 | /// Statement |
| 934 | /// <- KEYWORD_comptime? VarDecl | 928 | /// <- KEYWORD_comptime? VarDecl |
| 935 | /// / KEYWORD_comptime BlockExprStatement | 929 | /// / KEYWORD_comptime BlockExprStatement |
lib/std/zig/parser_test.zig+15-11| ... | @@ -1,3 +1,15 @@ | ... | @@ -1,3 +1,15 @@ |
| 1 | test "zig fmt: tuple struct" { | ||
| 2 | try testCanonical( | ||
| 3 | \\const T = struct { | ||
| 4 | \\ comptime u32, | ||
| 5 | \\ *u32 = 1, | ||
| 6 | \\ // needs to be wrapped in parentheses to not be parsed as a function decl | ||
| 7 | \\ (fn () void) align(1), | ||
| 8 | \\}; | ||
| 9 | \\ | ||
| 10 | ); | ||
| 11 | } | ||
| 12 | |||
| 1 | test "zig fmt: preserves clobbers in inline asm with stray comma" { | 13 | test "zig fmt: preserves clobbers in inline asm with stray comma" { |
| 2 | try testCanonical( | 14 | try testCanonical( |
| 3 | \\fn foo() void { | 15 | \\fn foo() void { |
| ... | @@ -265,14 +277,6 @@ test "zig fmt: decl between fields" { | ... | @@ -265,14 +277,6 @@ test "zig fmt: decl between fields" { |
| 265 | }); | 277 | }); |
| 266 | } | 278 | } |
| 267 | 279 | ||
| 268 | test "zig fmt: eof after missing comma" { | ||
| 269 | try testError( | ||
| 270 | \\foo() | ||
| 271 | , &[_]Error{ | ||
| 272 | .expected_comma_after_field, | ||
| 273 | }); | ||
| 274 | } | ||
| 275 | |||
| 276 | test "zig fmt: errdefer with payload" { | 280 | test "zig fmt: errdefer with payload" { |
| 277 | try testCanonical( | 281 | try testCanonical( |
| 278 | \\pub fn main() anyerror!void { | 282 | \\pub fn main() anyerror!void { |
| ... | @@ -5732,8 +5736,8 @@ test "recovery: missing semicolon" { | ... | @@ -5732,8 +5736,8 @@ test "recovery: missing semicolon" { |
| 5732 | test "recovery: invalid container members" { | 5736 | test "recovery: invalid container members" { |
| 5733 | try testError( | 5737 | try testError( |
| 5734 | \\usingnamespace; | 5738 | \\usingnamespace; |
| 5735 | \\foo+ | 5739 | \\@foo()+ |
| 5736 | \\bar@, | 5740 | \\@bar()@, |
| 5737 | \\while (a == 2) { test "" {}} | 5741 | \\while (a == 2) { test "" {}} |
| 5738 | \\test "" { | 5742 | \\test "" { |
| 5739 | \\ a & b | 5743 | \\ a & b |
| ... | @@ -5741,7 +5745,7 @@ test "recovery: invalid container members" { | ... | @@ -5741,7 +5745,7 @@ test "recovery: invalid container members" { |
| 5741 | , &[_]Error{ | 5745 | , &[_]Error{ |
| 5742 | .expected_expr, | 5746 | .expected_expr, |
| 5743 | .expected_comma_after_field, | 5747 | .expected_comma_after_field, |
| 5744 | .expected_container_members, | 5748 | .expected_type_expr, |
| 5745 | .expected_semi_after_stmt, | 5749 | .expected_semi_after_stmt, |
| 5746 | }); | 5750 | }); |
| 5747 | } | 5751 | } |
lib/std/zig/render.zig+66-18| ... | @@ -40,14 +40,34 @@ pub fn renderTree(buffer: *std.ArrayList(u8), tree: Ast) Error!void { | ... | @@ -40,14 +40,34 @@ pub fn renderTree(buffer: *std.ArrayList(u8), tree: Ast) Error!void { |
| 40 | /// Render all members in the given slice, keeping empty lines where appropriate | 40 | /// Render all members in the given slice, keeping empty lines where appropriate |
| 41 | fn renderMembers(gpa: Allocator, ais: *Ais, tree: Ast, members: []const Ast.Node.Index) Error!void { | 41 | fn renderMembers(gpa: Allocator, ais: *Ais, tree: Ast, members: []const Ast.Node.Index) Error!void { |
| 42 | if (members.len == 0) return; | 42 | if (members.len == 0) return; |
| 43 | try renderMember(gpa, ais, tree, members[0], .newline); | 43 | var any_non_tuple_like_fields = false; |
| 44 | for (members) |member| { | ||
| 45 | const tuple_like = switch (tree.nodes.items(.tag)[member]) { | ||
| 46 | .container_field_init => tree.containerFieldInit(member).ast.tuple_like, | ||
| 47 | .container_field_align => tree.containerFieldAlign(member).ast.tuple_like, | ||
| 48 | .container_field => tree.containerField(member).ast.tuple_like, | ||
| 49 | else => continue, | ||
| 50 | }; | ||
| 51 | if (!tuple_like) { | ||
| 52 | any_non_tuple_like_fields = true; | ||
| 53 | break; | ||
| 54 | } | ||
| 55 | } | ||
| 56 | try renderMember(gpa, ais, tree, members[0], any_non_tuple_like_fields, .newline); | ||
| 44 | for (members[1..]) |member| { | 57 | for (members[1..]) |member| { |
| 45 | try renderExtraNewline(ais, tree, member); | 58 | try renderExtraNewline(ais, tree, member); |
| 46 | try renderMember(gpa, ais, tree, member, .newline); | 59 | try renderMember(gpa, ais, tree, member, any_non_tuple_like_fields, .newline); |
| 47 | } | 60 | } |
| 48 | } | 61 | } |
| 49 | 62 | ||
| 50 | fn renderMember(gpa: Allocator, ais: *Ais, tree: Ast, decl: Ast.Node.Index, space: Space) Error!void { | 63 | fn renderMember( |
| 64 | gpa: Allocator, | ||
| 65 | ais: *Ais, | ||
| 66 | tree: Ast, | ||
| 67 | decl: Ast.Node.Index, | ||
| 68 | any_non_tuple_like_fields: bool, | ||
| 69 | space: Space, | ||
| 70 | ) Error!void { | ||
| 51 | const token_tags = tree.tokens.items(.tag); | 71 | const token_tags = tree.tokens.items(.tag); |
| 52 | const main_tokens = tree.nodes.items(.main_token); | 72 | const main_tokens = tree.nodes.items(.main_token); |
| 53 | const datas = tree.nodes.items(.data); | 73 | const datas = tree.nodes.items(.data); |
| ... | @@ -161,9 +181,9 @@ fn renderMember(gpa: Allocator, ais: *Ais, tree: Ast, decl: Ast.Node.Index, spac | ... | @@ -161,9 +181,9 @@ fn renderMember(gpa: Allocator, ais: *Ais, tree: Ast, decl: Ast.Node.Index, spac |
| 161 | try renderExpression(gpa, ais, tree, datas[decl].rhs, space); | 181 | try renderExpression(gpa, ais, tree, datas[decl].rhs, space); |
| 162 | }, | 182 | }, |
| 163 | 183 | ||
| 164 | .container_field_init => return renderContainerField(gpa, ais, tree, tree.containerFieldInit(decl), space), | 184 | .container_field_init => return renderContainerField(gpa, ais, tree, tree.containerFieldInit(decl), any_non_tuple_like_fields, space), |
| 165 | .container_field_align => return renderContainerField(gpa, ais, tree, tree.containerFieldAlign(decl), space), | 185 | .container_field_align => return renderContainerField(gpa, ais, tree, tree.containerFieldAlign(decl), any_non_tuple_like_fields, space), |
| 166 | .container_field => return renderContainerField(gpa, ais, tree, tree.containerField(decl), space), | 186 | .container_field => return renderContainerField(gpa, ais, tree, tree.containerField(decl), any_non_tuple_like_fields, space), |
| 167 | .@"comptime" => return renderExpression(gpa, ais, tree, decl, space), | 187 | .@"comptime" => return renderExpression(gpa, ais, tree, decl, space), |
| 168 | 188 | ||
| 169 | .root => unreachable, | 189 | .root => unreachable, |
| ... | @@ -1158,18 +1178,31 @@ fn renderContainerField( | ... | @@ -1158,18 +1178,31 @@ fn renderContainerField( |
| 1158 | gpa: Allocator, | 1178 | gpa: Allocator, |
| 1159 | ais: *Ais, | 1179 | ais: *Ais, |
| 1160 | tree: Ast, | 1180 | tree: Ast, |
| 1161 | field: Ast.full.ContainerField, | 1181 | field_param: Ast.full.ContainerField, |
| 1182 | any_non_tuple_like_fields: bool, | ||
| 1162 | space: Space, | 1183 | space: Space, |
| 1163 | ) Error!void { | 1184 | ) Error!void { |
| 1185 | var field = field_param; | ||
| 1186 | if (field.ast.tuple_like and any_non_tuple_like_fields and field.ast.type_expr != 0 | ||
| 1187 | and tree.nodes.items(.tag)[field.ast.type_expr] == .identifier | ||
| 1188 | ) { | ||
| 1189 | const ident = tree.nodes.items(.main_token)[field.ast.type_expr]; | ||
| 1190 | field.ast.tuple_like = false; | ||
| 1191 | field.ast.main_token = ident; | ||
| 1192 | field.ast.type_expr = 0; | ||
| 1193 | } | ||
| 1194 | |||
| 1164 | if (field.comptime_token) |t| { | 1195 | if (field.comptime_token) |t| { |
| 1165 | try renderToken(ais, tree, t, .space); // comptime | 1196 | try renderToken(ais, tree, t, .space); // comptime |
| 1166 | } | 1197 | } |
| 1167 | if (field.ast.type_expr == 0 and field.ast.value_expr == 0) { | 1198 | if (field.ast.type_expr == 0 and field.ast.value_expr == 0) { |
| 1168 | return renderIdentifierComma(ais, tree, field.ast.name_token, space, .eagerly_unquote); // name | 1199 | return renderIdentifierComma(ais, tree, field.ast.main_token, space, .eagerly_unquote); // name |
| 1169 | } | 1200 | } |
| 1170 | if (field.ast.type_expr != 0 and field.ast.value_expr == 0) { | 1201 | if (field.ast.type_expr != 0 and field.ast.value_expr == 0) { |
| 1171 | try renderIdentifier(ais, tree, field.ast.name_token, .none, .eagerly_unquote); // name | 1202 | if (!field.ast.tuple_like) { |
| 1172 | try renderToken(ais, tree, field.ast.name_token + 1, .space); // : | 1203 | try renderIdentifier(ais, tree, field.ast.main_token, .none, .eagerly_unquote); // name |
| 1204 | try renderToken(ais, tree, field.ast.main_token + 1, .space); // : | ||
| 1205 | } | ||
| 1173 | 1206 | ||
| 1174 | if (field.ast.align_expr != 0) { | 1207 | if (field.ast.align_expr != 0) { |
| 1175 | try renderExpression(gpa, ais, tree, field.ast.type_expr, .space); // type | 1208 | try renderExpression(gpa, ais, tree, field.ast.type_expr, .space); // type |
| ... | @@ -1184,13 +1217,14 @@ fn renderContainerField( | ... | @@ -1184,13 +1217,14 @@ fn renderContainerField( |
| 1184 | } | 1217 | } |
| 1185 | } | 1218 | } |
| 1186 | if (field.ast.type_expr == 0 and field.ast.value_expr != 0) { | 1219 | if (field.ast.type_expr == 0 and field.ast.value_expr != 0) { |
| 1187 | try renderIdentifier(ais, tree, field.ast.name_token, .space, .eagerly_unquote); // name | 1220 | try renderIdentifier(ais, tree, field.ast.main_token, .space, .eagerly_unquote); // name |
| 1188 | try renderToken(ais, tree, field.ast.name_token + 1, .space); // = | 1221 | try renderToken(ais, tree, field.ast.main_token + 1, .space); // = |
| 1189 | return renderExpressionComma(gpa, ais, tree, field.ast.value_expr, space); // value | 1222 | return renderExpressionComma(gpa, ais, tree, field.ast.value_expr, space); // value |
| 1190 | } | 1223 | } |
| 1191 | 1224 | if (!field.ast.tuple_like) { | |
| 1192 | try renderIdentifier(ais, tree, field.ast.name_token, .none, .eagerly_unquote); // name | 1225 | try renderIdentifier(ais, tree, field.ast.main_token, .none, .eagerly_unquote); // name |
| 1193 | try renderToken(ais, tree, field.ast.name_token + 1, .space); // : | 1226 | try renderToken(ais, tree, field.ast.main_token + 1, .space); // : |
| 1227 | } | ||
| 1194 | try renderExpression(gpa, ais, tree, field.ast.type_expr, .space); // type | 1228 | try renderExpression(gpa, ais, tree, field.ast.type_expr, .space); // type |
| 1195 | 1229 | ||
| 1196 | if (field.ast.align_expr != 0) { | 1230 | if (field.ast.align_expr != 0) { |
| ... | @@ -1901,6 +1935,20 @@ fn renderContainerDecl( | ... | @@ -1901,6 +1935,20 @@ fn renderContainerDecl( |
| 1901 | try renderToken(ais, tree, layout_token, .space); | 1935 | try renderToken(ais, tree, layout_token, .space); |
| 1902 | } | 1936 | } |
| 1903 | 1937 | ||
| 1938 | var any_non_tuple_like_fields = token_tags[container_decl.ast.main_token] != .keyword_struct; | ||
| 1939 | if (!any_non_tuple_like_fields) for (container_decl.ast.members) |member| { | ||
| 1940 | const tuple_like = switch (tree.nodes.items(.tag)[member]) { | ||
| 1941 | .container_field_init => tree.containerFieldInit(member).ast.tuple_like, | ||
| 1942 | .container_field_align => tree.containerFieldAlign(member).ast.tuple_like, | ||
| 1943 | .container_field => tree.containerField(member).ast.tuple_like, | ||
| 1944 | else => continue, | ||
| 1945 | }; | ||
| 1946 | if (!tuple_like) { | ||
| 1947 | any_non_tuple_like_fields = true; | ||
| 1948 | break; | ||
| 1949 | } | ||
| 1950 | }; | ||
| 1951 | |||
| 1904 | var lbrace: Ast.TokenIndex = undefined; | 1952 | var lbrace: Ast.TokenIndex = undefined; |
| 1905 | if (container_decl.ast.enum_token) |enum_token| { | 1953 | if (container_decl.ast.enum_token) |enum_token| { |
| 1906 | try renderToken(ais, tree, container_decl.ast.main_token, .none); // union | 1954 | try renderToken(ais, tree, container_decl.ast.main_token, .none); // union |
| ... | @@ -1967,7 +2015,7 @@ fn renderContainerDecl( | ... | @@ -1967,7 +2015,7 @@ fn renderContainerDecl( |
| 1967 | // Print all the declarations on the same line. | 2015 | // Print all the declarations on the same line. |
| 1968 | try renderToken(ais, tree, lbrace, .space); // lbrace | 2016 | try renderToken(ais, tree, lbrace, .space); // lbrace |
| 1969 | for (container_decl.ast.members) |member| { | 2017 | for (container_decl.ast.members) |member| { |
| 1970 | try renderMember(gpa, ais, tree, member, .space); | 2018 | try renderMember(gpa, ais, tree, member, any_non_tuple_like_fields, .space); |
| 1971 | } | 2019 | } |
| 1972 | return renderToken(ais, tree, rbrace, space); // rbrace | 2020 | return renderToken(ais, tree, rbrace, space); // rbrace |
| 1973 | } | 2021 | } |
| ... | @@ -1985,9 +2033,9 @@ fn renderContainerDecl( | ... | @@ -1985,9 +2033,9 @@ fn renderContainerDecl( |
| 1985 | .container_field_init, | 2033 | .container_field_init, |
| 1986 | .container_field_align, | 2034 | .container_field_align, |
| 1987 | .container_field, | 2035 | .container_field, |
| 1988 | => try renderMember(gpa, ais, tree, member, .comma), | 2036 | => try renderMember(gpa, ais, tree, member, any_non_tuple_like_fields, .comma), |
| 1989 | 2037 | ||
| 1990 | else => try renderMember(gpa, ais, tree, member, .newline), | 2038 | else => try renderMember(gpa, ais, tree, member, any_non_tuple_like_fields, .newline), |
| 1991 | } | 2039 | } |
| 1992 | } | 2040 | } |
| 1993 | ais.popIndent(); | 2041 | ais.popIndent(); |