aboutsummaryrefslogtreecommitdiff
path: root/crates
diff options
context:
space:
mode:
authorAleksey Kladov <[email protected]>2018-08-10 20:33:29 +0100
committerAleksey Kladov <[email protected]>2018-08-10 20:33:29 +0100
commit7c67612b8a894187fa3b64725531a5459f9211bf (patch)
tree9e2a536efa0c880d921fd8d4d74423afc9451fd4 /crates
parent26262aaf05983c5b7f41cc438e287523268fe1eb (diff)
organizize
Diffstat (limited to 'crates')
-rw-r--r--crates/cli/Cargo.toml11
-rw-r--r--crates/cli/src/main.rs95
-rw-r--r--crates/libanalysis/Cargo.toml12
-rw-r--r--crates/libanalysis/src/lib.rs132
-rw-r--r--crates/libeditor/Cargo.toml10
-rw-r--r--crates/libeditor/src/extend_selection.rs36
-rw-r--r--crates/libeditor/src/lib.rs155
-rw-r--r--crates/libeditor/src/line_index.rs62
-rw-r--r--crates/libeditor/tests/test.rs69
-rw-r--r--crates/libsyntax2/Cargo.toml15
-rw-r--r--crates/libsyntax2/src/algo/mod.rs123
-rw-r--r--crates/libsyntax2/src/algo/search.rs136
-rw-r--r--crates/libsyntax2/src/algo/walk.rs45
-rw-r--r--crates/libsyntax2/src/ast/generated.rs54
-rw-r--r--crates/libsyntax2/src/ast/generated.rs.tera22
-rw-r--r--crates/libsyntax2/src/ast/mod.rs74
-rw-r--r--crates/libsyntax2/src/grammar.ron227
-rw-r--r--crates/libsyntax2/src/grammar/attributes.rs79
-rw-r--r--crates/libsyntax2/src/grammar/expressions/atom.rs348
-rw-r--r--crates/libsyntax2/src/grammar/expressions/mod.rs379
-rw-r--r--crates/libsyntax2/src/grammar/items/consts.rs21
-rw-r--r--crates/libsyntax2/src/grammar/items/mod.rs332
-rw-r--r--crates/libsyntax2/src/grammar/items/structs.rs116
-rw-r--r--crates/libsyntax2/src/grammar/items/traits.rs87
-rw-r--r--crates/libsyntax2/src/grammar/items/use_item.rs66
-rw-r--r--crates/libsyntax2/src/grammar/mod.rs161
-rw-r--r--crates/libsyntax2/src/grammar/params.rs116
-rw-r--r--crates/libsyntax2/src/grammar/paths.rs86
-rw-r--r--crates/libsyntax2/src/grammar/patterns.rs204
-rw-r--r--crates/libsyntax2/src/grammar/type_args.rs48
-rw-r--r--crates/libsyntax2/src/grammar/type_params.rs127
-rw-r--r--crates/libsyntax2/src/grammar/types.rs212
-rw-r--r--crates/libsyntax2/src/lexer/classes.rs26
-rw-r--r--crates/libsyntax2/src/lexer/comments.rs57
-rw-r--r--crates/libsyntax2/src/lexer/mod.rs209
-rw-r--r--crates/libsyntax2/src/lexer/numbers.rs67
-rw-r--r--crates/libsyntax2/src/lexer/ptr.rs74
-rw-r--r--crates/libsyntax2/src/lexer/strings.rs106
-rw-r--r--crates/libsyntax2/src/lib.rs55
-rw-r--r--crates/libsyntax2/src/parser_api.rs195
-rw-r--r--crates/libsyntax2/src/parser_impl/event.rs154
-rw-r--r--crates/libsyntax2/src/parser_impl/input.rs86
-rw-r--r--crates/libsyntax2/src/parser_impl/mod.rs170
-rw-r--r--crates/libsyntax2/src/smol_str.rs83
-rw-r--r--crates/libsyntax2/src/syntax_kinds/generated.rs508
-rw-r--r--crates/libsyntax2/src/syntax_kinds/generated.rs.tera73
-rw-r--r--crates/libsyntax2/src/syntax_kinds/mod.rs26
-rw-r--r--crates/libsyntax2/src/utils.rs48
-rw-r--r--crates/libsyntax2/src/yellow/builder.rs65
-rw-r--r--crates/libsyntax2/src/yellow/green.rs95
-rw-r--r--crates/libsyntax2/src/yellow/mod.rs62
-rw-r--r--crates/libsyntax2/src/yellow/red.rs94
-rw-r--r--crates/libsyntax2/src/yellow/syntax.rs122
-rw-r--r--crates/libsyntax2/tests/data/lexer/00012_block_comment.rs4
-rw-r--r--crates/libsyntax2/tests/data/lexer/00012_block_comment.txt7
-rw-r--r--crates/libsyntax2/tests/data/lexer/0001_hello.rs1
-rw-r--r--crates/libsyntax2/tests/data/lexer/0001_hello.txt3
-rw-r--r--crates/libsyntax2/tests/data/lexer/0002_whitespace.rs4
-rw-r--r--crates/libsyntax2/tests/data/lexer/0002_whitespace.txt12
-rw-r--r--crates/libsyntax2/tests/data/lexer/0003_ident.rs1
-rw-r--r--crates/libsyntax2/tests/data/lexer/0003_ident.txt14
-rw-r--r--crates/libsyntax2/tests/data/lexer/0004_numbers.rs9
-rw-r--r--crates/libsyntax2/tests/data/lexer/0004_numbers.txt67
-rw-r--r--crates/libsyntax2/tests/data/lexer/0005_symbols.rs6
-rw-r--r--crates/libsyntax2/tests/data/lexer/0005_symbols.txt68
-rw-r--r--crates/libsyntax2/tests/data/lexer/0006_chars.rs1
-rw-r--r--crates/libsyntax2/tests/data/lexer/0006_chars.txt6
-rw-r--r--crates/libsyntax2/tests/data/lexer/0007_lifetimes.rs1
-rw-r--r--crates/libsyntax2/tests/data/lexer/0007_lifetimes.txt8
-rw-r--r--crates/libsyntax2/tests/data/lexer/0008_byte_strings.rs2
-rw-r--r--crates/libsyntax2/tests/data/lexer/0008_byte_strings.txt14
-rw-r--r--crates/libsyntax2/tests/data/lexer/0009_strings.rs1
-rw-r--r--crates/libsyntax2/tests/data/lexer/0009_strings.txt4
-rw-r--r--crates/libsyntax2/tests/data/lexer/0010_comments.rs3
-rw-r--r--crates/libsyntax2/tests/data/lexer/0010_comments.txt6
-rw-r--r--crates/libsyntax2/tests/data/lexer/0011_keywords.rs3
-rw-r--r--crates/libsyntax2/tests/data/lexer/0011_keywords.txt62
-rw-r--r--crates/libsyntax2/tests/data/parser/err/0000_struct_field_missing_comma.rs4
-rw-r--r--crates/libsyntax2/tests/data/parser/err/0000_struct_field_missing_comma.txt33
-rw-r--r--crates/libsyntax2/tests/data/parser/err/0001_item_recovery_in_file.rs3
-rw-r--r--crates/libsyntax2/tests/data/parser/err/0001_item_recovery_in_file.txt17
-rw-r--r--crates/libsyntax2/tests/data/parser/err/0002_duplicate_shebang.rs2
-rw-r--r--crates/libsyntax2/tests/data/parser/err/0002_duplicate_shebang.txt7
-rw-r--r--crates/libsyntax2/tests/data/parser/err/0003_C++_semicolon.rs4
-rw-r--r--crates/libsyntax2/tests/data/parser/err/0003_C++_semicolon.txt38
-rw-r--r--crates/libsyntax2/tests/data/parser/err/0004_use_path_bad_segment.rs1
-rw-r--r--crates/libsyntax2/tests/data/parser/err/0004_use_path_bad_segment.txt20
-rw-r--r--crates/libsyntax2/tests/data/parser/err/0005_attribute_recover.rs8
-rw-r--r--crates/libsyntax2/tests/data/parser/err/0005_attribute_recover.txt60
-rw-r--r--crates/libsyntax2/tests/data/parser/err/0006_named_field_recovery.rs7
-rw-r--r--crates/libsyntax2/tests/data/parser/err/0006_named_field_recovery.txt73
-rw-r--r--crates/libsyntax2/tests/data/parser/err/0007_stray_curly_in_file.rs9
-rw-r--r--crates/libsyntax2/tests/data/parser/err/0007_stray_curly_in_file.txt32
-rw-r--r--crates/libsyntax2/tests/data/parser/err/0008_item_block_recovery.rs13
-rw-r--r--crates/libsyntax2/tests/data/parser/err/0008_item_block_recovery.txt67
-rw-r--r--crates/libsyntax2/tests/data/parser/err/0009_broken_struct_type_parameter.rs5
-rw-r--r--crates/libsyntax2/tests/data/parser/err/0009_broken_struct_type_parameter.txt44
-rw-r--r--crates/libsyntax2/tests/data/parser/err/0010_unsafe_lambda_block.rs3
-rw-r--r--crates/libsyntax2/tests/data/parser/err/0010_unsafe_lambda_block.txt40
-rw-r--r--crates/libsyntax2/tests/data/parser/err/0011_extern_struct.rs1
-rw-r--r--crates/libsyntax2/tests/data/parser/err/0011_extern_struct.txt13
-rw-r--r--crates/libsyntax2/tests/data/parser/err/0012_broken_lambda.rs12
-rw-r--r--crates/libsyntax2/tests/data/parser/err/0012_broken_lambda.txt387
-rw-r--r--crates/libsyntax2/tests/data/parser/inline/0001_const_unsafe_fn.rs1
-rw-r--r--crates/libsyntax2/tests/data/parser/inline/0001_const_unsafe_fn.txt18
-rw-r--r--crates/libsyntax2/tests/data/parser/inline/0002_const_fn.rs1
-rw-r--r--crates/libsyntax2/tests/data/parser/inline/0002_const_fn.txt16
-rw-r--r--crates/libsyntax2/tests/data/parser/inline/0003_extern_block.rs1
-rw-r--r--crates/libsyntax2/tests/data/parser/inline/0003_extern_block.txt8
-rw-r--r--crates/libsyntax2/tests/data/parser/inline/0004_extern_fn.rs1
-rw-r--r--crates/libsyntax2/tests/data/parser/inline/0004_extern_fn.txt17
-rw-r--r--crates/libsyntax2/tests/data/parser/inline/0005_extern_crate.rs1
-rw-r--r--crates/libsyntax2/tests/data/parser/inline/0005_extern_crate.txt10
-rw-r--r--crates/libsyntax2/tests/data/parser/inline/0007_unsafe_trait.rs1
-rw-r--r--crates/libsyntax2/tests/data/parser/inline/0007_unsafe_trait.txt12
-rw-r--r--crates/libsyntax2/tests/data/parser/inline/0008_unsafe_impl.rs1
-rw-r--r--crates/libsyntax2/tests/data/parser/inline/0008_unsafe_impl.txt15
-rw-r--r--crates/libsyntax2/tests/data/parser/inline/0009_unsafe_auto_trait.rs1
-rw-r--r--crates/libsyntax2/tests/data/parser/inline/0009_unsafe_auto_trait.txt14
-rw-r--r--crates/libsyntax2/tests/data/parser/inline/0010_unsafe_default_impl.rs1
-rw-r--r--crates/libsyntax2/tests/data/parser/inline/0010_unsafe_default_impl.txt17
-rw-r--r--crates/libsyntax2/tests/data/parser/inline/0011_unsafe_fn.rs1
-rw-r--r--crates/libsyntax2/tests/data/parser/inline/0011_unsafe_fn.txt16
-rw-r--r--crates/libsyntax2/tests/data/parser/inline/0012_unsafe_extern_fn.rs1
-rw-r--r--crates/libsyntax2/tests/data/parser/inline/0012_unsafe_extern_fn.txt21
-rw-r--r--crates/libsyntax2/tests/data/parser/inline/0013_unsafe_block_in_mod.rs1
-rw-r--r--crates/libsyntax2/tests/data/parser/inline/0013_unsafe_block_in_mod.txt35
-rw-r--r--crates/libsyntax2/tests/data/parser/inline/0014_type_item_type_params.rs1
-rw-r--r--crates/libsyntax2/tests/data/parser/inline/0014_type_item_type_params.txt20
-rw-r--r--crates/libsyntax2/tests/data/parser/inline/0015_type_item.rs1
-rw-r--r--crates/libsyntax2/tests/data/parser/inline/0015_type_item.txt16
-rw-r--r--crates/libsyntax2/tests/data/parser/inline/0016_type_item_where_clause.rs1
-rw-r--r--crates/libsyntax2/tests/data/parser/inline/0016_type_item_where_clause.txt31
-rw-r--r--crates/libsyntax2/tests/data/parser/inline/0017_paren_type.rs1
-rw-r--r--crates/libsyntax2/tests/data/parser/inline/0017_paren_type.txt19
-rw-r--r--crates/libsyntax2/tests/data/parser/inline/0018_unit_type.rs1
-rw-r--r--crates/libsyntax2/tests/data/parser/inline/0018_unit_type.txt14
-rw-r--r--crates/libsyntax2/tests/data/parser/inline/0019_singleton_tuple_type.rs1
-rw-r--r--crates/libsyntax2/tests/data/parser/inline/0019_singleton_tuple_type.txt20
-rw-r--r--crates/libsyntax2/tests/data/parser/inline/0020_never_type.rs1
-rw-r--r--crates/libsyntax2/tests/data/parser/inline/0020_never_type.txt13
-rw-r--r--crates/libsyntax2/tests/data/parser/inline/0021_pointer_type_no_mutability.rs1
-rw-r--r--crates/libsyntax2/tests/data/parser/inline/0021_pointer_type_no_mutability.txt17
-rw-r--r--crates/libsyntax2/tests/data/parser/inline/0022_pointer_type_mut.rs2
-rw-r--r--crates/libsyntax2/tests/data/parser/inline/0022_pointer_type_mut.txt35
-rw-r--r--crates/libsyntax2/tests/data/parser/inline/0023_array_type_missing_semi.rs1
-rw-r--r--crates/libsyntax2/tests/data/parser/inline/0023_array_type_missing_semi.txt27
-rw-r--r--crates/libsyntax2/tests/data/parser/inline/0024_array_type.rs1
-rw-r--r--crates/libsyntax2/tests/data/parser/inline/0024_array_type.txt21
-rw-r--r--crates/libsyntax2/tests/data/parser/inline/0025_slice_type.rs1
-rw-r--r--crates/libsyntax2/tests/data/parser/inline/0025_slice_type.txt17
-rw-r--r--crates/libsyntax2/tests/data/parser/inline/0026_reference_type;.rs3
-rw-r--r--crates/libsyntax2/tests/data/parser/inline/0026_reference_type;.txt50
-rw-r--r--crates/libsyntax2/tests/data/parser/inline/0027_placeholder_type.rs1
-rw-r--r--crates/libsyntax2/tests/data/parser/inline/0027_placeholder_type.txt13
-rw-r--r--crates/libsyntax2/tests/data/parser/inline/0028_fn_pointer_type.rs3
-rw-r--r--crates/libsyntax2/tests/data/parser/inline/0028_fn_pointer_type.txt55
-rw-r--r--crates/libsyntax2/tests/data/parser/inline/0029_fn_pointer_type_missing_fn.rs1
-rw-r--r--crates/libsyntax2/tests/data/parser/inline/0029_fn_pointer_type_missing_fn.txt23
-rw-r--r--crates/libsyntax2/tests/data/parser/inline/0030_fn_pointer_type_with_ret.rs1
-rw-r--r--crates/libsyntax2/tests/data/parser/inline/0030_fn_pointer_type_with_ret.txt22
-rw-r--r--crates/libsyntax2/tests/data/parser/inline/0031_for_type.rs1
-rw-r--r--crates/libsyntax2/tests/data/parser/inline/0031_for_type.txt30
-rw-r--r--crates/libsyntax2/tests/data/parser/inline/0032_path_type.rs4
-rw-r--r--crates/libsyntax2/tests/data/parser/inline/0032_path_type.txt70
-rw-r--r--crates/libsyntax2/tests/data/parser/inline/0034_bind_pat.rs8
-rw-r--r--crates/libsyntax2/tests/data/parser/inline/0034_bind_pat.txt127
-rw-r--r--crates/libsyntax2/tests/data/parser/inline/0035_ref_pat.rs4
-rw-r--r--crates/libsyntax2/tests/data/parser/inline/0035_ref_pat.txt49
-rw-r--r--crates/libsyntax2/tests/data/parser/inline/0036_placeholder_pat.rs1
-rw-r--r--crates/libsyntax2/tests/data/parser/inline/0036_placeholder_pat.txt28
-rw-r--r--crates/libsyntax2/tests/data/parser/inline/0037_crate_visibility.rs4
-rw-r--r--crates/libsyntax2/tests/data/parser/inline/0037_crate_visibility.txt53
-rw-r--r--crates/libsyntax2/tests/data/parser/inline/0038_function_ret_type.rs2
-rw-r--r--crates/libsyntax2/tests/data/parser/inline/0038_function_ret_type.txt33
-rw-r--r--crates/libsyntax2/tests/data/parser/inline/0039_path_expr.rs6
-rw-r--r--crates/libsyntax2/tests/data/parser/inline/0039_path_expr.txt94
-rw-r--r--crates/libsyntax2/tests/data/parser/inline/0040_expr_literals.rs12
-rw-r--r--crates/libsyntax2/tests/data/parser/inline/0040_expr_literals.txt135
-rw-r--r--crates/libsyntax2/tests/data/parser/inline/0041_type_param_bounds.rs1
-rw-r--r--crates/libsyntax2/tests/data/parser/inline/0041_type_param_bounds.txt34
-rw-r--r--crates/libsyntax2/tests/data/parser/inline/0042_type_param_default.rs1
-rw-r--r--crates/libsyntax2/tests/data/parser/inline/0042_type_param_default.txt22
-rw-r--r--crates/libsyntax2/tests/data/parser/inline/0043_call_expr.rs4
-rw-r--r--crates/libsyntax2/tests/data/parser/inline/0043_call_expr.txt70
-rw-r--r--crates/libsyntax2/tests/data/parser/inline/0044_ref_expr.rs4
-rw-r--r--crates/libsyntax2/tests/data/parser/inline/0044_ref_expr.txt54
-rw-r--r--crates/libsyntax2/tests/data/parser/inline/0045_block.rs4
-rw-r--r--crates/libsyntax2/tests/data/parser/inline/0045_block.txt86
-rw-r--r--crates/libsyntax2/tests/data/parser/inline/0046_default_impl.rs1
-rw-r--r--crates/libsyntax2/tests/data/parser/inline/0046_default_impl.txt15
-rw-r--r--crates/libsyntax2/tests/data/parser/inline/0047_impl_item.rs1
-rw-r--r--crates/libsyntax2/tests/data/parser/inline/0047_impl_item.txt13
-rw-r--r--crates/libsyntax2/tests/data/parser/inline/0048_impl_item_neg.rs1
-rw-r--r--crates/libsyntax2/tests/data/parser/inline/0048_impl_item_neg.txt22
-rw-r--r--crates/libsyntax2/tests/data/parser/inline/0050_let_stmt;.rs6
-rw-r--r--crates/libsyntax2/tests/data/parser/inline/0050_let_stmt;.txt71
-rw-r--r--crates/libsyntax2/tests/data/parser/inline/0051_method_call_expr.rs4
-rw-r--r--crates/libsyntax2/tests/data/parser/inline/0051_method_call_expr.txt62
-rw-r--r--crates/libsyntax2/tests/data/parser/inline/0052_field_expr.rs4
-rw-r--r--crates/libsyntax2/tests/data/parser/inline/0052_field_expr.txt42
-rw-r--r--crates/libsyntax2/tests/data/parser/inline/0053_block_items.rs1
-rw-r--r--crates/libsyntax2/tests/data/parser/inline/0053_block_items.txt28
-rw-r--r--crates/libsyntax2/tests/data/parser/inline/0054_impl_item_items.rs6
-rw-r--r--crates/libsyntax2/tests/data/parser/inline/0054_impl_item_items.txt77
-rw-r--r--crates/libsyntax2/tests/data/parser/inline/0055_self_param.rs6
-rw-r--r--crates/libsyntax2/tests/data/parser/inline/0055_self_param.txt98
-rw-r--r--crates/libsyntax2/tests/data/parser/inline/0056_trait_item.rs1
-rw-r--r--crates/libsyntax2/tests/data/parser/inline/0056_trait_item.txt45
-rw-r--r--crates/libsyntax2/tests/data/parser/inline/0057_auto_trait.rs1
-rw-r--r--crates/libsyntax2/tests/data/parser/inline/0057_auto_trait.txt12
-rw-r--r--crates/libsyntax2/tests/data/parser/inline/0058_type_arg.rs1
-rw-r--r--crates/libsyntax2/tests/data/parser/inline/0058_type_arg.txt40
-rw-r--r--crates/libsyntax2/tests/data/parser/inline/0059_function_where_clause.rs1
-rw-r--r--crates/libsyntax2/tests/data/parser/inline/0059_function_where_clause.txt36
-rw-r--r--crates/libsyntax2/tests/data/parser/inline/0060_function_type_params.rs1
-rw-r--r--crates/libsyntax2/tests/data/parser/inline/0060_function_type_params.txt32
-rw-r--r--crates/libsyntax2/tests/data/parser/inline/0061_struct_lit.rs5
-rw-r--r--crates/libsyntax2/tests/data/parser/inline/0061_struct_lit.txt94
-rw-r--r--crates/libsyntax2/tests/data/parser/inline/0063_impl_trait_type.rs1
-rw-r--r--crates/libsyntax2/tests/data/parser/inline/0063_impl_trait_type.txt39
-rw-r--r--crates/libsyntax2/tests/data/parser/inline/0063_lambda_expr.txt91
-rw-r--r--crates/libsyntax2/tests/data/parser/inline/0064_param_list.rs4
-rw-r--r--crates/libsyntax2/tests/data/parser/inline/0064_param_list.txt99
-rw-r--r--crates/libsyntax2/tests/data/parser/inline/0065_if_expr.rs6
-rw-r--r--crates/libsyntax2/tests/data/parser/inline/0065_if_expr.txt90
-rw-r--r--crates/libsyntax2/tests/data/parser/inline/0066_lambda_expr.rs6
-rw-r--r--crates/libsyntax2/tests/data/parser/inline/0066_lambda_expr.txt93
-rw-r--r--crates/libsyntax2/tests/data/parser/inline/0067_block_expr.rs4
-rw-r--r--crates/libsyntax2/tests/data/parser/inline/0067_block_expr.txt29
-rw-r--r--crates/libsyntax2/tests/data/parser/inline/0068_pub_expr.rs1
-rw-r--r--crates/libsyntax2/tests/data/parser/inline/0068_pub_expr.txt25
-rw-r--r--crates/libsyntax2/tests/data/parser/inline/0068_return_expr.rs4
-rw-r--r--crates/libsyntax2/tests/data/parser/inline/0068_return_expr.txt28
-rw-r--r--crates/libsyntax2/tests/data/parser/inline/0069_match_arm.rs6
-rw-r--r--crates/libsyntax2/tests/data/parser/inline/0069_match_arm.txt65
-rw-r--r--crates/libsyntax2/tests/data/parser/inline/0070_match_expr.rs4
-rw-r--r--crates/libsyntax2/tests/data/parser/inline/0070_match_expr.txt42
-rw-r--r--crates/libsyntax2/tests/data/parser/inline/0071_tuple_pat_fields.rs6
-rw-r--r--crates/libsyntax2/tests/data/parser/inline/0071_tuple_pat_fields.txt103
-rw-r--r--crates/libsyntax2/tests/data/parser/inline/0072_path_part.rs6
-rw-r--r--crates/libsyntax2/tests/data/parser/inline/0072_path_part.txt94
-rw-r--r--crates/libsyntax2/tests/data/parser/inline/0073_struct_pat_fields.rs6
-rw-r--r--crates/libsyntax2/tests/data/parser/inline/0073_struct_pat_fields.txt122
-rw-r--r--crates/libsyntax2/tests/data/parser/inline/0074_unary_expr.rs5
-rw-r--r--crates/libsyntax2/tests/data/parser/inline/0074_unary_expr.txt44
-rw-r--r--crates/libsyntax2/tests/data/parser/inline/0075_try_expr.rs3
-rw-r--r--crates/libsyntax2/tests/data/parser/inline/0075_try_expr.txt25
-rw-r--r--crates/libsyntax2/tests/data/parser/inline/0076_cond.rs1
-rw-r--r--crates/libsyntax2/tests/data/parser/inline/0076_cond.txt42
-rw-r--r--crates/libsyntax2/tests/data/parser/inline/0077_while_expr.rs4
-rw-r--r--crates/libsyntax2/tests/data/parser/inline/0077_while_expr.txt64
-rw-r--r--crates/libsyntax2/tests/data/parser/inline/0078_mod_contents.rs5
-rw-r--r--crates/libsyntax2/tests/data/parser/inline/0078_mod_contents.txt62
-rw-r--r--crates/libsyntax2/tests/data/parser/inline/0079_cast_expr.rs3
-rw-r--r--crates/libsyntax2/tests/data/parser/inline/0079_cast_expr.txt29
-rw-r--r--crates/libsyntax2/tests/data/parser/inline/0080_tuple_expr.rs5
-rw-r--r--crates/libsyntax2/tests/data/parser/inline/0080_tuple_expr.txt38
-rw-r--r--crates/libsyntax2/tests/data/parser/inline/0081_index_expr.rs3
-rw-r--r--crates/libsyntax2/tests/data/parser/inline/0081_index_expr.txt33
-rw-r--r--crates/libsyntax2/tests/data/parser/inline/0082_tuple_pat.rs3
-rw-r--r--crates/libsyntax2/tests/data/parser/inline/0082_tuple_pat.txt40
-rw-r--r--crates/libsyntax2/tests/data/parser/inline/0083_postfix_range.rs1
-rw-r--r--crates/libsyntax2/tests/data/parser/inline/0083_postfix_range.txt30
-rw-r--r--crates/libsyntax2/tests/data/parser/inline/0084_loop_expr.rs3
-rw-r--r--crates/libsyntax2/tests/data/parser/inline/0084_loop_expr.txt24
-rw-r--r--crates/libsyntax2/tests/data/parser/inline/0085_for_expr.rs3
-rw-r--r--crates/libsyntax2/tests/data/parser/inline/0085_for_expr.txt34
-rw-r--r--crates/libsyntax2/tests/data/parser/inline/0085_match_arms_commas.rs7
-rw-r--r--crates/libsyntax2/tests/data/parser/inline/0085_match_arms_commas.txt57
-rw-r--r--crates/libsyntax2/tests/data/parser/inline/0086_array_expr.rs6
-rw-r--r--crates/libsyntax2/tests/data/parser/inline/0086_array_expr.txt54
-rw-r--r--crates/libsyntax2/tests/data/parser/inline/0086_no_semi_after_block.rs9
-rw-r--r--crates/libsyntax2/tests/data/parser/inline/0086_no_semi_after_block.txt82
-rw-r--r--crates/libsyntax2/tests/data/parser/inline/0087_stmt_postfix_expr_ambiguity.rs7
-rw-r--r--crates/libsyntax2/tests/data/parser/inline/0087_stmt_postfix_expr_ambiguity.txt58
-rw-r--r--crates/libsyntax2/tests/data/parser/inline/0088_stmt_bin_expr_ambiguity.rs4
-rw-r--r--crates/libsyntax2/tests/data/parser/inline/0088_stmt_bin_expr_ambiguity.txt50
-rw-r--r--crates/libsyntax2/tests/data/parser/inline/0089_slice_pat.rs3
-rw-r--r--crates/libsyntax2/tests/data/parser/inline/0089_slice_pat.txt40
-rw-r--r--crates/libsyntax2/tests/data/parser/inline/0090_trait_item_items.rs6
-rw-r--r--crates/libsyntax2/tests/data/parser/inline/0090_trait_item_items.txt67
-rw-r--r--crates/libsyntax2/tests/data/parser/inline/0091_fn_decl.rs1
-rw-r--r--crates/libsyntax2/tests/data/parser/inline/0091_fn_decl.txt21
-rw-r--r--crates/libsyntax2/tests/data/parser/inline/0092_literal_pattern.rs7
-rw-r--r--crates/libsyntax2/tests/data/parser/inline/0092_literal_pattern.txt59
-rw-r--r--crates/libsyntax2/tests/data/parser/inline/0093_path_fn_trait_args.rs1
-rw-r--r--crates/libsyntax2/tests/data/parser/inline/0093_path_fn_trait_args.txt45
-rw-r--r--crates/libsyntax2/tests/data/parser/inline/0094_range_pat.rs3
-rw-r--r--crates/libsyntax2/tests/data/parser/inline/0094_range_pat.txt41
-rw-r--r--crates/libsyntax2/tests/data/parser/inline/0095_path_type_with_bounds.rs1
-rw-r--r--crates/libsyntax2/tests/data/parser/inline/0095_path_type_with_bounds.txt35
-rw-r--r--crates/libsyntax2/tests/data/parser/inline/0096_value_parameters_no_patterns.rs1
-rw-r--r--crates/libsyntax2/tests/data/parser/inline/0096_value_parameters_no_patterns.txt81
-rw-r--r--crates/libsyntax2/tests/data/parser/inline/0097_param_list_opt_patterns.rs1
-rw-r--r--crates/libsyntax2/tests/data/parser/inline/0097_param_list_opt_patterns.txt43
-rw-r--r--crates/libsyntax2/tests/data/parser/inline/0098_where_clause.rs6
-rw-r--r--crates/libsyntax2/tests/data/parser/inline/0098_where_clause.txt69
-rw-r--r--crates/libsyntax2/tests/data/parser/inline/0099_crate_keyword_vis.rs1
-rw-r--r--crates/libsyntax2/tests/data/parser/inline/0099_crate_keyword_vis.txt18
-rw-r--r--crates/libsyntax2/tests/data/parser/ok/0000_empty.rs0
-rw-r--r--crates/libsyntax2/tests/data/parser/ok/0000_empty.txt1
-rw-r--r--crates/libsyntax2/tests/data/parser/ok/0001_struct_item.rs3
-rw-r--r--crates/libsyntax2/tests/data/parser/ok/0001_struct_item.txt10
-rw-r--r--crates/libsyntax2/tests/data/parser/ok/0002_struct_item_field.rs3
-rw-r--r--crates/libsyntax2/tests/data/parser/ok/0002_struct_item_field.txt21
-rw-r--r--crates/libsyntax2/tests/data/parser/ok/0004_file_shebang.rs1
-rw-r--r--crates/libsyntax2/tests/data/parser/ok/0004_file_shebang.txt2
-rw-r--r--crates/libsyntax2/tests/data/parser/ok/0005_fn_item.rs2
-rw-r--r--crates/libsyntax2/tests/data/parser/ok/0005_fn_item.txt15
-rw-r--r--crates/libsyntax2/tests/data/parser/ok/0006_inner_attributes.rs10
-rw-r--r--crates/libsyntax2/tests/data/parser/ok/0006_inner_attributes.txt176
-rw-r--r--crates/libsyntax2/tests/data/parser/ok/0007_extern_crate.rs2
-rw-r--r--crates/libsyntax2/tests/data/parser/ok/0007_extern_crate.txt25
-rw-r--r--crates/libsyntax2/tests/data/parser/ok/0008_mod_item.rs17
-rw-r--r--crates/libsyntax2/tests/data/parser/ok/0008_mod_item.txt85
-rw-r--r--crates/libsyntax2/tests/data/parser/ok/0009_use_item.rs2
-rw-r--r--crates/libsyntax2/tests/data/parser/ok/0009_use_item.txt21
-rw-r--r--crates/libsyntax2/tests/data/parser/ok/0010_use_path_segments.rs2
-rw-r--r--crates/libsyntax2/tests/data/parser/ok/0010_use_path_segments.txt42
-rw-r--r--crates/libsyntax2/tests/data/parser/ok/0011_outer_attribute.rs3
-rw-r--r--crates/libsyntax2/tests/data/parser/ok/0011_outer_attribute.txt32
-rw-r--r--crates/libsyntax2/tests/data/parser/ok/0012_visibility.rs5
-rw-r--r--crates/libsyntax2/tests/data/parser/ok/0012_visibility.txt102
-rw-r--r--crates/libsyntax2/tests/data/parser/ok/0013_use_path_self_super.rs3
-rw-r--r--crates/libsyntax2/tests/data/parser/ok/0013_use_path_self_super.txt57
-rw-r--r--crates/libsyntax2/tests/data/parser/ok/0014_use_tree.rs7
-rw-r--r--crates/libsyntax2/tests/data/parser/ok/0014_use_tree.txt91
-rw-r--r--crates/libsyntax2/tests/data/parser/ok/0015_use_tree.rs2
-rw-r--r--crates/libsyntax2/tests/data/parser/ok/0015_use_tree.txt64
-rw-r--r--crates/libsyntax2/tests/data/parser/ok/0016_struct_flavors.rs10
-rw-r--r--crates/libsyntax2/tests/data/parser/ok/0016_struct_flavors.txt89
-rw-r--r--crates/libsyntax2/tests/data/parser/ok/0017_attr_trailing_comma.rs2
-rw-r--r--crates/libsyntax2/tests/data/parser/ok/0017_attr_trailing_comma.txt26
-rw-r--r--crates/libsyntax2/tests/data/parser/ok/0018_struct_type_params.rs17
-rw-r--r--crates/libsyntax2/tests/data/parser/ok/0018_struct_type_params.txt255
-rw-r--r--crates/libsyntax2/tests/data/parser/ok/0019_enums.rs25
-rw-r--r--crates/libsyntax2/tests/data/parser/ok/0019_enums.txt146
-rw-r--r--crates/libsyntax2/tests/data/parser/ok/0020_type_param_bounds.rs9
-rw-r--r--crates/libsyntax2/tests/data/parser/ok/0020_type_param_bounds.txt193
-rw-r--r--crates/libsyntax2/tests/data/parser/ok/0021_extern_fn.rs8
-rw-r--r--crates/libsyntax2/tests/data/parser/ok/0021_extern_fn.txt56
-rw-r--r--crates/libsyntax2/tests/data/parser/ok/0022_empty_extern_block.rs5
-rw-r--r--crates/libsyntax2/tests/data/parser/ok/0022_empty_extern_block.txt19
-rw-r--r--crates/libsyntax2/tests/data/parser/ok/0023_static_items.rs2
-rw-r--r--crates/libsyntax2/tests/data/parser/ok/0023_static_items.txt41
-rw-r--r--crates/libsyntax2/tests/data/parser/ok/0024_const_item.rs2
-rw-r--r--crates/libsyntax2/tests/data/parser/ok/0024_const_item.txt41
-rw-r--r--crates/libsyntax2/tests/data/parser/ok/0025_extern_fn_in_block.rs3
-rw-r--r--crates/libsyntax2/tests/data/parser/ok/0025_extern_fn_in_block.txt31
-rw-r--r--crates/libsyntax2/tests/data/parser/ok/0026_const_fn_in_block.rs3
-rw-r--r--crates/libsyntax2/tests/data/parser/ok/0026_const_fn_in_block.txt30
-rw-r--r--crates/libsyntax2/tests/data/parser/ok/0027_unsafe_fn_in_block.rs4
-rw-r--r--crates/libsyntax2/tests/data/parser/ok/0027_unsafe_fn_in_block.txt40
-rw-r--r--crates/libsyntax2/tests/data/parser/ok/0028_operator_binding_power.rs14
-rw-r--r--crates/libsyntax2/tests/data/parser/ok/0028_operator_binding_power.txt185
-rw-r--r--crates/libsyntax2/tests/data/parser/ok/0029_range_forms.rs6
-rw-r--r--crates/libsyntax2/tests/data/parser/ok/0029_range_forms.txt83
-rw-r--r--crates/libsyntax2/tests/lexer.rs28
-rw-r--r--crates/libsyntax2/tests/parser.rs14
-rw-r--r--crates/libsyntax2/tests/testutils/Cargo.toml7
-rw-r--r--crates/libsyntax2/tests/testutils/src/lib.rs111
-rw-r--r--crates/server/Cargo.toml18
-rw-r--r--crates/server/src/caps.rs36
-rw-r--r--crates/server/src/dispatch.rs174
-rw-r--r--crates/server/src/handlers.rs61
-rw-r--r--crates/server/src/io.rs202
-rw-r--r--crates/server/src/main.rs249
-rw-r--r--crates/server/src/req.rs41
-rw-r--r--crates/tools/Cargo.toml14
-rw-r--r--crates/tools/src/lib.rs43
-rw-r--r--crates/tools/src/main.rs216
372 files changed, 15286 insertions, 0 deletions
diff --git a/crates/cli/Cargo.toml b/crates/cli/Cargo.toml
new file mode 100644
index 000000000..ac89a48d3
--- /dev/null
+++ b/crates/cli/Cargo.toml
@@ -0,0 +1,11 @@
1[package]
2name = "cli"
3version = "0.1.0"
4authors = ["Aleksey Kladov <[email protected]>"]
5publish = false
6
7[dependencies]
8clap = "2.32.0"
9failure = "0.1.1"
10libeditor = { path = "../libeditor" }
11tools = { path = "../tools" }
diff --git a/crates/cli/src/main.rs b/crates/cli/src/main.rs
new file mode 100644
index 000000000..45e0a1e4f
--- /dev/null
+++ b/crates/cli/src/main.rs
@@ -0,0 +1,95 @@
1extern crate clap;
2#[macro_use]
3extern crate failure;
4extern crate libeditor;
5extern crate tools;
6
7use std::{
8 fs, io::Read, path::Path,
9 time::Instant
10};
11use clap::{App, Arg, SubCommand};
12use tools::collect_tests;
13use libeditor::{ast, syntax_tree, symbols};
14
15type Result<T> = ::std::result::Result<T, failure::Error>;
16
17fn main() -> Result<()> {
18 let matches = App::new("libsyntax2-cli")
19 .setting(clap::AppSettings::SubcommandRequiredElseHelp)
20 .subcommand(
21 SubCommand::with_name("render-test")
22 .arg(
23 Arg::with_name("line")
24 .long("--line")
25 .required(true)
26 .takes_value(true),
27 )
28 .arg(
29 Arg::with_name("file")
30 .long("--file")
31 .required(true)
32 .takes_value(true),
33 ),
34 )
35 .subcommand(
36 SubCommand::with_name("parse")
37 .arg(Arg::with_name("no-dump").long("--no-dump"))
38 )
39 .subcommand(SubCommand::with_name("symbols"))
40 .get_matches();
41 match matches.subcommand() {
42 ("parse", Some(matches)) => {
43 let start = Instant::now();
44 let file = file()?;
45 let elapsed = start.elapsed();
46 if !matches.is_present("no-dump") {
47 println!("{}", syntax_tree(&file));
48 }
49 eprintln!("parsing: {:?}", elapsed);
50 ::std::mem::forget(file);
51 }
52 ("symbols", _) => {
53 let file = file()?;
54 for s in symbols(&file) {
55 println!("{:?}", s);
56 }
57 }
58 ("render-test", Some(matches)) => {
59 let file = matches.value_of("file").unwrap();
60 let file = Path::new(file);
61 let line: usize = matches.value_of("line").unwrap().parse()?;
62 let line = line - 1;
63 let (test, tree) = render_test(file, line)?;
64 println!("{}\n{}", test, tree);
65 }
66 _ => unreachable!(),
67 }
68 Ok(())
69}
70
71fn file() -> Result<ast::File> {
72 let text = read_stdin()?;
73 Ok(ast::File::parse(&text))
74}
75
76fn read_stdin() -> Result<String> {
77 let mut buff = String::new();
78 ::std::io::stdin().read_to_string(&mut buff)?;
79 Ok(buff)
80}
81
82fn render_test(file: &Path, line: usize) -> Result<(String, String)> {
83 let text = fs::read_to_string(file)?;
84 let tests = collect_tests(&text);
85 let test = tests.into_iter().find(|(start_line, t)| {
86 *start_line <= line && line <= *start_line + t.text.lines().count()
87 });
88 let test = match test {
89 None => bail!("No test found at line {} at {}", line, file.display()),
90 Some((_start_line, test)) => test,
91 };
92 let file = ast::File::parse(&test.text);
93 let tree = syntax_tree(&file);
94 Ok((test.text, tree))
95}
diff --git a/crates/libanalysis/Cargo.toml b/crates/libanalysis/Cargo.toml
new file mode 100644
index 000000000..c773f4211
--- /dev/null
+++ b/crates/libanalysis/Cargo.toml
@@ -0,0 +1,12 @@
1[package]
2name = "libanalysis"
3version = "0.1.0"
4authors = ["Aleksey Kladov <[email protected]>"]
5
6[dependencies]
7log = "0.4.2"
8failure = "0.1.2"
9parking_lot = "0.6.3"
10once_cell = "0.1.4"
11libsyntax2 = { path = "../libsyntax2" }
12libeditor = { path = "../libeditor" }
diff --git a/crates/libanalysis/src/lib.rs b/crates/libanalysis/src/lib.rs
new file mode 100644
index 000000000..6a946a0b0
--- /dev/null
+++ b/crates/libanalysis/src/lib.rs
@@ -0,0 +1,132 @@
1extern crate failure;
2extern crate parking_lot;
3#[macro_use]
4extern crate log;
5extern crate once_cell;
6extern crate libsyntax2;
7extern crate libeditor;
8
9use once_cell::sync::OnceCell;
10
11use std::{
12 fs,
13 sync::Arc,
14 collections::hash_map::HashMap,
15 path::{PathBuf, Path},
16};
17use parking_lot::RwLock;
18use libsyntax2::ast;
19use libeditor::LineIndex;
20
21pub type Result<T> = ::std::result::Result<T, ::failure::Error>;
22
23pub struct WorldState {
24 data: Arc<WorldData>
25}
26
27pub struct World {
28 data: Arc<WorldData>,
29}
30
31impl WorldState {
32 pub fn new() -> WorldState {
33 WorldState {
34 data: Arc::new(WorldData::default())
35 }
36 }
37
38 pub fn snapshot(&self) -> World {
39 World { data: self.data.clone() }
40 }
41
42 pub fn change_overlay(&mut self, path: PathBuf, text: Option<String>) {
43 let data = self.data_mut();
44 data.file_map.get_mut().remove(&path);
45 if let Some(text) = text {
46 data.mem_map.insert(path, Arc::new(text));
47 } else {
48 data.mem_map.remove(&path);
49 }
50 }
51
52 fn data_mut(&mut self) -> &mut WorldData {
53 if Arc::get_mut(&mut self.data).is_none() {
54 let file_map = self.data.file_map.read().clone();
55 self.data = Arc::new(WorldData {
56 mem_map: self.data.mem_map.clone(),
57 file_map: RwLock::new(file_map),
58 });
59 }
60 Arc::get_mut(&mut self.data).unwrap()
61 }
62}
63
64
65impl World {
66 pub fn file_syntax(&self, path: &Path) -> Result<ast::File> {
67 let data = self.file_data(path)?;
68 let syntax = data.syntax
69 .get_or_init(|| {
70 trace!("parsing: {}", path.display());
71 ast::File::parse(self.file_text(path, &data))
72 }).clone();
73 Ok(syntax)
74 }
75
76 pub fn file_line_index(&self, path: &Path) -> Result<LineIndex> {
77 let data = self.file_data(path)?;
78 let index = data.lines
79 .get_or_init(|| {
80 trace!("calc line index: {}", path.display());
81 LineIndex::new(self.file_text(path, &data))
82 });
83 Ok(index.clone())
84 }
85
86 fn file_text<'a>(&'a self, path: &Path, file_data: &'a FileData) -> &'a str {
87 match file_data.text.as_ref() {
88 Some(text) => text.as_str(),
89 None => self.data.mem_map[path].as_str()
90 }
91 }
92
93 fn file_data(&self, path: &Path) -> Result<Arc<FileData>> {
94 {
95 let guard = self.data.file_map.read();
96 if let Some(data) = guard.get(path) {
97 return Ok(data.clone());
98 }
99 }
100
101 let text = if self.data.mem_map.contains_key(path) {
102 None
103 } else {
104 trace!("loading file from disk: {}", path.display());
105 Some(fs::read_to_string(path)?)
106 };
107 let res = {
108 let mut guard = self.data.file_map.write();
109 guard.entry(path.to_owned())
110 .or_insert_with(|| Arc::new(FileData {
111 text,
112 syntax: OnceCell::new(),
113 lines: OnceCell::new(),
114 }))
115 .clone()
116 };
117 Ok(res)
118 }
119}
120
121
122#[derive(Default)]
123struct WorldData {
124 mem_map: HashMap<PathBuf, Arc<String>>,
125 file_map: RwLock<HashMap<PathBuf, Arc<FileData>>>,
126}
127
128struct FileData {
129 text: Option<String>,
130 syntax: OnceCell<ast::File>,
131 lines: OnceCell<LineIndex>,
132}
diff --git a/crates/libeditor/Cargo.toml b/crates/libeditor/Cargo.toml
new file mode 100644
index 000000000..d6423979b
--- /dev/null
+++ b/crates/libeditor/Cargo.toml
@@ -0,0 +1,10 @@
1[package]
2name = "libeditor"
3version = "0.1.0"
4authors = ["Aleksey Kladov <[email protected]>"]
5publish = false
6
7[dependencies]
8itertools = "0.7.8"
9superslice = "0.1.0"
10libsyntax2 = { path = "../libsyntax2" }
diff --git a/crates/libeditor/src/extend_selection.rs b/crates/libeditor/src/extend_selection.rs
new file mode 100644
index 000000000..16d4bc084
--- /dev/null
+++ b/crates/libeditor/src/extend_selection.rs
@@ -0,0 +1,36 @@
1use libsyntax2::{
2 TextRange, SyntaxNodeRef,
3 SyntaxKind::WHITESPACE,
4 algo::{find_leaf_at_offset, find_covering_node, ancestors},
5};
6
7
8pub(crate) fn extend_selection(root: SyntaxNodeRef, range: TextRange) -> Option<TextRange> {
9 if range.is_empty() {
10 let offset = range.start();
11 let mut leaves = find_leaf_at_offset(root, offset);
12 if let Some(leaf) = leaves.clone().find(|node| node.kind() != WHITESPACE) {
13 return Some(leaf.range());
14 }
15 let ws = leaves.next()?;
16// let ws_suffix = file.text().slice(
17// TextRange::from_to(offset, ws.range().end())
18// );
19// if ws.text().contains("\n") && !ws_suffix.contains("\n") {
20// if let Some(line_end) = file.text()
21// .slice(TextSuffix::from(ws.range().end()))
22// .find("\n")
23// {
24// let range = TextRange::from_len(ws.range().end(), line_end);
25// return Some(find_covering_node(file.root(), range).range());
26// }
27// }
28 return Some(ws.range());
29 };
30 let node = find_covering_node(root, range);
31
32 match ancestors(node).skip_while(|n| n.range() == range).next() {
33 None => None,
34 Some(parent) => Some(parent.range()),
35 }
36}
diff --git a/crates/libeditor/src/lib.rs b/crates/libeditor/src/lib.rs
new file mode 100644
index 000000000..f77647338
--- /dev/null
+++ b/crates/libeditor/src/lib.rs
@@ -0,0 +1,155 @@
1extern crate libsyntax2;
2extern crate superslice;
3
4mod extend_selection;
5mod line_index;
6
7use libsyntax2::{
8 SyntaxNodeRef, AstNode,
9 algo::walk,
10 SyntaxKind::*,
11};
12pub use libsyntax2::{TextRange, TextUnit, ast};
13pub use self::line_index::{LineIndex, LineCol};
14
15#[derive(Debug)]
16pub struct HighlightedRange {
17 pub range: TextRange,
18 pub tag: &'static str,
19}
20
21#[derive(Debug)]
22pub struct Diagnostic {
23 pub range: TextRange,
24 pub msg: String,
25}
26
27#[derive(Debug)]
28pub struct Symbol {
29 // pub parent: ???,
30 pub name: String,
31 pub range: TextRange,
32}
33
34#[derive(Debug)]
35pub struct Runnable {
36 pub range: TextRange,
37 pub kind: RunnableKind,
38}
39
40#[derive(Debug)]
41pub enum RunnableKind {
42 Test { name: String },
43 Bin,
44}
45
46pub fn highlight(file: &ast::File) -> Vec<HighlightedRange> {
47 let syntax = file.syntax();
48 let mut res = Vec::new();
49 for node in walk::preorder(syntax.as_ref()) {
50 let tag = match node.kind() {
51 ERROR => "error",
52 COMMENT | DOC_COMMENT => "comment",
53 STRING | RAW_STRING | RAW_BYTE_STRING | BYTE_STRING => "string",
54 ATTR => "attribute",
55 NAME_REF => "text",
56 NAME => "function",
57 INT_NUMBER | FLOAT_NUMBER | CHAR | BYTE => "literal",
58 LIFETIME => "parameter",
59 k if k.is_keyword() => "keyword",
60 _ => continue,
61 };
62 res.push(HighlightedRange {
63 range: node.range(),
64 tag,
65 })
66 }
67 res
68}
69
70pub fn diagnostics(file: &ast::File) -> Vec<Diagnostic> {
71 let syntax = file.syntax();
72 let mut res = Vec::new();
73
74 for node in walk::preorder(syntax.as_ref()) {
75 if node.kind() == ERROR {
76 res.push(Diagnostic {
77 range: node.range(),
78 msg: "Syntax Error".to_string(),
79 });
80 }
81 }
82 res.extend(file.errors().into_iter().map(|err| Diagnostic {
83 range: TextRange::offset_len(err.offset, 1.into()),
84 msg: err.msg,
85 }));
86 res
87}
88
89pub fn syntax_tree(file: &ast::File) -> String {
90 ::libsyntax2::utils::dump_tree(&file.syntax())
91}
92
93pub fn symbols(file: &ast::File) -> Vec<Symbol> {
94 let syntax = file.syntax();
95 let res: Vec<Symbol> = walk::preorder(syntax.as_ref())
96 .filter_map(Declaration::cast)
97 .filter_map(|decl| {
98 let name = decl.name()?;
99 let range = decl.range();
100 Some(Symbol { name, range })
101 })
102 .collect();
103 res // NLL :-(
104}
105
106pub fn extend_selection(file: &ast::File, range: TextRange) -> Option<TextRange> {
107 let syntax = file.syntax();
108 extend_selection::extend_selection(syntax.as_ref(), range)
109}
110
111pub fn runnables(file: &ast::File) -> Vec<Runnable> {
112 file
113 .functions()
114 .filter_map(|f| {
115 let name = f.name()?.text();
116 let kind = if name == "main" {
117 RunnableKind::Bin
118 } else if f.has_atom_attr("test") {
119 RunnableKind::Test {
120 name: name.to_string()
121 }
122 } else {
123 return None;
124 };
125 Some(Runnable {
126 range: f.syntax().range(),
127 kind,
128 })
129 })
130 .collect()
131}
132
133
134struct Declaration<'f> (SyntaxNodeRef<'f>);
135
136impl<'f> Declaration<'f> {
137 fn cast(node: SyntaxNodeRef<'f>) -> Option<Declaration<'f>> {
138 match node.kind() {
139 | STRUCT_ITEM | ENUM_ITEM | FUNCTION | TRAIT_ITEM
140 | CONST_ITEM | STATIC_ITEM | MOD_ITEM | NAMED_FIELD
141 | TYPE_ITEM => Some(Declaration(node)),
142 _ => None
143 }
144 }
145
146 fn name(&self) -> Option<String> {
147 let name = self.0.children()
148 .find(|child| child.kind() == NAME)?;
149 Some(name.text())
150 }
151
152 fn range(&self) -> TextRange {
153 self.0.range()
154 }
155}
diff --git a/crates/libeditor/src/line_index.rs b/crates/libeditor/src/line_index.rs
new file mode 100644
index 000000000..801726aa5
--- /dev/null
+++ b/crates/libeditor/src/line_index.rs
@@ -0,0 +1,62 @@
1use superslice::Ext;
2use ::TextUnit;
3
4#[derive(Clone, Debug)]
5pub struct LineIndex {
6 newlines: Vec<TextUnit>,
7}
8
9#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
10pub struct LineCol {
11 pub line: u32,
12 pub col: TextUnit,
13}
14
15impl LineIndex {
16 pub fn new(text: &str) -> LineIndex {
17 let mut newlines = vec![0.into()];
18 let mut curr = 0.into();
19 for c in text.chars() {
20 curr += TextUnit::of_char(c);
21 if c == '\n' {
22 newlines.push(curr);
23 }
24 }
25 LineIndex { newlines }
26 }
27
28 pub fn line_col(&self, offset: TextUnit) -> LineCol {
29 let line = self.newlines.upper_bound(&offset) - 1;
30 let line_start_offset = self.newlines[line];
31 let col = offset - line_start_offset;
32 return LineCol { line: line as u32, col };
33 }
34
35 pub fn offset(&self, line_col: LineCol) -> TextUnit {
36 //TODO: return Result
37 self.newlines[line_col.line as usize] + line_col.col
38 }
39}
40
41#[test]
42fn test_line_index() {
43 let text = "hello\nworld";
44 let index = LineIndex::new(text);
45 assert_eq!(index.line_col(0.into()), LineCol { line: 0, col: 0.into() });
46 assert_eq!(index.line_col(1.into()), LineCol { line: 0, col: 1.into() });
47 assert_eq!(index.line_col(5.into()), LineCol { line: 0, col: 5.into() });
48 assert_eq!(index.line_col(6.into()), LineCol { line: 1, col: 0.into() });
49 assert_eq!(index.line_col(7.into()), LineCol { line: 1, col: 1.into() });
50 assert_eq!(index.line_col(8.into()), LineCol { line: 1, col: 2.into() });
51 assert_eq!(index.line_col(10.into()), LineCol { line: 1, col: 4.into() });
52 assert_eq!(index.line_col(11.into()), LineCol { line: 1, col: 5.into() });
53 assert_eq!(index.line_col(12.into()), LineCol { line: 1, col: 6.into() });
54
55 let text = "\nhello\nworld";
56 let index = LineIndex::new(text);
57 assert_eq!(index.line_col(0.into()), LineCol { line: 0, col: 0.into() });
58 assert_eq!(index.line_col(1.into()), LineCol { line: 1, col: 0.into() });
59 assert_eq!(index.line_col(2.into()), LineCol { line: 1, col: 1.into() });
60 assert_eq!(index.line_col(6.into()), LineCol { line: 1, col: 5.into() });
61 assert_eq!(index.line_col(7.into()), LineCol { line: 2, col: 0.into() });
62}
diff --git a/crates/libeditor/tests/test.rs b/crates/libeditor/tests/test.rs
new file mode 100644
index 000000000..2a84c5080
--- /dev/null
+++ b/crates/libeditor/tests/test.rs
@@ -0,0 +1,69 @@
1extern crate libeditor;
2extern crate itertools;
3
4use std::fmt;
5use itertools::Itertools;
6use libeditor::{ast, highlight, runnables, extend_selection, TextRange};
7
8#[test]
9fn test_extend_selection() {
10 let file = file(r#"fn foo() {
11 1 + 1
12}
13"#);
14 let range = TextRange::offset_len(18.into(), 0.into());
15 let range = extend_selection(&file, range).unwrap();
16 assert_eq!(range, TextRange::from_to(17.into(), 18.into()));
17 let range = extend_selection(&file, range).unwrap();
18 assert_eq!(range, TextRange::from_to(15.into(), 20.into()));
19}
20
21#[test]
22fn test_highlighting() {
23 let file = file(r#"
24// comment
25fn main() {}
26 println!("Hello, {}!", 92);
27"#);
28 let hls = highlight(&file);
29 dbg_eq(
30 &hls,
31 r#"[HighlightedRange { range: [1; 11), tag: "comment" },
32 HighlightedRange { range: [12; 14), tag: "keyword" },
33 HighlightedRange { range: [15; 19), tag: "function" },
34 HighlightedRange { range: [29; 36), tag: "text" },
35 HighlightedRange { range: [38; 50), tag: "string" },
36 HighlightedRange { range: [52; 54), tag: "literal" }]"#
37 );
38}
39
40#[test]
41fn test_runnables() {
42 let file = file(r#"
43fn main() {}
44
45#[test]
46fn test_foo() {}
47
48#[test]
49#[ignore]
50fn test_foo() {}
51"#);
52 let runnables = runnables(&file);
53 dbg_eq(
54 &runnables,
55 r#"[Runnable { range: [1; 13), kind: Bin },
56 Runnable { range: [15; 39), kind: Test { name: "test_foo" } },
57 Runnable { range: [41; 75), kind: Test { name: "test_foo" } }]"#,
58 )
59}
60
61fn file(text: &str) -> ast::File {
62 ast::File::parse(text)
63}
64
65fn dbg_eq(actual: &impl fmt::Debug, expected: &str) {
66 let actual = format!("{:?}", actual);
67 let expected = expected.lines().map(|l| l.trim()).join(" ");
68 assert_eq!(actual, expected);
69}
diff --git a/crates/libsyntax2/Cargo.toml b/crates/libsyntax2/Cargo.toml
new file mode 100644
index 000000000..f67735540
--- /dev/null
+++ b/crates/libsyntax2/Cargo.toml
@@ -0,0 +1,15 @@
1[package]
2name = "libsyntax2"
3version = "0.1.0"
4authors = ["Aleksey Kladov <[email protected]>"]
5license = "MIT OR Apache-2.0"
6
7[dependencies]
8unicode-xid = "0.1.0"
9text_unit = "0.1.2"
10itertools = "0.7.5"
11drop_bomb = "0.1.4"
12parking_lot = "0.6.0"
13
14[dev-dependencies]
15testutils = { path = "./tests/testutils" }
diff --git a/crates/libsyntax2/src/algo/mod.rs b/crates/libsyntax2/src/algo/mod.rs
new file mode 100644
index 000000000..d2de70fd4
--- /dev/null
+++ b/crates/libsyntax2/src/algo/mod.rs
@@ -0,0 +1,123 @@
1pub mod walk;
2
3use {SyntaxNodeRef, TextUnit, TextRange};
4
5pub fn find_leaf_at_offset(node: SyntaxNodeRef, offset: TextUnit) -> LeafAtOffset {
6 let range = node.range();
7 assert!(
8 contains_offset_nonstrict(range, offset),
9 "Bad offset: range {:?} offset {:?}", range, offset
10 );
11 if range.is_empty() {
12 return LeafAtOffset::None;
13 }
14
15 if node.is_leaf() {
16 return LeafAtOffset::Single(node);
17 }
18
19 let mut children = node.children()
20 .filter(|child| {
21 let child_range = child.range();
22 !child_range.is_empty() && contains_offset_nonstrict(child_range, offset)
23 });
24
25 let left = children.next().unwrap();
26 let right = children.next();
27 assert!(children.next().is_none());
28 return if let Some(right) = right {
29 match (find_leaf_at_offset(left, offset), find_leaf_at_offset(right, offset)) {
30 (LeafAtOffset::Single(left), LeafAtOffset::Single(right)) =>
31 LeafAtOffset::Between(left, right),
32 _ => unreachable!()
33 }
34 } else {
35 find_leaf_at_offset(left, offset)
36 };
37}
38
39#[derive(Clone, Copy, Debug)]
40pub enum LeafAtOffset<'a> {
41 None,
42 Single(SyntaxNodeRef<'a>),
43 Between(SyntaxNodeRef<'a>, SyntaxNodeRef<'a>)
44}
45
46impl<'a> LeafAtOffset<'a> {
47 pub fn right_biased(self) -> Option<SyntaxNodeRef<'a>> {
48 match self {
49 LeafAtOffset::None => None,
50 LeafAtOffset::Single(node) => Some(node),
51 LeafAtOffset::Between(_, right) => Some(right)
52 }
53 }
54
55 pub fn left_biased(self) -> Option<SyntaxNodeRef<'a>> {
56 match self {
57 LeafAtOffset::None => None,
58 LeafAtOffset::Single(node) => Some(node),
59 LeafAtOffset::Between(left, _) => Some(left)
60 }
61 }
62}
63
64impl<'f> Iterator for LeafAtOffset<'f> {
65 type Item = SyntaxNodeRef<'f>;
66
67 fn next(&mut self) -> Option<SyntaxNodeRef<'f>> {
68 match *self {
69 LeafAtOffset::None => None,
70 LeafAtOffset::Single(node) => { *self = LeafAtOffset::None; Some(node) }
71 LeafAtOffset::Between(left, right) => { *self = LeafAtOffset::Single(right); Some(left) }
72 }
73 }
74}
75
76
77pub fn find_covering_node(root: SyntaxNodeRef, range: TextRange) -> SyntaxNodeRef {
78 assert!(is_subrange(root.range(), range));
79 let (left, right) = match (
80 find_leaf_at_offset(root, range.start()).right_biased(),
81 find_leaf_at_offset(root, range.end()).left_biased()
82 ) {
83 (Some(l), Some(r)) => (l, r),
84 _ => return root
85 };
86
87 common_ancestor(left, right)
88}
89
90fn common_ancestor<'a>(n1: SyntaxNodeRef<'a>, n2: SyntaxNodeRef<'a>) -> SyntaxNodeRef<'a> {
91 for p in ancestors(n1) {
92 if ancestors(n2).any(|a| a == p) {
93 return p;
94 }
95 }
96 panic!("Can't find common ancestor of {:?} and {:?}", n1, n2)
97}
98
99pub fn ancestors<'a>(node: SyntaxNodeRef<'a>) -> impl Iterator<Item=SyntaxNodeRef<'a>> {
100 Ancestors(Some(node))
101}
102
103#[derive(Debug)]
104struct Ancestors<'a>(Option<SyntaxNodeRef<'a>>);
105
106impl<'a> Iterator for Ancestors<'a> {
107 type Item = SyntaxNodeRef<'a>;
108
109 fn next(&mut self) -> Option<Self::Item> {
110 self.0.take().map(|n| {
111 self.0 = n.parent();
112 n
113 })
114 }
115}
116
117fn contains_offset_nonstrict(range: TextRange, offset: TextUnit) -> bool {
118 range.start() <= offset && offset <= range.end()
119}
120
121fn is_subrange(range: TextRange, subrange: TextRange) -> bool {
122 range.start() <= subrange.start() && subrange.end() <= range.end()
123}
diff --git a/crates/libsyntax2/src/algo/search.rs b/crates/libsyntax2/src/algo/search.rs
new file mode 100644
index 000000000..46404f537
--- /dev/null
+++ b/crates/libsyntax2/src/algo/search.rs
@@ -0,0 +1,136 @@
1use {Node, NodeType, TextUnit, TextRange};
2use ::visitor::{visitor, process_subtree_bottom_up};
3
4pub fn child_of_type(node: Node, ty: NodeType) -> Option<Node> {
5 node.children().find(|n| n.ty() == ty)
6}
7
8pub fn children_of_type<'f>(node: Node<'f>, ty: NodeType) -> Box<Iterator<Item=Node<'f>> + 'f> {
9 Box::new(node.children().filter(move |n| n.ty() == ty))
10}
11
12pub fn subtree<'f>(node: Node<'f>) -> Box<Iterator<Item=Node<'f>> + 'f> {
13 Box::new(node.children().flat_map(subtree).chain(::std::iter::once(node)))
14}
15
16pub fn descendants_of_type<'f>(node: Node<'f>, ty: NodeType) -> Vec<Node<'f>> {
17 process_subtree_bottom_up(
18 node,
19 visitor(Vec::new())
20 .visit_nodes(&[ty], |node, nodes| nodes.push(node))
21 )
22}
23
24pub fn child_of_type_exn(node: Node, ty: NodeType) -> Node {
25 child_of_type(node, ty).unwrap_or_else(|| {
26 panic!("No child of type {:?} for {:?}\
27 ----\
28 {}\
29 ----", ty, node.ty(), node.text())
30 })
31}
32
33
34pub fn ancestors(node: Node) -> Ancestors {
35 Ancestors(Some(node))
36}
37
38pub struct Ancestors<'f>(Option<Node<'f>>);
39
40impl<'f> Iterator for Ancestors<'f> {
41 type Item = Node<'f>;
42
43 fn next(&mut self) -> Option<Self::Item> {
44 let current = self.0;
45 self.0 = current.and_then(|n| n.parent());
46 current
47 }
48}
49
50pub fn is_leaf(node: Node) -> bool {
51 node.children().next().is_none() && !node.range().is_empty()
52}
53
54
55#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
56pub enum Direction {
57 Left, Right
58}
59
60pub fn sibling(node: Node, dir: Direction) -> Option<Node> {
61 let (parent, idx) = child_position(node)?;
62 let idx = match dir {
63 Direction::Left => idx.checked_sub(1)?,
64 Direction::Right => idx + 1,
65 };
66 parent.children().nth(idx)
67}
68
69pub mod ast {
70 use {Node, AstNode, TextUnit, AstChildren};
71 use visitor::{visitor, process_subtree_bottom_up};
72 use super::{ancestors, find_leaf_at_offset, LeafAtOffset};
73
74 pub fn ancestor<'f, T: AstNode<'f>>(node: Node<'f>) -> Option<T> {
75 ancestors(node)
76 .filter_map(T::wrap)
77 .next()
78 }
79
80 pub fn ancestor_exn<'f, T: AstNode<'f>>(node: Node<'f>) -> T {
81 ancestor(node).unwrap()
82 }
83
84 pub fn children_of_type<'f, N: AstNode<'f>>(node: Node<'f>) -> AstChildren<N> {
85 AstChildren::new(node.children())
86 }
87
88 pub fn descendants_of_type<'f, N: AstNode<'f>>(node: Node<'f>) -> Vec<N> {
89 process_subtree_bottom_up(
90 node,
91 visitor(Vec::new())
92 .visit::<N, _>(|node, acc| acc.push(node))
93 )
94 }
95
96 pub fn node_at_offset<'f, T: AstNode<'f>>(node: Node<'f>, offset: TextUnit) -> Option<T> {
97 match find_leaf_at_offset(node, offset) {
98 LeafAtOffset::None => None,
99 LeafAtOffset::Single(node) => ancestor(node),
100 LeafAtOffset::Between(left, right) => ancestor(left).or_else(|| ancestor(right)),
101 }
102 }
103}
104
105pub mod traversal {
106 use {Node};
107
108 pub fn bottom_up<'f, F: FnMut(Node<'f>)>(node: Node<'f>, mut f: F)
109 {
110 go(node, &mut f);
111
112 fn go<'f, F: FnMut(Node<'f>)>(node: Node<'f>, f: &mut F) {
113 for child in node.children() {
114 go(child, f)
115 }
116 f(node);
117 }
118 }
119}
120
121fn child_position(child: Node) -> Option<(Node, usize)> {
122 child.parent()
123 .map(|parent| {
124 (parent, parent.children().position(|n| n == child).unwrap())
125 })
126}
127
128fn common_ancestor<'f>(n1: Node<'f>, n2: Node<'f>) -> Node<'f> {
129 for p in ancestors(n1) {
130 if ancestors(n2).any(|a| a == p) {
131 return p;
132 }
133 }
134 panic!("Can't find common ancestor of {:?} and {:?}", n1, n2)
135}
136
diff --git a/crates/libsyntax2/src/algo/walk.rs b/crates/libsyntax2/src/algo/walk.rs
new file mode 100644
index 000000000..a50ec2a09
--- /dev/null
+++ b/crates/libsyntax2/src/algo/walk.rs
@@ -0,0 +1,45 @@
1use SyntaxNodeRef;
2
3pub fn preorder<'a>(root: SyntaxNodeRef<'a>) -> impl Iterator<Item = SyntaxNodeRef<'a>> {
4 walk(root).filter_map(|event| match event {
5 WalkEvent::Enter(node) => Some(node),
6 WalkEvent::Exit(_) => None,
7 })
8}
9
10#[derive(Debug, Copy, Clone)]
11pub enum WalkEvent<'a> {
12 Enter(SyntaxNodeRef<'a>),
13 Exit(SyntaxNodeRef<'a>),
14}
15
16pub fn walk<'a>(root: SyntaxNodeRef<'a>) -> impl Iterator<Item = WalkEvent<'a>> {
17 let mut done = false;
18 ::itertools::unfold(WalkEvent::Enter(root), move |pos| {
19 if done {
20 return None;
21 }
22 let res = *pos;
23 *pos = match *pos {
24 WalkEvent::Enter(node) => match node.first_child() {
25 Some(child) => WalkEvent::Enter(child),
26 None => WalkEvent::Exit(node),
27 },
28 WalkEvent::Exit(node) => {
29 if node == root {
30 done = true;
31 WalkEvent::Exit(node)
32 } else {
33 match node.next_sibling() {
34 Some(sibling) => WalkEvent::Enter(sibling),
35 None => match node.parent() {
36 Some(node) => WalkEvent::Exit(node),
37 None => WalkEvent::Exit(node),
38 },
39 }
40 }
41 }
42 };
43 Some(res)
44 })
45}
diff --git a/crates/libsyntax2/src/ast/generated.rs b/crates/libsyntax2/src/ast/generated.rs
new file mode 100644
index 000000000..2f813050a
--- /dev/null
+++ b/crates/libsyntax2/src/ast/generated.rs
@@ -0,0 +1,54 @@
1use std::sync::Arc;
2use {
3 SyntaxNode, SyntaxRoot, TreeRoot, AstNode,
4 SyntaxKind::*,
5};
6
7
8#[derive(Debug, Clone, Copy)]
9pub struct File<R: TreeRoot = Arc<SyntaxRoot>> {
10 syntax: SyntaxNode<R>,
11}
12
13impl<R: TreeRoot> AstNode<R> for File<R> {
14 fn cast(syntax: SyntaxNode<R>) -> Option<Self> {
15 match syntax.kind() {
16 FILE => Some(File { syntax }),
17 _ => None,
18 }
19 }
20 fn syntax(&self) -> &SyntaxNode<R> { &self.syntax }
21}
22
23
24#[derive(Debug, Clone, Copy)]
25pub struct Function<R: TreeRoot = Arc<SyntaxRoot>> {
26 syntax: SyntaxNode<R>,
27}
28
29impl<R: TreeRoot> AstNode<R> for Function<R> {
30 fn cast(syntax: SyntaxNode<R>) -> Option<Self> {
31 match syntax.kind() {
32 FUNCTION => Some(Function { syntax }),
33 _ => None,
34 }
35 }
36 fn syntax(&self) -> &SyntaxNode<R> { &self.syntax }
37}
38
39
40#[derive(Debug, Clone, Copy)]
41pub struct Name<R: TreeRoot = Arc<SyntaxRoot>> {
42 syntax: SyntaxNode<R>,
43}
44
45impl<R: TreeRoot> AstNode<R> for Name<R> {
46 fn cast(syntax: SyntaxNode<R>) -> Option<Self> {
47 match syntax.kind() {
48 NAME => Some(Name { syntax }),
49 _ => None,
50 }
51 }
52 fn syntax(&self) -> &SyntaxNode<R> { &self.syntax }
53}
54
diff --git a/crates/libsyntax2/src/ast/generated.rs.tera b/crates/libsyntax2/src/ast/generated.rs.tera
new file mode 100644
index 000000000..242837801
--- /dev/null
+++ b/crates/libsyntax2/src/ast/generated.rs.tera
@@ -0,0 +1,22 @@
1use std::sync::Arc;
2use {
3 SyntaxNode, SyntaxRoot, TreeRoot, AstNode,
4 SyntaxKind::*,
5};
6{% for node in ast %}
7{% set Name = node.kind | camel %}
8#[derive(Debug, Clone, Copy)]
9pub struct {{ Name }}<R: TreeRoot = Arc<SyntaxRoot>> {
10 syntax: SyntaxNode<R>,
11}
12
13impl<R: TreeRoot> AstNode<R> for {{ Name }}<R> {
14 fn cast(syntax: SyntaxNode<R>) -> Option<Self> {
15 match syntax.kind() {
16 {{ node.kind }} => Some({{ Name }} { syntax }),
17 _ => None,
18 }
19 }
20 fn syntax(&self) -> &SyntaxNode<R> { &self.syntax }
21}
22{% endfor %}
diff --git a/crates/libsyntax2/src/ast/mod.rs b/crates/libsyntax2/src/ast/mod.rs
new file mode 100644
index 000000000..eeb7ae6f6
--- /dev/null
+++ b/crates/libsyntax2/src/ast/mod.rs
@@ -0,0 +1,74 @@
1mod generated;
2
3use std::sync::Arc;
4use {
5 SyntaxNode, SyntaxRoot, TreeRoot, SyntaxError,
6 SyntaxKind::*,
7};
8pub use self::generated::*;
9
10pub trait AstNode<R: TreeRoot>: Sized {
11 fn cast(syntax: SyntaxNode<R>) -> Option<Self>;
12 fn syntax(&self) -> &SyntaxNode<R>;
13}
14
15impl File<Arc<SyntaxRoot>> {
16 pub fn parse(text: &str) -> Self {
17 File::cast(::parse(text)).unwrap()
18 }
19}
20
21impl<R: TreeRoot> File<R> {
22 pub fn errors(&self) -> Vec<SyntaxError> {
23 self.syntax().root.errors.clone()
24 }
25
26 pub fn functions<'a>(&'a self) -> impl Iterator<Item = Function<R>> + 'a {
27 self.syntax()
28 .children()
29 .filter_map(Function::cast)
30 }
31}
32
33impl<R: TreeRoot> Function<R> {
34 pub fn name(&self) -> Option<Name<R>> {
35 self.syntax()
36 .children()
37 .filter_map(Name::cast)
38 .next()
39 }
40
41 pub fn has_atom_attr(&self, atom: &str) -> bool {
42 self.syntax()
43 .children()
44 .filter(|node| node.kind() == ATTR)
45 .any(|attr| {
46 let mut metas = attr.children().filter(|node| node.kind() == META_ITEM);
47 let meta = match metas.next() {
48 None => return false,
49 Some(meta) => {
50 if metas.next().is_some() {
51 return false;
52 }
53 meta
54 }
55 };
56 let mut children = meta.children();
57 match children.next() {
58 None => false,
59 Some(child) => {
60 if children.next().is_some() {
61 return false;
62 }
63 child.kind() == IDENT && child.text() == atom
64 }
65 }
66 })
67 }
68}
69
70impl<R: TreeRoot> Name<R> {
71 pub fn text(&self) -> String {
72 self.syntax().text()
73 }
74}
diff --git a/crates/libsyntax2/src/grammar.ron b/crates/libsyntax2/src/grammar.ron
new file mode 100644
index 000000000..bcc79843a
--- /dev/null
+++ b/crates/libsyntax2/src/grammar.ron
@@ -0,0 +1,227 @@
1Grammar(
2 single_byte_tokens: [
3 [";", "SEMI"],
4 [",", "COMMA"],
5 ["(", "L_PAREN"],
6 [")", "R_PAREN"],
7 ["{", "L_CURLY"],
8 ["}", "R_CURLY"],
9 ["[", "L_BRACK"],
10 ["]", "R_BRACK"],
11 ["<", "L_ANGLE"],
12 [">", "R_ANGLE"],
13 ["@", "AT"],
14 ["#", "POUND"],
15 ["~", "TILDE"],
16 ["?", "QUESTION"],
17 ["$", "DOLLAR"],
18 ["&", "AMP"],
19 ["|", "PIPE"],
20 ["+", "PLUS"],
21 ["*", "STAR"],
22 ["/", "SLASH"],
23 ["^", "CARET"],
24 ["%", "PERCENT"],
25 ],
26 multi_byte_tokens: [
27 [".", "DOT"],
28 ["..", "DOTDOT"],
29 ["...", "DOTDOTDOT"],
30 ["..=", "DOTDOTEQ"],
31 [":", "COLON"],
32 ["::", "COLONCOLON"],
33 ["=", "EQ"],
34 ["==", "EQEQ"],
35 ["=>", "FAT_ARROW"],
36 ["!", "EXCL"],
37 ["!=", "NEQ"],
38 ["-", "MINUS"],
39 ["->", "THIN_ARROW"],
40 ["<=", "LTEQ"],
41 [">=", "GTEQ"],
42 ["+=", "PLUSEQ"],
43 ["-=", "MINUSEQ"],
44 ["&&", "AMPAMP"],
45 ["||", "PIPEPIPE"],
46 ["<<", "SHL"],
47 [">>", "SHR"],
48 ["<<=", "SHLEQ"],
49 [">>=", "SHREQ"],
50 ],
51 keywords: [
52 "use",
53 "fn",
54 "struct",
55 "enum",
56 "trait",
57 "impl",
58 "true",
59 "false",
60 "as",
61 "extern",
62 "crate",
63 "mod",
64 "pub",
65 "self",
66 "super",
67 "in",
68 "where",
69 "for",
70 "loop",
71 "while",
72 "if",
73 "else",
74 "match",
75 "const",
76 "static",
77 "mut",
78 "unsafe",
79 "type",
80 "ref",
81 "let",
82 "move",
83 "return",
84 ],
85 contextual_keywords: [
86 "auto",
87 "default",
88 "union",
89 ],
90 tokens: [
91 "ERROR",
92 "IDENT",
93 "UNDERSCORE",
94 "WHITESPACE",
95 "INT_NUMBER",
96 "FLOAT_NUMBER",
97 "LIFETIME",
98 "CHAR",
99 "BYTE",
100 "STRING",
101 "RAW_STRING",
102 "BYTE_STRING",
103 "RAW_BYTE_STRING",
104 "COMMENT",
105 "DOC_COMMENT",
106 "SHEBANG",
107 ],
108 nodes: [
109 "FILE",
110
111 "STRUCT_ITEM",
112 "ENUM_ITEM",
113 "FUNCTION",
114 "EXTERN_CRATE_ITEM",
115 "MOD_ITEM",
116 "USE_ITEM",
117 "STATIC_ITEM",
118 "CONST_ITEM",
119 "TRAIT_ITEM",
120 "IMPL_ITEM",
121 "TYPE_ITEM",
122 "MACRO_CALL",
123 "TOKEN_TREE",
124
125 "PAREN_TYPE",
126 "TUPLE_TYPE",
127 "NEVER_TYPE",
128 "PATH_TYPE",
129 "POINTER_TYPE",
130 "ARRAY_TYPE",
131 "SLICE_TYPE",
132 "REFERENCE_TYPE",
133 "PLACEHOLDER_TYPE",
134 "FN_POINTER_TYPE",
135 "FOR_TYPE",
136 "IMPL_TRAIT_TYPE",
137
138 "REF_PAT",
139 "BIND_PAT",
140 "PLACEHOLDER_PAT",
141 "PATH_PAT",
142 "STRUCT_PAT",
143 "TUPLE_STRUCT_PAT",
144 "TUPLE_PAT",
145 "SLICE_PAT",
146 "RANGE_PAT",
147
148 // atoms
149 "TUPLE_EXPR",
150 "ARRAY_EXPR",
151 "PAREN_EXPR",
152 "PATH_EXPR",
153 "LAMBDA_EXPR",
154 "IF_EXPR",
155 "WHILE_EXPR",
156 "LOOP_EXPR",
157 "FOR_EXPR",
158 "BLOCK_EXPR",
159 "RETURN_EXPR",
160 "MATCH_EXPR",
161 "MATCH_ARM",
162 "MATCH_GUARD",
163 "STRUCT_LIT",
164 "STRUCT_LIT_FIELD",
165
166 // postfix
167 "CALL_EXPR",
168 "INDEX_EXPR",
169 "METHOD_CALL_EXPR",
170 "FIELD_EXPR",
171 "TRY_EXPR",
172 "CAST_EXPR",
173
174 // unary
175 "REF_EXPR",
176 "PREFIX_EXPR",
177
178 "RANGE_EXPR", // just weird
179 "BIN_EXPR",
180
181
182 "EXTERN_BLOCK_EXPR",
183 "ENUM_VARIANT",
184 "NAMED_FIELD",
185 "POS_FIELD",
186 "ATTR",
187 "META_ITEM", // not an item actually
188 "USE_TREE",
189 "PATH",
190 "PATH_SEGMENT",
191 "LITERAL",
192 "ALIAS",
193 "VISIBILITY",
194 "WHERE_CLAUSE",
195 "WHERE_PRED",
196 "ABI",
197 "NAME",
198 "NAME_REF",
199
200 "LET_STMT",
201 "EXPR_STMT",
202
203 "TYPE_PARAM_LIST",
204 "LIFETIME_PARAM",
205 "TYPE_PARAM",
206 "TYPE_ARG_LIST",
207 "LIFETIME_ARG",
208 "TYPE_ARG",
209 "ASSOC_TYPE_ARG",
210
211 "PARAM_LIST",
212 "PARAM",
213 "SELF_PARAM",
214 "ARG_LIST",
215 ],
216 ast: [
217 (
218 kind: "FILE"
219 ),
220 (
221 kind: "FUNCTION"
222 ),
223 (
224 kind: "NAME"
225 ),
226 ]
227)
diff --git a/crates/libsyntax2/src/grammar/attributes.rs b/crates/libsyntax2/src/grammar/attributes.rs
new file mode 100644
index 000000000..c411d4d7f
--- /dev/null
+++ b/crates/libsyntax2/src/grammar/attributes.rs
@@ -0,0 +1,79 @@
1use super::*;
2
3pub(super) fn inner_attributes(p: &mut Parser) {
4 while p.current() == POUND && p.nth(1) == EXCL {
5 attribute(p, true)
6 }
7}
8
9pub(super) fn outer_attributes(p: &mut Parser) {
10 while p.at(POUND) {
11 attribute(p, false)
12 }
13}
14
15fn attribute(p: &mut Parser, inner: bool) {
16 let attr = p.start();
17 assert!(p.at(POUND));
18 p.bump();
19
20 if inner {
21 assert!(p.at(EXCL));
22 p.bump();
23 }
24
25 if p.expect(L_BRACK) {
26 meta_item(p);
27 p.expect(R_BRACK);
28 }
29 attr.complete(p, ATTR);
30}
31
32fn meta_item(p: &mut Parser) {
33 if p.at(IDENT) {
34 let meta_item = p.start();
35 p.bump();
36 match p.current() {
37 EQ => {
38 p.bump();
39 if expressions::literal(p).is_none() {
40 p.error("expected literal");
41 }
42 }
43 L_PAREN => meta_item_arg_list(p),
44 _ => (),
45 }
46 meta_item.complete(p, META_ITEM);
47 } else {
48 p.error("expected attribute value");
49 }
50}
51
52fn meta_item_arg_list(p: &mut Parser) {
53 assert!(p.at(L_PAREN));
54 p.bump();
55 loop {
56 match p.current() {
57 EOF | R_PAREN => break,
58 IDENT => meta_item(p),
59 c => if expressions::literal(p).is_none() {
60 let message = "expected attribute";
61
62 if items::ITEM_FIRST.contains(c) {
63 p.error(message);
64 return;
65 }
66
67 let err = p.start();
68 p.error(message);
69 p.bump();
70 err.complete(p, ERROR);
71 continue;
72 },
73 }
74 if !p.at(R_PAREN) {
75 p.expect(COMMA);
76 }
77 }
78 p.expect(R_PAREN);
79}
diff --git a/crates/libsyntax2/src/grammar/expressions/atom.rs b/crates/libsyntax2/src/grammar/expressions/atom.rs
new file mode 100644
index 000000000..af9f47c5e
--- /dev/null
+++ b/crates/libsyntax2/src/grammar/expressions/atom.rs
@@ -0,0 +1,348 @@
1use super::*;
2
3// test expr_literals
4// fn foo() {
5// let _ = true;
6// let _ = false;
7// let _ = 1;
8// let _ = 2.0;
9// let _ = b'a';
10// let _ = 'b';
11// let _ = "c";
12// let _ = r"d";
13// let _ = b"e";
14// let _ = br"f";
15// }
16const LITERAL_FIRST: TokenSet =
17 token_set![TRUE_KW, FALSE_KW, INT_NUMBER, FLOAT_NUMBER, BYTE, CHAR,
18 STRING, RAW_STRING, BYTE_STRING, RAW_BYTE_STRING];
19
20pub(crate) fn literal(p: &mut Parser) -> Option<CompletedMarker> {
21 if !LITERAL_FIRST.contains(p.current()) {
22 return None;
23 }
24 let m = p.start();
25 p.bump();
26 Some(m.complete(p, LITERAL))
27}
28
29pub(super) const ATOM_EXPR_FIRST: TokenSet =
30 token_set_union![
31 LITERAL_FIRST,
32 token_set![L_PAREN, PIPE, MOVE_KW, IF_KW, WHILE_KW, MATCH_KW, UNSAFE_KW, L_CURLY, RETURN_KW,
33 IDENT, SELF_KW, SUPER_KW, COLONCOLON ],
34 ];
35
36pub(super) fn atom_expr(p: &mut Parser, r: Restrictions) -> Option<CompletedMarker> {
37 match literal(p) {
38 Some(m) => return Some(m),
39 None => (),
40 }
41 if paths::is_path_start(p) {
42 return Some(path_expr(p, r));
43 }
44 let la = p.nth(1);
45 let done = match p.current() {
46 L_PAREN => tuple_expr(p),
47 L_BRACK => array_expr(p),
48 PIPE => lambda_expr(p),
49 MOVE_KW if la == PIPE => lambda_expr(p),
50 IF_KW => if_expr(p),
51 WHILE_KW => while_expr(p),
52 LOOP_KW => loop_expr(p),
53 FOR_KW => for_expr(p),
54 MATCH_KW => match_expr(p),
55 UNSAFE_KW if la == L_CURLY => block_expr(p),
56 L_CURLY => block_expr(p),
57 RETURN_KW => return_expr(p),
58 _ => {
59 p.err_and_bump("expected expression");
60 return None;
61 }
62 };
63 Some(done)
64}
65
66// test tuple_expr
67// fn foo() {
68// ();
69// (1);
70// (1,);
71// }
72fn tuple_expr(p: &mut Parser) -> CompletedMarker {
73 assert!(p.at(L_PAREN));
74 let m = p.start();
75 p.expect(L_PAREN);
76
77 let mut saw_comma = false;
78 let mut saw_expr = false;
79 while !p.at(EOF) && !p.at(R_PAREN) {
80 saw_expr = true;
81 expr(p);
82 if !p.at(R_PAREN) {
83 saw_comma = true;
84 p.expect(COMMA);
85 }
86 }
87 p.expect(R_PAREN);
88 m.complete(p, if saw_expr && !saw_comma { PAREN_EXPR } else { TUPLE_EXPR })
89}
90
91// test array_expr
92// fn foo() {
93// [];
94// [1];
95// [1, 2,];
96// [1; 2];
97// }
98fn array_expr(p: &mut Parser) -> CompletedMarker {
99 assert!(p.at(L_BRACK));
100 let m = p.start();
101 p.bump();
102 if p.eat(R_BRACK) {
103 return m.complete(p, ARRAY_EXPR);
104 }
105 expr(p);
106 if p.eat(SEMI) {
107 expr(p);
108 p.expect(R_BRACK);
109 return m.complete(p, ARRAY_EXPR);
110 }
111 while !p.at(EOF) && !p.at(R_BRACK) {
112 p.expect(COMMA);
113 if !p.at(R_BRACK) {
114 expr(p);
115 }
116 }
117 p.expect(R_BRACK);
118 m.complete(p, ARRAY_EXPR)
119}
120
121// test lambda_expr
122// fn foo() {
123// || ();
124// || -> i32 { 92 };
125// |x| x;
126// move |x: i32,| x;
127// }
128fn lambda_expr(p: &mut Parser) -> CompletedMarker {
129 assert!(p.at(PIPE) || (p.at(MOVE_KW) && p.nth(1) == PIPE));
130 let m = p.start();
131 p.eat(MOVE_KW);
132 params::param_list_opt_types(p);
133 if fn_ret_type(p) {
134 block(p);
135 } else {
136 expr(p);
137 }
138 m.complete(p, LAMBDA_EXPR)
139}
140
141// test if_expr
142// fn foo() {
143// if true {};
144// if true {} else {};
145// if true {} else if false {} else {};
146// if S {};
147// }
148fn if_expr(p: &mut Parser) -> CompletedMarker {
149 assert!(p.at(IF_KW));
150 let m = p.start();
151 p.bump();
152 cond(p);
153 block(p);
154 if p.at(ELSE_KW) {
155 p.bump();
156 if p.at(IF_KW) {
157 if_expr(p);
158 } else {
159 block(p);
160 }
161 }
162 m.complete(p, IF_EXPR)
163}
164
165// test while_expr
166// fn foo() {
167// while true {};
168// while let Some(x) = it.next() {};
169// }
170fn while_expr(p: &mut Parser) -> CompletedMarker {
171 assert!(p.at(WHILE_KW));
172 let m = p.start();
173 p.bump();
174 cond(p);
175 block(p);
176 m.complete(p, WHILE_EXPR)
177}
178
179// test loop_expr
180// fn foo() {
181// loop {};
182// }
183fn loop_expr(p: &mut Parser) -> CompletedMarker {
184 assert!(p.at(LOOP_KW));
185 let m = p.start();
186 p.bump();
187 block(p);
188 m.complete(p, LOOP_EXPR)
189}
190
191// test for_expr
192// fn foo() {
193// for x in [] {};
194// }
195fn for_expr(p: &mut Parser) -> CompletedMarker {
196 assert!(p.at(FOR_KW));
197 let m = p.start();
198 p.bump();
199 patterns::pattern(p);
200 p.expect(IN_KW);
201 expr_no_struct(p);
202 block(p);
203 m.complete(p, FOR_EXPR)
204}
205
206// test cond
207// fn foo() { if let Some(_) = None {} }
208fn cond(p: &mut Parser) {
209 if p.eat(LET_KW) {
210 patterns::pattern(p);
211 p.expect(EQ);
212 }
213 expr_no_struct(p)
214}
215
216// test match_expr
217// fn foo() {
218// match () { };
219// match S {};
220// }
221fn match_expr(p: &mut Parser) -> CompletedMarker {
222 assert!(p.at(MATCH_KW));
223 let m = p.start();
224 p.bump();
225 expr_no_struct(p);
226 p.eat(L_CURLY);
227 while !p.at(EOF) && !p.at(R_CURLY) {
228 // test match_arms_commas
229 // fn foo() {
230 // match () {
231 // _ => (),
232 // _ => {}
233 // _ => ()
234 // }
235 // }
236 if match_arm(p).is_block() {
237 p.eat(COMMA);
238 } else if !p.at(R_CURLY) {
239 p.expect(COMMA);
240 }
241 }
242 p.expect(R_CURLY);
243 m.complete(p, MATCH_EXPR)
244}
245
246// test match_arm
247// fn foo() {
248// match () {
249// _ => (),
250// X | Y if Z => (),
251// };
252// }
253fn match_arm(p: &mut Parser) -> BlockLike {
254 let m = p.start();
255 loop {
256 patterns::pattern(p);
257 if !p.eat(PIPE) {
258 break;
259 }
260 }
261 if p.eat(IF_KW) {
262 expr_no_struct(p);
263 }
264 p.expect(FAT_ARROW);
265 let ret = expr_stmt(p);
266 m.complete(p, MATCH_ARM);
267 ret
268}
269
270// test block_expr
271// fn foo() {
272// {};
273// unsafe {};
274// }
275pub(super) fn block_expr(p: &mut Parser) -> CompletedMarker {
276 assert!(p.at(L_CURLY) || p.at(UNSAFE_KW) && p.nth(1) == L_CURLY);
277 let m = p.start();
278 p.eat(UNSAFE_KW);
279 p.bump();
280 while !p.at(EOF) && !p.at(R_CURLY) {
281 match p.current() {
282 LET_KW => let_stmt(p),
283 _ => {
284 // test block_items
285 // fn a() { fn b() {} }
286 let m = p.start();
287 match items::maybe_item(p) {
288 items::MaybeItem::Item(kind) => {
289 m.complete(p, kind);
290 }
291 items::MaybeItem::Modifiers => {
292 m.abandon(p);
293 p.error("expected an item");
294 }
295 // test pub_expr
296 // fn foo() { pub 92; } //FIXME
297 items::MaybeItem::None => {
298 let is_blocklike = expressions::expr_stmt(p) == BlockLike::Block;
299 if p.eat(SEMI) || (is_blocklike && !p.at(R_CURLY)) {
300 m.complete(p, EXPR_STMT);
301 } else {
302 m.abandon(p);
303 }
304 }
305 }
306 }
307 }
308 }
309 p.expect(R_CURLY);
310 m.complete(p, BLOCK_EXPR)
311}
312
313// test let_stmt;
314// fn foo() {
315// let a;
316// let b: i32;
317// let c = 92;
318// let d: i32 = 92;
319// }
320fn let_stmt(p: &mut Parser) {
321 assert!(p.at(LET_KW));
322 let m = p.start();
323 p.bump();
324 patterns::pattern(p);
325 if p.at(COLON) {
326 types::ascription(p);
327 }
328 if p.eat(EQ) {
329 expressions::expr(p);
330 }
331 p.expect(SEMI);
332 m.complete(p, LET_STMT);
333}
334
335// test return_expr
336// fn foo() {
337// return;
338// return 92;
339// }
340fn return_expr(p: &mut Parser) -> CompletedMarker {
341 assert!(p.at(RETURN_KW));
342 let m = p.start();
343 p.bump();
344 if EXPR_FIRST.contains(p.current()) {
345 expr(p);
346 }
347 m.complete(p, RETURN_EXPR)
348}
diff --git a/crates/libsyntax2/src/grammar/expressions/mod.rs b/crates/libsyntax2/src/grammar/expressions/mod.rs
new file mode 100644
index 000000000..dcbb1e2a8
--- /dev/null
+++ b/crates/libsyntax2/src/grammar/expressions/mod.rs
@@ -0,0 +1,379 @@
1mod atom;
2
3use super::*;
4pub(super) use self::atom::literal;
5
6const EXPR_FIRST: TokenSet = LHS_FIRST;
7
8pub(super) fn expr(p: &mut Parser) -> BlockLike {
9 let r = Restrictions { forbid_structs: false, prefer_stmt: false };
10 expr_bp(p, r, 1)
11}
12
13pub(super) fn expr_stmt(p: &mut Parser) -> BlockLike {
14 let r = Restrictions { forbid_structs: false, prefer_stmt: true };
15 expr_bp(p, r, 1)
16}
17
18fn expr_no_struct(p: &mut Parser) {
19 let r = Restrictions { forbid_structs: true, prefer_stmt: false };
20 expr_bp(p, r, 1);
21}
22
23// test block
24// fn a() {}
25// fn b() { let _ = 1; }
26// fn c() { 1; 2; }
27// fn d() { 1; 2 }
28pub(super) fn block(p: &mut Parser) {
29 if !p.at(L_CURLY) {
30 p.error("expected block");
31 return;
32 }
33 atom::block_expr(p);
34}
35
36#[derive(Clone, Copy)]
37struct Restrictions {
38 forbid_structs: bool,
39 prefer_stmt: bool,
40}
41
42enum Op {
43 Simple,
44 Composite(SyntaxKind, u8),
45}
46
47fn current_op(p: &Parser) -> (u8, Op) {
48 if p.at_compound2(PLUS, EQ) {
49 return (1, Op::Composite(PLUSEQ, 2));
50 }
51 if p.at_compound2(MINUS, EQ) {
52 return (1, Op::Composite(MINUSEQ, 2));
53 }
54 if p.at_compound3(L_ANGLE, L_ANGLE, EQ) {
55 return (1, Op::Composite(SHLEQ, 3));
56 }
57 if p.at_compound3(R_ANGLE, R_ANGLE, EQ) {
58 return (1, Op::Composite(SHREQ, 3));
59 }
60 if p.at_compound2(PIPE, PIPE) {
61 return (3, Op::Composite(PIPEPIPE, 2));
62 }
63 if p.at_compound2(AMP, AMP) {
64 return (4, Op::Composite(AMPAMP, 2));
65 }
66 if p.at_compound2(L_ANGLE, EQ) {
67 return (5, Op::Composite(LTEQ, 2));
68 }
69 if p.at_compound2(R_ANGLE, EQ) {
70 return (5, Op::Composite(GTEQ, 2));
71 }
72 if p.at_compound2(L_ANGLE, L_ANGLE) {
73 return (9, Op::Composite(SHL, 2));
74 }
75 if p.at_compound2(R_ANGLE, R_ANGLE) {
76 return (9, Op::Composite(SHR, 2));
77 }
78
79 let bp = match p.current() {
80 EQ => 1,
81 DOTDOT => 2,
82 EQEQ | NEQ | L_ANGLE | R_ANGLE => 5,
83 PIPE => 6,
84 CARET => 7,
85 AMP => 8,
86 MINUS | PLUS => 10,
87 STAR | SLASH | PERCENT => 11,
88 _ => 0,
89 };
90 (bp, Op::Simple)
91}
92
93// Parses expression with binding power of at least bp.
94fn expr_bp(p: &mut Parser, r: Restrictions, bp: u8) -> BlockLike {
95 let mut lhs = match lhs(p, r) {
96 Some(lhs) => {
97 // test stmt_bin_expr_ambiguity
98 // fn foo() {
99 // let _ = {1} & 2;
100 // {1} &2;
101 // }
102 if r.prefer_stmt && is_block(lhs.kind()) {
103 return BlockLike::Block;
104 }
105 lhs
106 }
107 None => return BlockLike::NotBlock,
108 };
109
110 loop {
111 let is_range = p.current() == DOTDOT;
112 let (op_bp, op) = current_op(p);
113 if op_bp < bp {
114 break;
115 }
116 let m = lhs.precede(p);
117 match op {
118 Op::Simple => p.bump(),
119 Op::Composite(kind, n) => {
120 p.bump_compound(kind, n);
121 }
122 }
123 expr_bp(p, r, op_bp + 1);
124 lhs = m.complete(p, if is_range { RANGE_EXPR } else { BIN_EXPR });
125 }
126 BlockLike::NotBlock
127}
128
129// test no_semi_after_block
130// fn foo() {
131// if true {}
132// loop {}
133// match () {}
134// while true {}
135// for _ in () {}
136// {}
137// {}
138// }
139fn is_block(kind: SyntaxKind) -> bool {
140 match kind {
141 IF_EXPR | WHILE_EXPR | FOR_EXPR | LOOP_EXPR | MATCH_EXPR | BLOCK_EXPR => true,
142 _ => false,
143 }
144}
145
146const LHS_FIRST: TokenSet =
147 token_set_union![
148 token_set![AMP, STAR, EXCL, DOTDOT, MINUS],
149 atom::ATOM_EXPR_FIRST,
150 ];
151
152fn lhs(p: &mut Parser, r: Restrictions) -> Option<CompletedMarker> {
153 let m;
154 let kind = match p.current() {
155 // test ref_expr
156 // fn foo() {
157 // let _ = &1;
158 // let _ = &mut &f();
159 // }
160 AMP => {
161 m = p.start();
162 p.bump();
163 p.eat(MUT_KW);
164 REF_EXPR
165 }
166 // test unary_expr
167 // fn foo() {
168 // **&1;
169 // !!true;
170 // --1;
171 // }
172 STAR | EXCL | MINUS => {
173 m = p.start();
174 p.bump();
175 PREFIX_EXPR
176 }
177 DOTDOT => {
178 m = p.start();
179 p.bump();
180 expr_bp(p, r, 2);
181 return Some(m.complete(p, RANGE_EXPR));
182 }
183 _ => {
184 let lhs = atom::atom_expr(p, r)?;
185 return Some(postfix_expr(p, r, lhs));
186 }
187 };
188 expr_bp(p, r, 255);
189 Some(m.complete(p, kind))
190}
191
192fn postfix_expr(p: &mut Parser, r: Restrictions, mut lhs: CompletedMarker) -> CompletedMarker {
193 let mut allow_calls = !r.prefer_stmt || !is_block(lhs.kind());
194 loop {
195 lhs = match p.current() {
196 // test stmt_postfix_expr_ambiguity
197 // fn foo() {
198 // match () {
199 // _ => {}
200 // () => {}
201 // [] => {}
202 // }
203 // }
204 L_PAREN if allow_calls => call_expr(p, lhs),
205 L_BRACK if allow_calls => index_expr(p, lhs),
206 DOT if p.nth(1) == IDENT => if p.nth(2) == L_PAREN || p.nth(2) == COLONCOLON {
207 method_call_expr(p, lhs)
208 } else {
209 field_expr(p, lhs)
210 },
211 DOT if p.nth(1) == INT_NUMBER => field_expr(p, lhs),
212 // test postfix_range
213 // fn foo() { let x = 1..; }
214 DOTDOT if !EXPR_FIRST.contains(p.nth(1)) => {
215 let m = lhs.precede(p);
216 p.bump();
217 m.complete(p, RANGE_EXPR)
218 }
219 QUESTION => try_expr(p, lhs),
220 AS_KW => cast_expr(p, lhs),
221 _ => break,
222 };
223 allow_calls = true
224 }
225 lhs
226}
227
228// test call_expr
229// fn foo() {
230// let _ = f();
231// let _ = f()(1)(1, 2,);
232// }
233fn call_expr(p: &mut Parser, lhs: CompletedMarker) -> CompletedMarker {
234 assert!(p.at(L_PAREN));
235 let m = lhs.precede(p);
236 arg_list(p);
237 m.complete(p, CALL_EXPR)
238}
239
240// test index_expr
241// fn foo() {
242// x[1][2];
243// }
244fn index_expr(p: &mut Parser, lhs: CompletedMarker) -> CompletedMarker {
245 assert!(p.at(L_BRACK));
246 let m = lhs.precede(p);
247 p.bump();
248 expr(p);
249 p.expect(R_BRACK);
250 m.complete(p, INDEX_EXPR)
251}
252
253// test method_call_expr
254// fn foo() {
255// x.foo();
256// y.bar::<T>(1, 2,);
257// }
258fn method_call_expr(p: &mut Parser, lhs: CompletedMarker) -> CompletedMarker {
259 assert!(
260 p.at(DOT) && p.nth(1) == IDENT
261 && (p.nth(2) == L_PAREN || p.nth(2) == COLONCOLON)
262 );
263 let m = lhs.precede(p);
264 p.bump();
265 name_ref(p);
266 type_args::type_arg_list(p, true);
267 arg_list(p);
268 m.complete(p, METHOD_CALL_EXPR)
269}
270
271// test field_expr
272// fn foo() {
273// x.foo;
274// x.0.bar;
275// }
276fn field_expr(p: &mut Parser, lhs: CompletedMarker) -> CompletedMarker {
277 assert!(p.at(DOT) && (p.nth(1) == IDENT || p.nth(1) == INT_NUMBER));
278 let m = lhs.precede(p);
279 p.bump();
280 if p.at(IDENT) {
281 name_ref(p)
282 } else {
283 p.bump()
284 }
285 m.complete(p, FIELD_EXPR)
286}
287
288// test try_expr
289// fn foo() {
290// x?;
291// }
292fn try_expr(p: &mut Parser, lhs: CompletedMarker) -> CompletedMarker {
293 assert!(p.at(QUESTION));
294 let m = lhs.precede(p);
295 p.bump();
296 m.complete(p, TRY_EXPR)
297}
298
299// test cast_expr
300// fn foo() {
301// 82 as i32;
302// }
303fn cast_expr(p: &mut Parser, lhs: CompletedMarker) -> CompletedMarker {
304 assert!(p.at(AS_KW));
305 let m = lhs.precede(p);
306 p.bump();
307 types::type_(p);
308 m.complete(p, CAST_EXPR)
309}
310
311fn arg_list(p: &mut Parser) {
312 assert!(p.at(L_PAREN));
313 let m = p.start();
314 p.bump();
315 while !p.at(R_PAREN) && !p.at(EOF) {
316 expr(p);
317 if !p.at(R_PAREN) && !p.expect(COMMA) {
318 break;
319 }
320 }
321 p.eat(R_PAREN);
322 m.complete(p, ARG_LIST);
323}
324
325// test path_expr
326// fn foo() {
327// let _ = a;
328// let _ = a::b;
329// let _ = ::a::<b>;
330// let _ = format!();
331// }
332fn path_expr(p: &mut Parser, r: Restrictions) -> CompletedMarker {
333 assert!(paths::is_path_start(p));
334 let m = p.start();
335 paths::expr_path(p);
336 match p.current() {
337 L_CURLY if !r.forbid_structs => {
338 struct_lit(p);
339 m.complete(p, STRUCT_LIT)
340 }
341 EXCL => {
342 items::macro_call_after_excl(p);
343 m.complete(p, MACRO_CALL)
344 }
345 _ => m.complete(p, PATH_EXPR)
346 }
347}
348
349// test struct_lit
350// fn foo() {
351// S {};
352// S { x, y: 32, };
353// S { x, y: 32, ..Default::default() };
354// }
355fn struct_lit(p: &mut Parser) {
356 assert!(p.at(L_CURLY));
357 p.bump();
358 while !p.at(EOF) && !p.at(R_CURLY) {
359 match p.current() {
360 IDENT => {
361 let m = p.start();
362 name_ref(p);
363 if p.eat(COLON) {
364 expr(p);
365 }
366 m.complete(p, STRUCT_LIT_FIELD);
367 }
368 DOTDOT => {
369 p.bump();
370 expr(p);
371 }
372 _ => p.err_and_bump("expected identifier"),
373 }
374 if !p.at(R_CURLY) {
375 p.expect(COMMA);
376 }
377 }
378 p.expect(R_CURLY);
379}
diff --git a/crates/libsyntax2/src/grammar/items/consts.rs b/crates/libsyntax2/src/grammar/items/consts.rs
new file mode 100644
index 000000000..b11949b49
--- /dev/null
+++ b/crates/libsyntax2/src/grammar/items/consts.rs
@@ -0,0 +1,21 @@
1use super::*;
2
3pub(super) fn static_item(p: &mut Parser) {
4 const_or_static(p, STATIC_KW)
5}
6
7pub(super) fn const_item(p: &mut Parser) {
8 const_or_static(p, CONST_KW)
9}
10
11fn const_or_static(p: &mut Parser, kw: SyntaxKind) {
12 assert!(p.at(kw));
13 p.bump();
14 p.eat(MUT_KW); // TODO: validator to forbid const mut
15 name(p);
16 types::ascription(p);
17 if p.eat(EQ) {
18 expressions::expr(p);
19 }
20 p.expect(SEMI);
21}
diff --git a/crates/libsyntax2/src/grammar/items/mod.rs b/crates/libsyntax2/src/grammar/items/mod.rs
new file mode 100644
index 000000000..3bf906f85
--- /dev/null
+++ b/crates/libsyntax2/src/grammar/items/mod.rs
@@ -0,0 +1,332 @@
1use super::*;
2
3mod consts;
4mod structs;
5mod traits;
6mod use_item;
7
8// test mod_contents
9// fn foo() {}
10// macro_rules! foo {}
11// foo::bar!();
12// super::baz! {}
13// struct S;
14pub(super) fn mod_contents(p: &mut Parser, stop_on_r_curly: bool) {
15 attributes::inner_attributes(p);
16 while !p.at(EOF) && !(stop_on_r_curly && p.at(R_CURLY)) {
17 item_or_macro(p, stop_on_r_curly)
18 }
19}
20
21pub(super) fn item_or_macro(p: &mut Parser, stop_on_r_curly: bool) {
22 let m = p.start();
23 match maybe_item(p) {
24 MaybeItem::Item(kind) => {
25 m.complete(p, kind);
26 }
27 MaybeItem::None => {
28 if paths::is_path_start(p) {
29 match macro_call(p) {
30 BlockLike::Block => (),
31 BlockLike::NotBlock => {
32 p.expect(SEMI);
33 }
34 }
35 m.complete(p, MACRO_CALL);
36 } else {
37 m.abandon(p);
38 if p.at(L_CURLY) {
39 error_block(p, "expected an item");
40 } else if !p.at(EOF) && !(stop_on_r_curly && p.at(R_CURLY)) {
41 p.err_and_bump("expected an item");
42 } else {
43 p.error("expected an item");
44 }
45 }
46 }
47 MaybeItem::Modifiers => {
48 p.error("expected fn, trait or impl");
49 m.complete(p, ERROR);
50 }
51 }
52}
53
54pub(super) const ITEM_FIRST: TokenSet =
55 token_set![EXTERN_KW, MOD_KW, USE_KW, STRUCT_KW, ENUM_KW, FN_KW, PUB_KW, POUND];
56
57pub(super) enum MaybeItem {
58 None,
59 Item(SyntaxKind),
60 Modifiers,
61}
62
63pub(super) fn maybe_item(p: &mut Parser) -> MaybeItem {
64 attributes::outer_attributes(p);
65 visibility(p);
66 if let Some(kind) = items_without_modifiers(p) {
67 return MaybeItem::Item(kind);
68 }
69
70 let mut has_mods = false;
71 // modifiers
72 has_mods |= p.eat(CONST_KW);
73
74 // test unsafe_block_in_mod
75 // fn foo(){} unsafe { } fn bar(){}
76 if p.at(UNSAFE_KW) && p.nth(1) != L_CURLY {
77 p.eat(UNSAFE_KW);
78 has_mods = true;
79 }
80 if p.at(EXTERN_KW) {
81 has_mods = true;
82 abi(p);
83 }
84 if p.at(IDENT) && p.at_contextual_kw("auto") && p.nth(1) == TRAIT_KW {
85 p.bump_remap(AUTO_KW);
86 has_mods = true;
87 }
88 if p.at(IDENT) && p.at_contextual_kw("default") && p.nth(1) == IMPL_KW {
89 p.bump_remap(DEFAULT_KW);
90 has_mods = true;
91 }
92
93 // items
94 let kind = match p.current() {
95 // test extern_fn
96 // extern fn foo() {}
97
98 // test const_fn
99 // const fn foo() {}
100
101 // test const_unsafe_fn
102 // const unsafe fn foo() {}
103
104 // test unsafe_extern_fn
105 // unsafe extern "C" fn foo() {}
106
107 // test unsafe_fn
108 // unsafe fn foo() {}
109 FN_KW => {
110 function(p);
111 FUNCTION
112 }
113
114 // test unsafe_trait
115 // unsafe trait T {}
116
117 // test auto_trait
118 // auto trait T {}
119
120 // test unsafe_auto_trait
121 // unsafe auto trait T {}
122 TRAIT_KW => {
123 traits::trait_item(p);
124 TRAIT_ITEM
125 }
126
127 // test unsafe_impl
128 // unsafe impl Foo {}
129
130 // test default_impl
131 // default impl Foo {}
132
133 // test unsafe_default_impl
134 // unsafe default impl Foo {}
135 IMPL_KW => {
136 traits::impl_item(p);
137 IMPL_ITEM
138 }
139 _ => return if has_mods {
140 MaybeItem::Modifiers
141 } else {
142 MaybeItem::None
143 }
144 };
145
146 MaybeItem::Item(kind)
147}
148
149fn items_without_modifiers(p: &mut Parser) -> Option<SyntaxKind> {
150 let la = p.nth(1);
151 let kind = match p.current() {
152 // test extern_crate
153 // extern crate foo;
154 EXTERN_KW if la == CRATE_KW => {
155 extern_crate_item(p);
156 EXTERN_CRATE_ITEM
157 }
158 TYPE_KW => {
159 type_item(p);
160 TYPE_ITEM
161 }
162 MOD_KW => {
163 mod_item(p);
164 MOD_ITEM
165 }
166 STRUCT_KW => {
167 structs::struct_item(p);
168 if p.at(SEMI) {
169 p.err_and_bump(
170 "expected item, found `;`\n\
171 consider removing this semicolon"
172 );
173 }
174 STRUCT_ITEM
175 }
176 ENUM_KW => {
177 structs::enum_item(p);
178 ENUM_ITEM
179 }
180 USE_KW => {
181 use_item::use_item(p);
182 USE_ITEM
183 }
184 CONST_KW if (la == IDENT || la == MUT_KW) => {
185 consts::const_item(p);
186 CONST_ITEM
187 }
188 STATIC_KW => {
189 consts::static_item(p);
190 STATIC_ITEM
191 }
192 // test extern_block
193 // extern {}
194 EXTERN_KW if la == L_CURLY || ((la == STRING || la == RAW_STRING) && p.nth(2) == L_CURLY) => {
195 abi(p);
196 extern_block(p);
197 EXTERN_BLOCK_EXPR
198 }
199 _ => return None,
200 };
201 Some(kind)
202}
203
204fn extern_crate_item(p: &mut Parser) {
205 assert!(p.at(EXTERN_KW));
206 p.bump();
207 assert!(p.at(CRATE_KW));
208 p.bump();
209 name(p);
210 alias(p);
211 p.expect(SEMI);
212}
213
214fn extern_block(p: &mut Parser) {
215 assert!(p.at(L_CURLY));
216 p.bump();
217 p.expect(R_CURLY);
218}
219
220fn function(p: &mut Parser) {
221 assert!(p.at(FN_KW));
222 p.bump();
223
224 name(p);
225 // test function_type_params
226 // fn foo<T: Clone + Copy>(){}
227 type_params::type_param_list(p);
228
229 if p.at(L_PAREN) {
230 params::param_list(p);
231 } else {
232 p.error("expected function arguments");
233 }
234 // test function_ret_type
235 // fn foo() {}
236 // fn bar() -> () {}
237 fn_ret_type(p);
238
239 // test function_where_clause
240 // fn foo<T>() where T: Copy {}
241 type_params::where_clause(p);
242
243 // test fn_decl
244 // trait T { fn foo(); }
245 if !p.eat(SEMI) {
246 expressions::block(p);
247 }
248}
249
250// test type_item
251// type Foo = Bar;
252fn type_item(p: &mut Parser) {
253 assert!(p.at(TYPE_KW));
254 p.bump();
255
256 name(p);
257
258 // test type_item_type_params
259 // type Result<T> = ();
260 type_params::type_param_list(p);
261
262 if p.at(COLON) {
263 type_params::bounds(p);
264 }
265
266 // test type_item_where_clause
267 // type Foo where Foo: Copy = ();
268 type_params::where_clause(p);
269
270 if p.eat(EQ) {
271 types::type_(p);
272 }
273 p.expect(SEMI);
274}
275
276fn mod_item(p: &mut Parser) {
277 assert!(p.at(MOD_KW));
278 p.bump();
279
280 name(p);
281 if !p.eat(SEMI) {
282 if p.expect(L_CURLY) {
283 mod_contents(p, true);
284 p.expect(R_CURLY);
285 }
286 }
287}
288
289fn macro_call(p: &mut Parser) -> BlockLike {
290 assert!(paths::is_path_start(p));
291 paths::use_path(p);
292 macro_call_after_excl(p)
293}
294
295pub(super) fn macro_call_after_excl(p: &mut Parser) -> BlockLike {
296 p.expect(EXCL);
297 p.eat(IDENT);
298 let flavor = match p.current() {
299 L_CURLY => {
300 token_tree(p);
301 BlockLike::Block
302 }
303 L_PAREN | L_BRACK => {
304 token_tree(p);
305 BlockLike::NotBlock
306 }
307 _ => {
308 p.error("expected `{`, `[`, `(`");
309 BlockLike::NotBlock
310 },
311 };
312
313 flavor
314}
315
316fn token_tree(p: &mut Parser) {
317 let closing_paren_kind = match p.current() {
318 L_CURLY => R_CURLY,
319 L_PAREN => R_PAREN,
320 L_BRACK => R_BRACK,
321 _ => unreachable!(),
322 };
323 p.bump();
324 while !p.at(EOF) && !p.at(closing_paren_kind) {
325 match p.current() {
326 L_CURLY | L_PAREN | L_BRACK => token_tree(p),
327 R_CURLY | R_PAREN | R_BRACK => p.err_and_bump("unmatched brace"),
328 _ => p.bump()
329 }
330 };
331 p.expect(closing_paren_kind);
332}
diff --git a/crates/libsyntax2/src/grammar/items/structs.rs b/crates/libsyntax2/src/grammar/items/structs.rs
new file mode 100644
index 000000000..67616eaad
--- /dev/null
+++ b/crates/libsyntax2/src/grammar/items/structs.rs
@@ -0,0 +1,116 @@
1use super::*;
2
3pub(super) fn struct_item(p: &mut Parser) {
4 assert!(p.at(STRUCT_KW));
5 p.bump();
6
7 name(p);
8 type_params::type_param_list(p);
9 match p.current() {
10 WHERE_KW => {
11 type_params::where_clause(p);
12 match p.current() {
13 SEMI => {
14 p.bump();
15 return;
16 }
17 L_CURLY => named_fields(p),
18 _ => {
19 //TODO: special case `(` error message
20 p.error("expected `;` or `{`");
21 return;
22 }
23 }
24 }
25 SEMI => {
26 p.bump();
27 return;
28 }
29 L_CURLY => named_fields(p),
30 L_PAREN => {
31 pos_fields(p);
32 p.expect(SEMI);
33 }
34 _ => {
35 p.error("expected `;`, `{`, or `(`");
36 return;
37 }
38 }
39}
40
41pub(super) fn enum_item(p: &mut Parser) {
42 assert!(p.at(ENUM_KW));
43 p.bump();
44 name(p);
45 type_params::type_param_list(p);
46 type_params::where_clause(p);
47 if p.expect(L_CURLY) {
48 while !p.at(EOF) && !p.at(R_CURLY) {
49 let var = p.start();
50 attributes::outer_attributes(p);
51 if p.at(IDENT) {
52 name(p);
53 match p.current() {
54 L_CURLY => named_fields(p),
55 L_PAREN => pos_fields(p),
56 EQ => {
57 p.bump();
58 expressions::expr(p);
59 }
60 _ => (),
61 }
62 var.complete(p, ENUM_VARIANT);
63 } else {
64 var.abandon(p);
65 p.err_and_bump("expected enum variant");
66 }
67 if !p.at(R_CURLY) {
68 p.expect(COMMA);
69 }
70 }
71 p.expect(R_CURLY);
72 }
73}
74
75fn named_fields(p: &mut Parser) {
76 assert!(p.at(L_CURLY));
77 p.bump();
78 while !p.at(R_CURLY) && !p.at(EOF) {
79 named_field(p);
80 if !p.at(R_CURLY) {
81 p.expect(COMMA);
82 }
83 }
84 p.expect(R_CURLY);
85
86 fn named_field(p: &mut Parser) {
87 let field = p.start();
88 visibility(p);
89 if p.at(IDENT) {
90 name(p);
91 p.expect(COLON);
92 types::type_(p);
93 field.complete(p, NAMED_FIELD);
94 } else {
95 field.abandon(p);
96 p.err_and_bump("expected field declaration");
97 }
98 }
99}
100
101fn pos_fields(p: &mut Parser) {
102 if !p.expect(L_PAREN) {
103 return;
104 }
105 while !p.at(R_PAREN) && !p.at(EOF) {
106 let pos_field = p.start();
107 visibility(p);
108 types::type_(p);
109 pos_field.complete(p, POS_FIELD);
110
111 if !p.at(R_PAREN) {
112 p.expect(COMMA);
113 }
114 }
115 p.expect(R_PAREN);
116}
diff --git a/crates/libsyntax2/src/grammar/items/traits.rs b/crates/libsyntax2/src/grammar/items/traits.rs
new file mode 100644
index 000000000..0b9fb2b0b
--- /dev/null
+++ b/crates/libsyntax2/src/grammar/items/traits.rs
@@ -0,0 +1,87 @@
1use super::*;
2
3// test trait_item
4// trait T<U>: Hash + Clone where U: Copy {}
5pub(super) fn trait_item(p: &mut Parser) {
6 assert!(p.at(TRAIT_KW));
7 p.bump();
8 name(p);
9 type_params::type_param_list(p);
10 if p.at(COLON) {
11 type_params::bounds(p);
12 }
13 type_params::where_clause(p);
14 p.expect(L_CURLY);
15 // test trait_item_items
16 // impl F {
17 // type A: Clone;
18 // const B: i32;
19 // fn foo() {}
20 // fn bar(&self);
21 // }
22 while !p.at(EOF) && !p.at(R_CURLY) {
23 item_or_macro(p, true);
24 }
25 p.expect(R_CURLY);
26}
27
28// test impl_item
29// impl Foo {}
30pub(super) fn impl_item(p: &mut Parser) {
31 assert!(p.at(IMPL_KW));
32 p.bump();
33 if choose_type_params_over_qpath(p) {
34 type_params::type_param_list(p);
35 }
36
37 // TODO: never type
38 // impl ! {}
39
40 // test impl_item_neg
41 // impl !Send for X {}
42 p.eat(EXCL);
43 types::type_(p);
44 if p.eat(FOR_KW) {
45 types::type_(p);
46 }
47 type_params::where_clause(p);
48 p.expect(L_CURLY);
49
50 // test impl_item_items
51 // impl F {
52 // type A = i32;
53 // const B: i32 = 92;
54 // fn foo() {}
55 // fn bar(&self) {}
56 // }
57 while !p.at(EOF) && !p.at(R_CURLY) {
58 item_or_macro(p, true);
59 }
60 p.expect(R_CURLY);
61}
62
63fn choose_type_params_over_qpath(p: &Parser) -> bool {
64 // There's an ambiguity between generic parameters and qualified paths in impls.
65 // If we see `<` it may start both, so we have to inspect some following tokens.
66 // The following combinations can only start generics,
67 // but not qualified paths (with one exception):
68 // `<` `>` - empty generic parameters
69 // `<` `#` - generic parameters with attributes
70 // `<` (LIFETIME|IDENT) `>` - single generic parameter
71 // `<` (LIFETIME|IDENT) `,` - first generic parameter in a list
72 // `<` (LIFETIME|IDENT) `:` - generic parameter with bounds
73 // `<` (LIFETIME|IDENT) `=` - generic parameter with a default
74 // The only truly ambiguous case is
75 // `<` IDENT `>` `::` IDENT ...
76 // we disambiguate it in favor of generics (`impl<T> ::absolute::Path<T> { ... }`)
77 // because this is what almost always expected in practice, qualified paths in impls
78 // (`impl <Type>::AssocTy { ... }`) aren't even allowed by type checker at the moment.
79 if !p.at(L_ANGLE) {
80 return false;
81 }
82 if p.nth(1) == POUND || p.nth(1) == R_ANGLE {
83 return true;
84 }
85 (p.nth(1) == LIFETIME || p.nth(1) == IDENT)
86 && (p.nth(2) == R_ANGLE || p.nth(2) == COMMA || p.nth(2) == COLON || p.nth(2) == EQ)
87}
diff --git a/crates/libsyntax2/src/grammar/items/use_item.rs b/crates/libsyntax2/src/grammar/items/use_item.rs
new file mode 100644
index 000000000..a3f7f0da8
--- /dev/null
+++ b/crates/libsyntax2/src/grammar/items/use_item.rs
@@ -0,0 +1,66 @@
1use super::*;
2
3pub(super) fn use_item(p: &mut Parser) {
4 assert!(p.at(USE_KW));
5 p.bump();
6 use_tree(p);
7 p.expect(SEMI);
8}
9
10fn use_tree(p: &mut Parser) {
11 let la = p.nth(1);
12 let m = p.start();
13 match (p.current(), la) {
14 (STAR, _) => p.bump(),
15 (COLONCOLON, STAR) => {
16 p.bump();
17 p.bump();
18 }
19 (L_CURLY, _) | (COLONCOLON, L_CURLY) => {
20 if p.at(COLONCOLON) {
21 p.bump();
22 }
23 nested_trees(p);
24 }
25 _ if paths::is_path_start(p) => {
26 paths::use_path(p);
27 match p.current() {
28 AS_KW => {
29 alias(p);
30 }
31 COLONCOLON => {
32 p.bump();
33 match p.current() {
34 STAR => {
35 p.bump();
36 }
37 L_CURLY => nested_trees(p),
38 _ => {
39 // is this unreachable?
40 p.error("expected `{` or `*`");
41 }
42 }
43 }
44 _ => (),
45 }
46 }
47 _ => {
48 m.abandon(p);
49 p.err_and_bump("expected one of `*`, `::`, `{`, `self`, `super`, `indent`");
50 return;
51 }
52 }
53 m.complete(p, USE_TREE);
54}
55
56fn nested_trees(p: &mut Parser) {
57 assert!(p.at(L_CURLY));
58 p.bump();
59 while !p.at(EOF) && !p.at(R_CURLY) {
60 use_tree(p);
61 if !p.at(R_CURLY) {
62 p.expect(COMMA);
63 }
64 }
65 p.expect(R_CURLY);
66}
diff --git a/crates/libsyntax2/src/grammar/mod.rs b/crates/libsyntax2/src/grammar/mod.rs
new file mode 100644
index 000000000..e1329044d
--- /dev/null
+++ b/crates/libsyntax2/src/grammar/mod.rs
@@ -0,0 +1,161 @@
1//! This is the actual "grammar" of the Rust language.
2//!
3//! Each function in this module and its children corresponds
4//! to a production of the format grammar. Submodules roughly
5//! correspond to different *areas* of the grammar. By convention,
6//! each submodule starts with `use super::*` import and exports
7//! "public" productions via `pub(super)`.
8//!
9//! See docs for `Parser` to learn about API, available to the grammar,
10//! and see docs for `Event` to learn how this actually manages to
11//! produce parse trees.
12//!
13//! Code in this module also contains inline tests, which start with
14//! `// test name-of-the-test` comment and look like this:
15//!
16//! ```
17//! // test function_with_zero_parameters
18//! // fn foo() {}
19//! ```
20//!
21//! After adding a new inline-test, run `cargo collect-tests` to extract
22//! it as a standalone text-fixture into `tests/data/parser/inline`, and
23//! run `cargo test` once to create the "gold" value.
24mod attributes;
25mod expressions;
26mod items;
27mod params;
28mod paths;
29mod patterns;
30mod type_args;
31mod type_params;
32mod types;
33
34use {
35 parser_api::{CompletedMarker, Parser, TokenSet},
36 SyntaxKind::{self, *},
37};
38
39pub(crate) fn file(p: &mut Parser) {
40 let file = p.start();
41 p.eat(SHEBANG);
42 items::mod_contents(p, false);
43 file.complete(p, FILE);
44}
45
46
47#[derive(Clone, Copy, PartialEq, Eq)]
48enum BlockLike {
49 Block,
50 NotBlock,
51}
52
53impl BlockLike {
54 fn is_block(self) -> bool { self == BlockLike::Block }
55}
56
57fn visibility(p: &mut Parser) {
58 match p.current() {
59 PUB_KW => {
60 let m = p.start();
61 p.bump();
62 if p.at(L_PAREN) {
63 match p.nth(1) {
64 // test crate_visibility
65 // pub(crate) struct S;
66 // pub(self) struct S;
67 // pub(self) struct S;
68 // pub(self) struct S;
69 CRATE_KW | SELF_KW | SUPER_KW => {
70 p.bump();
71 p.bump();
72 p.expect(R_PAREN);
73 }
74 IN_KW => {
75 p.bump();
76 p.bump();
77 paths::use_path(p);
78 p.expect(R_PAREN);
79 }
80 _ => (),
81 }
82 }
83 m.complete(p, VISIBILITY);
84 }
85 // test crate_keyword_vis
86 // crate fn main() { }
87 CRATE_KW => {
88 let m = p.start();
89 p.bump();
90 m.complete(p, VISIBILITY);
91 }
92 _ => (),
93 }
94}
95fn alias(p: &mut Parser) -> bool {
96 if p.at(AS_KW) {
97 let alias = p.start();
98 p.bump();
99 name(p);
100 alias.complete(p, ALIAS);
101 }
102 true //FIXME: return false if three are errors
103}
104
105fn abi(p: &mut Parser) {
106 assert!(p.at(EXTERN_KW));
107 let abi = p.start();
108 p.bump();
109 match p.current() {
110 STRING | RAW_STRING => p.bump(),
111 _ => (),
112 }
113 abi.complete(p, ABI);
114}
115
116fn fn_ret_type(p: &mut Parser) -> bool {
117 if p.at(THIN_ARROW) {
118 p.bump();
119 types::type_(p);
120 true
121 } else {
122 false
123 }
124}
125
126fn name(p: &mut Parser) {
127 if p.at(IDENT) {
128 let m = p.start();
129 p.bump();
130 m.complete(p, NAME);
131 } else {
132 p.error("expected a name");
133 }
134}
135
136fn name_ref(p: &mut Parser) {
137 if p.at(IDENT) {
138 let m = p.start();
139 p.bump();
140 m.complete(p, NAME_REF);
141 } else {
142 p.error("expected identifier");
143 }
144}
145
146fn error_block(p: &mut Parser, message: &str) {
147 assert!(p.at(L_CURLY));
148 let err = p.start();
149 p.error(message);
150 p.bump();
151 let mut level: u32 = 1;
152 while level > 0 && !p.at(EOF) {
153 match p.current() {
154 L_CURLY => level += 1,
155 R_CURLY => level -= 1,
156 _ => (),
157 }
158 p.bump();
159 }
160 err.complete(p, ERROR);
161}
diff --git a/crates/libsyntax2/src/grammar/params.rs b/crates/libsyntax2/src/grammar/params.rs
new file mode 100644
index 000000000..32e905cb2
--- /dev/null
+++ b/crates/libsyntax2/src/grammar/params.rs
@@ -0,0 +1,116 @@
1use super::*;
2
3// test param_list
4// fn a() {}
5// fn b(x: i32) {}
6// fn c(x: i32, ) {}
7// fn d(x: i32, y: ()) {}
8pub(super) fn param_list(p: &mut Parser) {
9 list_(p, Flavor::Normal)
10}
11
12// test param_list_opt_patterns
13// fn foo<F: FnMut(&mut Foo<'a>)>(){}
14pub(super) fn param_list_opt_patterns(p: &mut Parser) {
15 list_(p, Flavor::OptionalPattern)
16}
17
18pub(super) fn param_list_opt_types(p: &mut Parser) {
19 list_(p, Flavor::OptionalType)
20}
21
22#[derive(Clone, Copy, Eq, PartialEq)]
23enum Flavor {
24 OptionalType,
25 OptionalPattern,
26 Normal,
27}
28
29impl Flavor {
30 fn type_required(self) -> bool {
31 match self {
32 Flavor::OptionalType => false,
33 _ => true,
34 }
35 }
36}
37
38fn list_(p: &mut Parser, flavor: Flavor) {
39 let (bra, ket) = if flavor.type_required() {
40 (L_PAREN, R_PAREN)
41 } else {
42 (PIPE, PIPE)
43 };
44 assert!(p.at(bra));
45 let m = p.start();
46 p.bump();
47 if flavor.type_required() {
48 self_param(p);
49 }
50 while !p.at(EOF) && !p.at(ket) {
51 value_parameter(p, flavor);
52 if !p.at(ket) {
53 p.expect(COMMA);
54 }
55 }
56 p.expect(ket);
57 m.complete(p, PARAM_LIST);
58}
59
60fn value_parameter(p: &mut Parser, flavor: Flavor) {
61 let m = p.start();
62 match flavor {
63 Flavor::OptionalType | Flavor::Normal => {
64 patterns::pattern(p);
65 if p.at(COLON) || flavor.type_required() {
66 types::ascription(p)
67 }
68 },
69 // test value_parameters_no_patterns
70 // type F = Box<Fn(a: i32, &b: &i32, &mut c: &i32, ())>;
71 Flavor::OptionalPattern => {
72 let la0 = p.current();
73 let la1 = p.nth(1);
74 let la2 = p.nth(2);
75 let la3 = p.nth(3);
76 if la0 == IDENT && la1 == COLON
77 || la0 == AMP && la1 == IDENT && la2 == COLON
78 || la0 == AMP && la1 == MUT_KW && la2 == IDENT && la3 == COLON {
79 patterns::pattern(p);
80 types::ascription(p);
81 } else {
82 types::type_(p);
83 }
84 },
85 }
86 m.complete(p, PARAM);
87}
88
89// test self_param
90// impl S {
91// fn a(self) {}
92// fn b(&self,) {}
93// fn c(&'a self,) {}
94// fn d(&'a mut self, x: i32) {}
95// }
96fn self_param(p: &mut Parser) {
97 let la1 = p.nth(1);
98 let la2 = p.nth(2);
99 let la3 = p.nth(3);
100 let n_toks = match (p.current(), la1, la2, la3) {
101 (SELF_KW, _, _, _) => 1,
102 (AMP, SELF_KW, _, _) => 2,
103 (AMP, MUT_KW, SELF_KW, _) => 3,
104 (AMP, LIFETIME, SELF_KW, _) => 3,
105 (AMP, LIFETIME, MUT_KW, SELF_KW) => 4,
106 _ => return,
107 };
108 let m = p.start();
109 for _ in 0..n_toks {
110 p.bump();
111 }
112 m.complete(p, SELF_PARAM);
113 if !p.at(R_PAREN) {
114 p.expect(COMMA);
115 }
116}
diff --git a/crates/libsyntax2/src/grammar/paths.rs b/crates/libsyntax2/src/grammar/paths.rs
new file mode 100644
index 000000000..c277e2a6b
--- /dev/null
+++ b/crates/libsyntax2/src/grammar/paths.rs
@@ -0,0 +1,86 @@
1use super::*;
2
3pub(super) fn is_path_start(p: &Parser) -> bool {
4 match p.current() {
5 IDENT | SELF_KW | SUPER_KW | COLONCOLON => true,
6 _ => false,
7 }
8}
9
10pub(super) fn use_path(p: &mut Parser) {
11 path(p, Mode::Use)
12}
13
14pub(super) fn type_path(p: &mut Parser) {
15 path(p, Mode::Type)
16}
17
18pub(super) fn expr_path(p: &mut Parser) {
19 path(p, Mode::Expr)
20}
21
22#[derive(Clone, Copy, Eq, PartialEq)]
23enum Mode {
24 Use,
25 Type,
26 Expr,
27}
28
29fn path(p: &mut Parser, mode: Mode) {
30 if !is_path_start(p) {
31 return;
32 }
33 let path = p.start();
34 path_segment(p, mode, true);
35 let mut qual = path.complete(p, PATH);
36 loop {
37 let use_tree = match p.nth(1) {
38 STAR | L_CURLY => true,
39 _ => false,
40 };
41 if p.at(COLONCOLON) && !use_tree {
42 let path = qual.precede(p);
43 p.bump();
44 path_segment(p, mode, false);
45 let path = path.complete(p, PATH);
46 qual = path;
47 } else {
48 break;
49 }
50 }
51}
52
53fn path_segment(p: &mut Parser, mode: Mode, first: bool) {
54 let segment = p.start();
55 if first {
56 p.eat(COLONCOLON);
57 }
58 match p.current() {
59 IDENT => {
60 name_ref(p);
61 path_generic_args(p, mode);
62 }
63 SELF_KW | SUPER_KW => p.bump(),
64 _ => {
65 p.error("expected identifier");
66 }
67 };
68 segment.complete(p, PATH_SEGMENT);
69}
70
71fn path_generic_args(p: &mut Parser, mode: Mode) {
72 match mode {
73 Mode::Use => return,
74 Mode::Type => {
75 // test path_fn_trait_args
76 // type F = Box<Fn(x: i32) -> ()>;
77 if p.at(L_PAREN) {
78 params::param_list_opt_patterns(p);
79 fn_ret_type(p);
80 } else {
81 type_args::type_arg_list(p, false)
82 }
83 },
84 Mode::Expr => type_args::type_arg_list(p, true),
85 }
86}
diff --git a/crates/libsyntax2/src/grammar/patterns.rs b/crates/libsyntax2/src/grammar/patterns.rs
new file mode 100644
index 000000000..436f3b26d
--- /dev/null
+++ b/crates/libsyntax2/src/grammar/patterns.rs
@@ -0,0 +1,204 @@
1use super::*;
2
3pub(super) fn pattern(p: &mut Parser) {
4 if let Some(lhs) = atom_pat(p) {
5 // test range_pat
6 // fn main() {
7 // match 92 { 0 ... 100 => () }
8 // }
9 if p.at(DOTDOTDOT) {
10 let m = lhs.precede(p);
11 p.bump();
12 atom_pat(p);
13 m.complete(p, RANGE_PAT);
14 }
15 }
16}
17
18fn atom_pat(p: &mut Parser) -> Option<CompletedMarker> {
19 let la0 = p.nth(0);
20 let la1 = p.nth(1);
21 if la0 == REF_KW || la0 == MUT_KW
22 || (la0 == IDENT && !(la1 == COLONCOLON || la1 == L_PAREN || la1 == L_CURLY)) {
23 return Some(bind_pat(p, true));
24 }
25 if paths::is_path_start(p) {
26 return Some(path_pat(p));
27 }
28
29 // test literal_pattern
30 // fn main() {
31 // match () {
32 // 92 => (),
33 // 'c' => (),
34 // "hello" => (),
35 // }
36 // }
37 match expressions::literal(p) {
38 Some(m) => return Some(m),
39 None => (),
40 }
41
42 let m = match la0 {
43 UNDERSCORE => placeholder_pat(p),
44 AMP => ref_pat(p),
45 L_PAREN => tuple_pat(p),
46 L_BRACK => slice_pat(p),
47 _ => {
48 p.err_and_bump("expected pattern");
49 return None;
50 }
51 };
52 Some(m)
53}
54
55// test path_part
56// fn foo() {
57// let foo::Bar = ();
58// let ::Bar = ();
59// let Bar { .. } = ();
60// let Bar(..) = ();
61// }
62fn path_pat(p: &mut Parser) -> CompletedMarker {
63 let m = p.start();
64 paths::expr_path(p);
65 let kind = match p.current() {
66 L_PAREN => {
67 tuple_pat_fields(p);
68 TUPLE_STRUCT_PAT
69 }
70 L_CURLY => {
71 struct_pat_fields(p);
72 STRUCT_PAT
73 }
74 _ => PATH_PAT
75 };
76 m.complete(p, kind)
77}
78
79// test tuple_pat_fields
80// fn foo() {
81// let S() = ();
82// let S(_) = ();
83// let S(_,) = ();
84// let S(_, .. , x) = ();
85// }
86fn tuple_pat_fields(p: &mut Parser) {
87 assert!(p.at(L_PAREN));
88 p.bump();
89 while !p.at(EOF) && !p.at(R_PAREN) {
90 match p.current() {
91 DOTDOT => p.bump(),
92 _ => pattern(p),
93 }
94 if !p.at(R_PAREN) {
95 p.expect(COMMA);
96 }
97 }
98 p.expect(R_PAREN);
99}
100
101// test struct_pat_fields
102// fn foo() {
103// let S {} = ();
104// let S { f, ref mut g } = ();
105// let S { h: _, ..} = ();
106// let S { h: _, } = ();
107// }
108fn struct_pat_fields(p: &mut Parser) {
109 assert!(p.at(L_CURLY));
110 p.bump();
111 while !p.at(EOF) && !p.at(R_CURLY) {
112 match p.current() {
113 DOTDOT => p.bump(),
114 IDENT if p.nth(1) == COLON => {
115 p.bump();
116 p.bump();
117 pattern(p);
118 }
119 _ => {
120 bind_pat(p, false);
121 }
122 }
123 if !p.at(R_CURLY) {
124 p.expect(COMMA);
125 }
126 }
127 p.expect(R_CURLY);
128}
129
130// test placeholder_pat
131// fn main() { let _ = (); }
132fn placeholder_pat(p: &mut Parser) -> CompletedMarker {
133 assert!(p.at(UNDERSCORE));
134 let m = p.start();
135 p.bump();
136 m.complete(p, PLACEHOLDER_PAT)
137}
138
139// test ref_pat
140// fn main() {
141// let &a = ();
142// let &mut b = ();
143// }
144fn ref_pat(p: &mut Parser) -> CompletedMarker {
145 assert!(p.at(AMP));
146 let m = p.start();
147 p.bump();
148 p.eat(MUT_KW);
149 pattern(p);
150 m.complete(p, REF_PAT)
151}
152
153// test tuple_pat
154// fn main() {
155// let (a, b, ..) = ();
156// }
157fn tuple_pat(p: &mut Parser) -> CompletedMarker {
158 assert!(p.at(L_PAREN));
159 let m = p.start();
160 tuple_pat_fields(p);
161 m.complete(p, TUPLE_PAT)
162}
163
164// test slice_pat
165// fn main() {
166// let [a, b, ..] = [];
167// }
168fn slice_pat(p: &mut Parser) -> CompletedMarker {
169 assert!(p.at(L_BRACK));
170 let m = p.start();
171 p.bump();
172 while !p.at(EOF) && !p.at(R_BRACK) {
173 match p.current() {
174 DOTDOT => p.bump(),
175 _ => pattern(p),
176 }
177 if !p.at(R_BRACK) {
178 p.expect(COMMA);
179 }
180 }
181 p.expect(R_BRACK);
182
183 m.complete(p, SLICE_PAT)
184}
185
186// test bind_pat
187// fn main() {
188// let a = ();
189// let mut b = ();
190// let ref c = ();
191// let ref mut d = ();
192// let e @ _ = ();
193// let ref mut f @ g @ _ = ();
194// }
195fn bind_pat(p: &mut Parser, with_at: bool) -> CompletedMarker {
196 let m = p.start();
197 p.eat(REF_KW);
198 p.eat(MUT_KW);
199 name(p);
200 if with_at && p.eat(AT) {
201 pattern(p);
202 }
203 m.complete(p, BIND_PAT)
204}
diff --git a/crates/libsyntax2/src/grammar/type_args.rs b/crates/libsyntax2/src/grammar/type_args.rs
new file mode 100644
index 000000000..5b960f10b
--- /dev/null
+++ b/crates/libsyntax2/src/grammar/type_args.rs
@@ -0,0 +1,48 @@
1use super::*;
2
3pub(super) fn type_arg_list(p: &mut Parser, colon_colon_required: bool) {
4 let m;
5 match (colon_colon_required, p.nth(0), p.nth(1)) {
6 (_, COLONCOLON, L_ANGLE) => {
7 m = p.start();
8 p.bump();
9 p.bump();
10 }
11 (false, L_ANGLE, _) => {
12 m = p.start();
13 p.bump();
14 }
15 _ => return,
16 };
17
18 while !p.at(EOF) && !p.at(R_ANGLE) {
19 type_arg(p);
20 if !p.at(R_ANGLE) && !p.expect(COMMA) {
21 break;
22 }
23 }
24 p.expect(R_ANGLE);
25 m.complete(p, TYPE_ARG_LIST);
26}
27
28// test type_arg
29// type A = B<'static, i32, Item=u64>
30fn type_arg(p: &mut Parser) {
31 let m = p.start();
32 match p.current() {
33 LIFETIME => {
34 p.bump();
35 m.complete(p, LIFETIME_ARG);
36 }
37 IDENT if p.nth(1) == EQ => {
38 name_ref(p);
39 p.bump();
40 types::type_(p);
41 m.complete(p, ASSOC_TYPE_ARG);
42 }
43 _ => {
44 types::type_(p);
45 m.complete(p, TYPE_ARG);
46 }
47 }
48}
diff --git a/crates/libsyntax2/src/grammar/type_params.rs b/crates/libsyntax2/src/grammar/type_params.rs
new file mode 100644
index 000000000..0a3e8fd07
--- /dev/null
+++ b/crates/libsyntax2/src/grammar/type_params.rs
@@ -0,0 +1,127 @@
1use super::*;
2
3pub(super) fn type_param_list(p: &mut Parser) {
4 if !p.at(L_ANGLE) {
5 return;
6 }
7 let m = p.start();
8 p.bump();
9
10 while !p.at(EOF) && !p.at(R_ANGLE) {
11 match p.current() {
12 LIFETIME => lifetime_param(p),
13 IDENT => type_param(p),
14 _ => p.err_and_bump("expected type parameter"),
15 }
16 if !p.at(R_ANGLE) && !p.expect(COMMA) {
17 break;
18 }
19 }
20 p.expect(R_ANGLE);
21 m.complete(p, TYPE_PARAM_LIST);
22
23 fn lifetime_param(p: &mut Parser) {
24 assert!(p.at(LIFETIME));
25 let m = p.start();
26 p.bump();
27 if p.at(COLON) {
28 lifetime_bounds(p);
29 }
30 m.complete(p, LIFETIME_PARAM);
31 }
32
33 fn type_param(p: &mut Parser) {
34 assert!(p.at(IDENT));
35 let m = p.start();
36 name(p);
37 if p.at(COLON) {
38 bounds(p);
39 }
40 // test type_param_default
41 // struct S<T = i32>;
42 if p.at(EQ) {
43 p.bump();
44 types::type_(p)
45 }
46 m.complete(p, TYPE_PARAM);
47 }
48}
49
50// test type_param_bounds
51// struct S<T: 'a + ?Sized + (Copy)>;
52pub(super) fn bounds(p: &mut Parser) {
53 assert!(p.at(COLON));
54 p.bump();
55 bounds_without_colon(p);
56}
57
58fn lifetime_bounds(p: &mut Parser) {
59 assert!(p.at(COLON));
60 p.bump();
61 while p.at(LIFETIME) {
62 p.bump();
63 if !p.eat(PLUS) {
64 break;
65 }
66 }
67}
68
69pub(super) fn bounds_without_colon(p: &mut Parser) {
70 loop {
71 let has_paren = p.eat(L_PAREN);
72 p.eat(QUESTION);
73 if p.at(FOR_KW) {
74 //TODO
75 }
76 if p.at(LIFETIME) {
77 p.bump();
78 } else if paths::is_path_start(p) {
79 paths::type_path(p);
80 } else {
81 break;
82 }
83 if has_paren {
84 p.expect(R_PAREN);
85 }
86 if !p.eat(PLUS) {
87 break;
88 }
89 }
90}
91
92// test where_clause
93// fn foo()
94// where
95// 'a: 'b + 'c,
96// T: Clone + Copy + 'static,
97// Iterator::Item: 'a,
98// {}
99pub(super) fn where_clause(p: &mut Parser) {
100 if !p.at(WHERE_KW) {
101 return;
102 }
103 let m = p.start();
104 p.bump();
105 loop {
106 if !(paths::is_path_start(p) || p.current() == LIFETIME) {
107 break
108 }
109 where_predicate(p);
110 if p.current() != L_CURLY && p.current() != SEMI {
111 p.expect(COMMA);
112 }
113 }
114 m.complete(p, WHERE_CLAUSE);
115}
116
117fn where_predicate(p: &mut Parser) {
118 let m = p.start();
119 if p.at(LIFETIME) {
120 p.eat(LIFETIME);
121 lifetime_bounds(p)
122 } else {
123 types::path_type(p);
124 bounds(p);
125 }
126 m.complete(p, WHERE_PRED);
127}
diff --git a/crates/libsyntax2/src/grammar/types.rs b/crates/libsyntax2/src/grammar/types.rs
new file mode 100644
index 000000000..0d8c6bfba
--- /dev/null
+++ b/crates/libsyntax2/src/grammar/types.rs
@@ -0,0 +1,212 @@
1use super::*;
2
3pub(super) fn type_(p: &mut Parser) {
4 match p.current() {
5 L_PAREN => paren_or_tuple_type(p),
6 EXCL => never_type(p),
7 STAR => pointer_type(p),
8 L_BRACK => array_or_slice_type(p),
9 AMP => reference_type(p),
10 UNDERSCORE => placeholder_type(p),
11 FN_KW | UNSAFE_KW | EXTERN_KW => fn_pointer_type(p),
12 FOR_KW => for_type(p),
13 IMPL_KW => impl_trait_type(p),
14 _ if paths::is_path_start(p) => path_type(p),
15 _ => {
16 p.error("expected type");
17 }
18 }
19}
20
21pub(super) fn ascription(p: &mut Parser) {
22 p.expect(COLON);
23 type_(p)
24}
25
26fn type_no_plus(p: &mut Parser) {
27 type_(p);
28}
29
30fn paren_or_tuple_type(p: &mut Parser) {
31 assert!(p.at(L_PAREN));
32 let m = p.start();
33 p.bump();
34 let mut n_types: u32 = 0;
35 let mut trailing_comma: bool = false;
36 while !p.at(EOF) && !p.at(R_PAREN) {
37 n_types += 1;
38 type_(p);
39 if p.eat(COMMA) {
40 trailing_comma = true;
41 } else {
42 trailing_comma = false;
43 break;
44 }
45 }
46 p.expect(R_PAREN);
47
48 let kind = if n_types == 1 && !trailing_comma {
49 // test paren_type
50 // type T = (i32);
51 PAREN_TYPE
52 } else {
53 // test unit_type
54 // type T = ();
55
56 // test singleton_tuple_type
57 // type T = (i32,);
58 TUPLE_TYPE
59 };
60 m.complete(p, kind);
61}
62
63// test never_type
64// type Never = !;
65fn never_type(p: &mut Parser) {
66 assert!(p.at(EXCL));
67 let m = p.start();
68 p.bump();
69 m.complete(p, NEVER_TYPE);
70}
71
72fn pointer_type(p: &mut Parser) {
73 assert!(p.at(STAR));
74 let m = p.start();
75 p.bump();
76
77 match p.current() {
78 // test pointer_type_mut
79 // type M = *mut ();
80 // type C = *mut ();
81 MUT_KW | CONST_KW => p.bump(),
82 _ => {
83 // test pointer_type_no_mutability
84 // type T = *();
85 p.error(
86 "expected mut or const in raw pointer type \
87 (use `*mut T` or `*const T` as appropriate)",
88 );
89 }
90 };
91
92 type_no_plus(p);
93 m.complete(p, POINTER_TYPE);
94}
95
96fn array_or_slice_type(p: &mut Parser) {
97 assert!(p.at(L_BRACK));
98 let m = p.start();
99 p.bump();
100
101 type_(p);
102 let kind = match p.current() {
103 // test slice_type
104 // type T = [()];
105 R_BRACK => {
106 p.bump();
107 SLICE_TYPE
108 }
109
110 // test array_type
111 // type T = [(); 92];
112 SEMI => {
113 p.bump();
114 expressions::expr(p);
115 p.expect(R_BRACK);
116 ARRAY_TYPE
117 }
118 // test array_type_missing_semi
119 // type T = [() 92];
120 _ => {
121 p.error("expected `;` or `]`");
122 SLICE_TYPE
123 }
124 };
125 m.complete(p, kind);
126}
127
128// test reference_type;
129// type A = &();
130// type B = &'static ();
131// type C = &mut ();
132fn reference_type(p: &mut Parser) {
133 assert!(p.at(AMP));
134 let m = p.start();
135 p.bump();
136 p.eat(LIFETIME);
137 p.eat(MUT_KW);
138 type_no_plus(p);
139 m.complete(p, REFERENCE_TYPE);
140}
141
142// test placeholder_type
143// type Placeholder = _;
144fn placeholder_type(p: &mut Parser) {
145 assert!(p.at(UNDERSCORE));
146 let m = p.start();
147 p.bump();
148 m.complete(p, PLACEHOLDER_TYPE);
149}
150
151// test fn_pointer_type
152// type A = fn();
153// type B = unsafe fn();
154// type C = unsafe extern "C" fn();
155fn fn_pointer_type(p: &mut Parser) {
156 let m = p.start();
157 p.eat(UNSAFE_KW);
158 if p.at(EXTERN_KW) {
159 abi(p);
160 }
161 // test fn_pointer_type_missing_fn
162 // type F = unsafe ();
163 if !p.eat(FN_KW) {
164 m.abandon(p);
165 p.error("expected `fn`");
166 return;
167 }
168
169 params::param_list_opt_patterns(p);
170 // test fn_pointer_type_with_ret
171 // type F = fn() -> ();
172 fn_ret_type(p);
173 m.complete(p, FN_POINTER_TYPE);
174}
175
176// test for_type
177// type A = for<'a> fn() -> ();
178fn for_type(p: &mut Parser) {
179 assert!(p.at(FOR_KW));
180 let m = p.start();
181 p.bump();
182 type_params::type_param_list(p);
183 type_(p);
184 m.complete(p, FOR_TYPE);
185}
186
187// test impl_trait_type
188// type A = impl Iterator<Item=Foo<'a>> + 'a;
189fn impl_trait_type(p: &mut Parser) {
190 assert!(p.at(IMPL_KW));
191 let m = p.start();
192 p.bump();
193 type_params::bounds_without_colon(p);
194 m.complete(p, IMPL_TRAIT_TYPE);
195}
196
197// test path_type
198// type A = Foo;
199// type B = ::Foo;
200// type C = self::Foo;
201// type D = super::Foo;
202pub(super) fn path_type(p: &mut Parser) {
203 assert!(paths::is_path_start(p));
204 let m = p.start();
205 paths::type_path(p);
206 // test path_type_with_bounds
207 // fn foo() -> Box<T + 'f> {}
208 if p.eat(PLUS) {
209 type_params::bounds_without_colon(p);
210 }
211 m.complete(p, PATH_TYPE);
212}
diff --git a/crates/libsyntax2/src/lexer/classes.rs b/crates/libsyntax2/src/lexer/classes.rs
new file mode 100644
index 000000000..4235d2648
--- /dev/null
+++ b/crates/libsyntax2/src/lexer/classes.rs
@@ -0,0 +1,26 @@
1use unicode_xid::UnicodeXID;
2
3pub fn is_ident_start(c: char) -> bool {
4 (c >= 'a' && c <= 'z')
5 || (c >= 'A' && c <= 'Z')
6 || c == '_'
7 || (c > '\x7f' && UnicodeXID::is_xid_start(c))
8}
9
10pub fn is_ident_continue(c: char) -> bool {
11 (c >= 'a' && c <= 'z')
12 || (c >= 'A' && c <= 'Z')
13 || (c >= '0' && c <= '9')
14 || c == '_'
15 || (c > '\x7f' && UnicodeXID::is_xid_continue(c))
16}
17
18pub fn is_whitespace(c: char) -> bool {
19 //FIXME: use is_pattern_whitespace
20 //https://github.com/behnam/rust-unic/issues/192
21 c.is_whitespace()
22}
23
24pub fn is_dec_digit(c: char) -> bool {
25 '0' <= c && c <= '9'
26}
diff --git a/crates/libsyntax2/src/lexer/comments.rs b/crates/libsyntax2/src/lexer/comments.rs
new file mode 100644
index 000000000..01acb6515
--- /dev/null
+++ b/crates/libsyntax2/src/lexer/comments.rs
@@ -0,0 +1,57 @@
1use lexer::ptr::Ptr;
2
3use SyntaxKind::{self, *};
4
5pub(crate) fn scan_shebang(ptr: &mut Ptr) -> bool {
6 if ptr.next_is('!') && ptr.nnext_is('/') {
7 ptr.bump();
8 ptr.bump();
9 bump_until_eol(ptr);
10 true
11 } else {
12 false
13 }
14}
15
16fn scan_block_comment(ptr: &mut Ptr) -> Option<SyntaxKind> {
17 if ptr.next_is('*') {
18 ptr.bump();
19 let mut depth: u32 = 1;
20 while depth > 0 {
21 if ptr.next_is('*') && ptr.nnext_is('/') {
22 depth -= 1;
23 ptr.bump();
24 ptr.bump();
25 } else if ptr.next_is('/') && ptr.nnext_is('*') {
26 depth += 1;
27 ptr.bump();
28 ptr.bump();
29 } else if ptr.bump().is_none() {
30 break;
31 }
32 }
33 Some(COMMENT)
34 } else {
35 None
36 }
37}
38
39pub(crate) fn scan_comment(ptr: &mut Ptr) -> Option<SyntaxKind> {
40 if ptr.next_is('/') {
41 bump_until_eol(ptr);
42 Some(COMMENT)
43 } else {
44 scan_block_comment(ptr)
45 }
46}
47
48fn bump_until_eol(ptr: &mut Ptr) {
49 loop {
50 if ptr.next_is('\n') || ptr.next_is('\r') && ptr.nnext_is('\n') {
51 return;
52 }
53 if ptr.bump().is_none() {
54 break;
55 }
56 }
57}
diff --git a/crates/libsyntax2/src/lexer/mod.rs b/crates/libsyntax2/src/lexer/mod.rs
new file mode 100644
index 000000000..f8fdc41ac
--- /dev/null
+++ b/crates/libsyntax2/src/lexer/mod.rs
@@ -0,0 +1,209 @@
1mod classes;
2mod comments;
3mod numbers;
4mod ptr;
5mod strings;
6
7use {
8 SyntaxKind::{self, *},
9 TextUnit,
10};
11
12use self::{
13 classes::*,
14 comments::{scan_comment, scan_shebang},
15 numbers::scan_number,
16 ptr::Ptr,
17 strings::{
18 is_string_literal_start, scan_byte_char_or_string, scan_char, scan_raw_string, scan_string,
19 },
20};
21
22/// A token of Rust source.
23#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
24pub struct Token {
25 /// The kind of token.
26 pub kind: SyntaxKind,
27 /// The length of the token.
28 pub len: TextUnit,
29}
30
31/// Break a string up into its component tokens
32pub fn tokenize(text: &str) -> Vec<Token> {
33 let mut text = text;
34 let mut acc = Vec::new();
35 while !text.is_empty() {
36 let token = next_token(text);
37 acc.push(token);
38 let len: u32 = token.len.into();
39 text = &text[len as usize..];
40 }
41 acc
42}
43
44/// Get the next token from a string
45pub fn next_token(text: &str) -> Token {
46 assert!(!text.is_empty());
47 let mut ptr = Ptr::new(text);
48 let c = ptr.bump().unwrap();
49 let kind = next_token_inner(c, &mut ptr);
50 let len = ptr.into_len();
51 Token { kind, len }
52}
53
54fn next_token_inner(c: char, ptr: &mut Ptr) -> SyntaxKind {
55 if is_whitespace(c) {
56 ptr.bump_while(is_whitespace);
57 return WHITESPACE;
58 }
59
60 match c {
61 '#' => if scan_shebang(ptr) {
62 return SHEBANG;
63 },
64 '/' => if let Some(kind) = scan_comment(ptr) {
65 return kind;
66 },
67 _ => (),
68 }
69
70 let ident_start = is_ident_start(c) && !is_string_literal_start(c, ptr.next(), ptr.nnext());
71 if ident_start {
72 return scan_ident(c, ptr);
73 }
74
75 if is_dec_digit(c) {
76 let kind = scan_number(c, ptr);
77 scan_literal_suffix(ptr);
78 return kind;
79 }
80
81 // One-byte tokens.
82 if let Some(kind) = SyntaxKind::from_char(c) {
83 return kind;
84 }
85
86 match c {
87 // Multi-byte tokens.
88 '.' => {
89 return match (ptr.next(), ptr.nnext()) {
90 (Some('.'), Some('.')) => {
91 ptr.bump();
92 ptr.bump();
93 DOTDOTDOT
94 }
95 (Some('.'), Some('=')) => {
96 ptr.bump();
97 ptr.bump();
98 DOTDOTEQ
99 }
100 (Some('.'), _) => {
101 ptr.bump();
102 DOTDOT
103 }
104 _ => DOT,
105 };
106 }
107 ':' => {
108 return match ptr.next() {
109 Some(':') => {
110 ptr.bump();
111 COLONCOLON
112 }
113 _ => COLON,
114 };
115 }
116 '=' => {
117 return match ptr.next() {
118 Some('=') => {
119 ptr.bump();
120 EQEQ
121 }
122 Some('>') => {
123 ptr.bump();
124 FAT_ARROW
125 }
126 _ => EQ,
127 };
128 }
129 '!' => {
130 return match ptr.next() {
131 Some('=') => {
132 ptr.bump();
133 NEQ
134 }
135 _ => EXCL,
136 };
137 }
138 '-' => {
139 return if ptr.next_is('>') {
140 ptr.bump();
141 THIN_ARROW
142 } else {
143 MINUS
144 };
145 }
146
147 // If the character is an ident start not followed by another single
148 // quote, then this is a lifetime name:
149 '\'' => {