]> git.lizzy.rs Git - rust.git/commitdiff
Merge #215
authorbors[bot] <bors[bot]@users.noreply.github.com>
Sat, 17 Nov 2018 12:50:29 +0000 (12:50 +0000)
committerbors[bot] <bors[bot]@users.noreply.github.com>
Sat, 17 Nov 2018 12:50:29 +0000 (12:50 +0000)
215: Add Travis Windows build r=matklad a=phansch

This adds the Windows build to Travis.

I had to use the `dos2unix` utility on the parser directory to fix some incorrect
line endings. I'm not sure where they are coming from but I guess git is
converting them automatically when cloning on Windows.

Closes #139

Co-authored-by: Philipp Hansch <dev@phansch.net>
238 files changed:
.travis.yml
Cargo.lock
ROADMAP.md [new file with mode: 0644]
crates/gen_lsp_server/src/lib.rs
crates/ra_analysis/src/completion.rs [deleted file]
crates/ra_analysis/src/completion/mod.rs [new file with mode: 0644]
crates/ra_analysis/src/completion/reference_completion.rs [new file with mode: 0644]
crates/ra_analysis/src/db.rs
crates/ra_analysis/src/descriptors/function/scope.rs
crates/ra_analysis/src/descriptors/module/imp.rs
crates/ra_analysis/src/descriptors/module/mod.rs
crates/ra_analysis/src/descriptors/module/scope.rs
crates/ra_analysis/src/imp.rs
crates/ra_analysis/src/lib.rs
crates/ra_analysis/src/symbol_index.rs
crates/ra_analysis/src/syntax_ptr.rs
crates/ra_analysis/tests/tests.rs
crates/ra_cli/src/main.rs
crates/ra_editor/src/code_actions.rs
crates/ra_editor/src/extend_selection.rs
crates/ra_editor/src/folding_ranges.rs
crates/ra_editor/src/lib.rs
crates/ra_editor/src/line_index.rs
crates/ra_editor/src/symbols.rs
crates/ra_editor/src/test_utils.rs
crates/ra_editor/src/typing.rs
crates/ra_lsp_server/Cargo.toml
crates/ra_lsp_server/src/caps.rs
crates/ra_lsp_server/src/conv.rs
crates/ra_lsp_server/src/main.rs
crates/ra_lsp_server/src/main_loop/handlers.rs
crates/ra_lsp_server/src/main_loop/mod.rs
crates/ra_lsp_server/tests/heavy_tests/support.rs
crates/ra_syntax/Cargo.toml
crates/ra_syntax/src/ast/generated.rs
crates/ra_syntax/src/ast/generated.rs.tera
crates/ra_syntax/src/ast/mod.rs
crates/ra_syntax/src/grammar.ron
crates/ra_syntax/src/grammar/mod.rs
crates/ra_syntax/src/lexer/ptr.rs
crates/ra_syntax/src/lib.rs
crates/ra_syntax/src/parser_impl/event.rs
crates/ra_syntax/src/reparsing.rs
crates/ra_syntax/src/string_lexing/byte.rs [new file with mode: 0644]
crates/ra_syntax/src/string_lexing/byte_string.rs [new file with mode: 0644]
crates/ra_syntax/src/string_lexing/char.rs [new file with mode: 0644]
crates/ra_syntax/src/string_lexing/mod.rs
crates/ra_syntax/src/string_lexing/parser.rs [new file with mode: 0644]
crates/ra_syntax/src/string_lexing/string.rs [new file with mode: 0644]
crates/ra_syntax/src/syntax_kinds/generated.rs
crates/ra_syntax/src/utils.rs
crates/ra_syntax/src/validation.rs [deleted file]
crates/ra_syntax/src/validation/byte.rs [new file with mode: 0644]
crates/ra_syntax/src/validation/byte_string.rs [new file with mode: 0644]
crates/ra_syntax/src/validation/char.rs [new file with mode: 0644]
crates/ra_syntax/src/validation/mod.rs [new file with mode: 0644]
crates/ra_syntax/src/validation/string.rs [new file with mode: 0644]
crates/ra_syntax/src/yellow/syntax_error.rs
crates/ra_syntax/tests/data/parser/err/0000_struct_field_missing_comma.txt
crates/ra_syntax/tests/data/parser/err/0001_item_recovery_in_file.txt
crates/ra_syntax/tests/data/parser/err/0002_duplicate_shebang.txt
crates/ra_syntax/tests/data/parser/err/0003_C++_semicolon.txt
crates/ra_syntax/tests/data/parser/err/0004_use_path_bad_segment.txt
crates/ra_syntax/tests/data/parser/err/0005_attribute_recover.txt
crates/ra_syntax/tests/data/parser/err/0006_named_field_recovery.txt
crates/ra_syntax/tests/data/parser/err/0007_stray_curly_in_file.txt
crates/ra_syntax/tests/data/parser/err/0008_item_block_recovery.txt
crates/ra_syntax/tests/data/parser/err/0009_broken_struct_type_parameter.txt
crates/ra_syntax/tests/data/parser/err/0010_unsafe_lambda_block.txt
crates/ra_syntax/tests/data/parser/err/0011_extern_struct.txt
crates/ra_syntax/tests/data/parser/err/0012_broken_lambda.txt
crates/ra_syntax/tests/data/parser/err/0013_invalid_type.txt
crates/ra_syntax/tests/data/parser/err/0014_where_no_bounds.txt
crates/ra_syntax/tests/data/parser/err/0015_curly_in_params.txt
crates/ra_syntax/tests/data/parser/err/0016_missing_semi.txt
crates/ra_syntax/tests/data/parser/err/0017_incomplete_binexpr.txt
crates/ra_syntax/tests/data/parser/err/0018_incomplete_fn.txt
crates/ra_syntax/tests/data/parser/err/0019_let_recover.txt
crates/ra_syntax/tests/data/parser/err/0020_fn_recover.txt
crates/ra_syntax/tests/data/parser/err/0021_incomplete_param.txt
crates/ra_syntax/tests/data/parser/err/0022_bad_exprs.txt
crates/ra_syntax/tests/data/parser/err/0023_mismatched_paren.txt
crates/ra_syntax/tests/data/parser/err/0024_many_type_parens.txt
crates/ra_syntax/tests/data/parser/err/0025_nope.txt
crates/ra_syntax/tests/data/parser/err/0026_imp_recovery.txt
crates/ra_syntax/tests/data/parser/err/0027_incomplere_where_for.txt
crates/ra_syntax/tests/data/parser/inline/0001_const_unsafe_fn.txt
crates/ra_syntax/tests/data/parser/inline/0002_const_fn.txt
crates/ra_syntax/tests/data/parser/inline/0003_extern_block.txt
crates/ra_syntax/tests/data/parser/inline/0004_extern_fn.txt
crates/ra_syntax/tests/data/parser/inline/0005_extern_crate.txt
crates/ra_syntax/tests/data/parser/inline/0007_unsafe_trait.txt
crates/ra_syntax/tests/data/parser/inline/0008_unsafe_impl.txt
crates/ra_syntax/tests/data/parser/inline/0009_unsafe_auto_trait.txt
crates/ra_syntax/tests/data/parser/inline/0010_unsafe_default_impl.txt
crates/ra_syntax/tests/data/parser/inline/0011_unsafe_fn.txt
crates/ra_syntax/tests/data/parser/inline/0012_unsafe_extern_fn.txt
crates/ra_syntax/tests/data/parser/inline/0013_unsafe_block_in_mod.txt
crates/ra_syntax/tests/data/parser/inline/0014_type_item_type_params.txt
crates/ra_syntax/tests/data/parser/inline/0015_type_item.txt
crates/ra_syntax/tests/data/parser/inline/0016_type_item_where_clause.txt
crates/ra_syntax/tests/data/parser/inline/0017_paren_type.txt
crates/ra_syntax/tests/data/parser/inline/0018_unit_type.txt
crates/ra_syntax/tests/data/parser/inline/0019_singleton_tuple_type.txt
crates/ra_syntax/tests/data/parser/inline/0020_never_type.txt
crates/ra_syntax/tests/data/parser/inline/0021_pointer_type_no_mutability.txt
crates/ra_syntax/tests/data/parser/inline/0022_pointer_type_mut.txt
crates/ra_syntax/tests/data/parser/inline/0023_array_type_missing_semi.txt
crates/ra_syntax/tests/data/parser/inline/0024_array_type.txt
crates/ra_syntax/tests/data/parser/inline/0025_slice_type.txt
crates/ra_syntax/tests/data/parser/inline/0026_reference_type;.txt
crates/ra_syntax/tests/data/parser/inline/0027_placeholder_type.txt
crates/ra_syntax/tests/data/parser/inline/0028_fn_pointer_type.txt
crates/ra_syntax/tests/data/parser/inline/0029_fn_pointer_type_missing_fn.txt
crates/ra_syntax/tests/data/parser/inline/0030_fn_pointer_type_with_ret.txt
crates/ra_syntax/tests/data/parser/inline/0031_for_type.txt
crates/ra_syntax/tests/data/parser/inline/0032_path_type.txt
crates/ra_syntax/tests/data/parser/inline/0034_bind_pat.txt
crates/ra_syntax/tests/data/parser/inline/0035_ref_pat.txt
crates/ra_syntax/tests/data/parser/inline/0036_placeholder_pat.txt
crates/ra_syntax/tests/data/parser/inline/0037_crate_visibility.txt
crates/ra_syntax/tests/data/parser/inline/0038_function_ret_type.txt
crates/ra_syntax/tests/data/parser/inline/0039_path_expr.txt
crates/ra_syntax/tests/data/parser/inline/0040_expr_literals.txt
crates/ra_syntax/tests/data/parser/inline/0041_type_param_bounds.txt
crates/ra_syntax/tests/data/parser/inline/0042_type_param_default.txt
crates/ra_syntax/tests/data/parser/inline/0043_call_expr.txt
crates/ra_syntax/tests/data/parser/inline/0044_ref_expr.txt
crates/ra_syntax/tests/data/parser/inline/0045_block.txt
crates/ra_syntax/tests/data/parser/inline/0046_default_impl.txt
crates/ra_syntax/tests/data/parser/inline/0047_impl_item.txt
crates/ra_syntax/tests/data/parser/inline/0048_impl_item_neg.txt
crates/ra_syntax/tests/data/parser/inline/0049_trait_item_list.txt
crates/ra_syntax/tests/data/parser/inline/0050_let_stmt;.txt
crates/ra_syntax/tests/data/parser/inline/0051_method_call_expr.txt
crates/ra_syntax/tests/data/parser/inline/0052_field_expr.txt
crates/ra_syntax/tests/data/parser/inline/0053_block_items.txt
crates/ra_syntax/tests/data/parser/inline/0054_field_pat_list.txt
crates/ra_syntax/tests/data/parser/inline/0055_self_param.txt
crates/ra_syntax/tests/data/parser/inline/0056_trait_item.txt
crates/ra_syntax/tests/data/parser/inline/0057_auto_trait.txt
crates/ra_syntax/tests/data/parser/inline/0058_type_arg.txt
crates/ra_syntax/tests/data/parser/inline/0059_function_where_clause.txt
crates/ra_syntax/tests/data/parser/inline/0060_function_type_params.txt
crates/ra_syntax/tests/data/parser/inline/0061_struct_lit.txt
crates/ra_syntax/tests/data/parser/inline/0063_impl_trait_type.txt
crates/ra_syntax/tests/data/parser/inline/0063_lambda_expr.txt
crates/ra_syntax/tests/data/parser/inline/0064_param_list.txt
crates/ra_syntax/tests/data/parser/inline/0065_if_expr.txt
crates/ra_syntax/tests/data/parser/inline/0066_lambda_expr.txt
crates/ra_syntax/tests/data/parser/inline/0067_block_expr.txt
crates/ra_syntax/tests/data/parser/inline/0068_pub_expr.txt
crates/ra_syntax/tests/data/parser/inline/0068_return_expr.txt
crates/ra_syntax/tests/data/parser/inline/0069_match_arm.txt
crates/ra_syntax/tests/data/parser/inline/0070_match_expr.txt
crates/ra_syntax/tests/data/parser/inline/0071_tuple_pat_fields.txt
crates/ra_syntax/tests/data/parser/inline/0072_path_part.txt
crates/ra_syntax/tests/data/parser/inline/0073_impl_item_list.txt
crates/ra_syntax/tests/data/parser/inline/0074_unary_expr.txt
crates/ra_syntax/tests/data/parser/inline/0075_try_expr.txt
crates/ra_syntax/tests/data/parser/inline/0076_cond.txt
crates/ra_syntax/tests/data/parser/inline/0077_while_expr.txt
crates/ra_syntax/tests/data/parser/inline/0078_mod_contents.txt
crates/ra_syntax/tests/data/parser/inline/0079_cast_expr.txt
crates/ra_syntax/tests/data/parser/inline/0080_tuple_expr.txt
crates/ra_syntax/tests/data/parser/inline/0081_index_expr.txt
crates/ra_syntax/tests/data/parser/inline/0082_tuple_pat.txt
crates/ra_syntax/tests/data/parser/inline/0083_postfix_range.txt
crates/ra_syntax/tests/data/parser/inline/0084_loop_expr.txt
crates/ra_syntax/tests/data/parser/inline/0085_for_expr.txt
crates/ra_syntax/tests/data/parser/inline/0085_match_arms_commas.txt
crates/ra_syntax/tests/data/parser/inline/0086_array_expr.txt
crates/ra_syntax/tests/data/parser/inline/0086_no_semi_after_block.txt
crates/ra_syntax/tests/data/parser/inline/0087_stmt_postfix_expr_ambiguity.txt
crates/ra_syntax/tests/data/parser/inline/0088_stmt_bin_expr_ambiguity.txt
crates/ra_syntax/tests/data/parser/inline/0089_slice_pat.txt
crates/ra_syntax/tests/data/parser/inline/0091_fn_decl.txt
crates/ra_syntax/tests/data/parser/inline/0092_literal_pattern.txt
crates/ra_syntax/tests/data/parser/inline/0093_path_fn_trait_args.txt
crates/ra_syntax/tests/data/parser/inline/0094_range_pat.txt
crates/ra_syntax/tests/data/parser/inline/0095_path_type_with_bounds.txt
crates/ra_syntax/tests/data/parser/inline/0096_value_parameters_no_patterns.txt
crates/ra_syntax/tests/data/parser/inline/0097_param_list_opt_patterns.txt
crates/ra_syntax/tests/data/parser/inline/0098_where_clause.txt
crates/ra_syntax/tests/data/parser/inline/0099_crate_keyword_vis.txt
crates/ra_syntax/tests/data/parser/inline/0100_dyn_trait_type.txt
crates/ra_syntax/tests/data/parser/inline/0101_qual_paths.txt
crates/ra_syntax/tests/data/parser/inline/0102_full_range_expr.txt
crates/ra_syntax/tests/data/parser/inline/0103_field_attrs.txt
crates/ra_syntax/tests/data/parser/inline/0104_arb_self_types.txt
crates/ra_syntax/tests/data/parser/inline/0105_continue_expr.txt
crates/ra_syntax/tests/data/parser/inline/0106_break_expr.txt
crates/ra_syntax/tests/data/parser/inline/0107_label.txt
crates/ra_syntax/tests/data/parser/inline/0108_misplaced_label_err.txt
crates/ra_syntax/tests/data/parser/inline/0109_struct_items.txt
crates/ra_syntax/tests/data/parser/inline/0110_union_items.txt
crates/ra_syntax/tests/data/parser/inline/0111_impl_type.txt
crates/ra_syntax/tests/data/parser/inline/0112_crate_path.txt
crates/ra_syntax/tests/data/parser/inline/0113_where_pred_for.txt
crates/ra_syntax/tests/data/parser/ok/0000_empty.txt
crates/ra_syntax/tests/data/parser/ok/0001_struct_item.txt
crates/ra_syntax/tests/data/parser/ok/0002_struct_item_field.txt
crates/ra_syntax/tests/data/parser/ok/0004_file_shebang.txt
crates/ra_syntax/tests/data/parser/ok/0005_fn_item.txt
crates/ra_syntax/tests/data/parser/ok/0006_inner_attributes.txt
crates/ra_syntax/tests/data/parser/ok/0007_extern_crate.txt
crates/ra_syntax/tests/data/parser/ok/0008_mod_item.txt
crates/ra_syntax/tests/data/parser/ok/0009_use_item.txt
crates/ra_syntax/tests/data/parser/ok/0010_use_path_segments.txt
crates/ra_syntax/tests/data/parser/ok/0011_outer_attribute.txt
crates/ra_syntax/tests/data/parser/ok/0012_visibility.txt
crates/ra_syntax/tests/data/parser/ok/0013_use_path_self_super.txt
crates/ra_syntax/tests/data/parser/ok/0014_use_tree.txt
crates/ra_syntax/tests/data/parser/ok/0015_use_tree.txt
crates/ra_syntax/tests/data/parser/ok/0016_struct_flavors.txt
crates/ra_syntax/tests/data/parser/ok/0017_attr_trailing_comma.txt
crates/ra_syntax/tests/data/parser/ok/0018_struct_type_params.txt
crates/ra_syntax/tests/data/parser/ok/0019_enums.txt
crates/ra_syntax/tests/data/parser/ok/0020_type_param_bounds.txt
crates/ra_syntax/tests/data/parser/ok/0021_extern_fn.txt
crates/ra_syntax/tests/data/parser/ok/0022_empty_extern_block.txt
crates/ra_syntax/tests/data/parser/ok/0023_static_items.txt
crates/ra_syntax/tests/data/parser/ok/0024_const_item.txt
crates/ra_syntax/tests/data/parser/ok/0025_extern_fn_in_block.txt
crates/ra_syntax/tests/data/parser/ok/0026_const_fn_in_block.txt
crates/ra_syntax/tests/data/parser/ok/0027_unsafe_fn_in_block.txt
crates/ra_syntax/tests/data/parser/ok/0028_operator_binding_power.txt
crates/ra_syntax/tests/data/parser/ok/0029_range_forms.txt
crates/ra_syntax/tests/data/parser/ok/0030_traits.txt
crates/ra_syntax/tests/data/parser/ok/0031_extern.txt
crates/ra_syntax/tests/data/parser/ok/0032_where_for.txt
crates/ra_syntax/tests/data/parser/ok/0033_label_break.txt
crates/ra_syntax/tests/data/parser/ok/0034_macro_2.0.txt
crates/ra_syntax/tests/data/parser/ok/0034_macro_stuck.txt
crates/ra_syntax/tests/data/parser/ok/0035_crate_path_in_call.txt
crates/ra_syntax/tests/test.rs
editors/code/src/server.ts
editors/emacs/ra.el

index 49d53859d81f810fbea7bf1419104650b9aeee53..4c691f670a6d488f9fea57c8bb42585362189517 100644 (file)
@@ -1,7 +1,8 @@
 cache: cargo
 before_cache:
   - find ./target/debug -type f -maxdepth 1 -delete
-  - rm -fr ./target/debug/{deps,.fingerprint}/{*ra_*,*test*}
+  - rm -fr ./target/debug/{deps,.fingerprint}/{*ra_*,*test*,*tools*,*gen_lsp*}
+  - rm -f  ./target/.rustc_info.json
 
 env:
   - CARGO_INCREMENTAL=0
index 80fbda23c70aa10ed66513f55144a7148c11d405..18aac79ab75d0e2dcedadd0f106b1782f9327986 100644 (file)
@@ -174,7 +174,7 @@ dependencies = [
  "arrayvec 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)",
  "cfg-if 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
  "crossbeam-utils 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
- "lazy_static 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "lazy_static 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "memoffset 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "nodrop 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)",
  "scopeguard 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -188,7 +188,7 @@ dependencies = [
  "arrayvec 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)",
  "cfg-if 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
  "crossbeam-utils 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
- "lazy_static 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "lazy_static 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "memoffset 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "scopeguard 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
@@ -208,12 +208,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 
 [[package]]
 name = "derive-new"
-version = "0.5.5"
+version = "0.5.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
  "proc-macro2 0.4.20 (registry+https://github.com/rust-lang/crates.io-index)",
  "quote 0.6.9 (registry+https://github.com/rust-lang/crates.io-index)",
- "syn 0.14.9 (registry+https://github.com/rust-lang/crates.io-index)",
+ "syn 0.15.18 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
@@ -268,7 +268,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
  "proc-macro2 0.4.20 (registry+https://github.com/rust-lang/crates.io-index)",
  "quote 0.6.9 (registry+https://github.com/rust-lang/crates.io-index)",
- "syn 0.15.17 (registry+https://github.com/rust-lang/crates.io-index)",
+ "syn 0.15.18 (registry+https://github.com/rust-lang/crates.io-index)",
  "synstructure 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
@@ -279,13 +279,13 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 
 [[package]]
 name = "flexi_logger"
-version = "0.9.3"
+version = "0.10.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
  "chrono 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
  "glob 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)",
  "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
- "regex 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)",
+ "regex 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
@@ -317,7 +317,7 @@ version = "0.1.0"
 dependencies = [
  "crossbeam-channel 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)",
  "failure 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
- "languageserver-types 0.51.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "languageserver-types 0.51.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
  "serde 1.0.80 (registry+https://github.com/rust-lang/crates.io-index)",
  "serde_derive 1.0.80 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -394,7 +394,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 
 [[package]]
 name = "languageserver-types"
-version = "0.51.0"
+version = "0.51.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
  "bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -403,17 +403,14 @@ dependencies = [
  "serde 1.0.80 (registry+https://github.com/rust-lang/crates.io-index)",
  "serde_derive 1.0.80 (registry+https://github.com/rust-lang/crates.io-index)",
  "serde_json 1.0.32 (registry+https://github.com/rust-lang/crates.io-index)",
- "url 1.7.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "url 1.7.2 (registry+https://github.com/rust-lang/crates.io-index)",
  "url_serde 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
 name = "lazy_static"
-version = "1.1.0"
+version = "1.2.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-dependencies = [
- "version_check 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
-]
 
 [[package]]
 name = "libc"
@@ -484,7 +481,7 @@ dependencies = [
  "num-traits 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)",
  "proc-macro2 0.4.20 (registry+https://github.com/rust-lang/crates.io-index)",
  "quote 0.6.9 (registry+https://github.com/rust-lang/crates.io-index)",
- "syn 0.15.17 (registry+https://github.com/rust-lang/crates.io-index)",
+ "syn 0.15.18 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
@@ -605,7 +602,7 @@ dependencies = [
  "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
  "ra_editor 0.1.0",
  "ra_syntax 0.1.0",
- "rayon 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "rayon 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)",
  "relative-path 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "rustc-hash 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "salsa 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -645,15 +642,15 @@ dependencies = [
  "drop_bomb 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
  "failure 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
  "failure_derive 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
- "flexi_logger 0.9.3 (registry+https://github.com/rust-lang/crates.io-index)",
+ "flexi_logger 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "gen_lsp_server 0.1.0",
  "im 12.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
- "languageserver-types 0.51.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "languageserver-types 0.51.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
  "ra_analysis 0.1.0",
  "ra_editor 0.1.0",
  "ra_syntax 0.1.0",
- "rayon 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "rayon 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)",
  "relative-path 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "rustc-hash 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "serde 1.0.80 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -671,6 +668,7 @@ dependencies = [
 name = "ra_syntax"
 version = "0.1.0"
 dependencies = [
+ "arrayvec 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)",
  "drop_bomb 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
  "itertools 0.7.8 (registry+https://github.com/rust-lang/crates.io-index)",
  "parking_lot 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -718,7 +716,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 
 [[package]]
 name = "rayon"
-version = "1.0.2"
+version = "1.0.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
  "crossbeam-deque 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -732,7 +730,7 @@ version = "1.4.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
  "crossbeam-deque 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
- "lazy_static 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "lazy_static 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)",
  "num_cpus 1.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
@@ -752,7 +750,7 @@ dependencies = [
 
 [[package]]
 name = "regex"
-version = "1.0.5"
+version = "1.0.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
  "aho-corasick 0.6.9 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -839,7 +837,7 @@ name = "salsa"
 version = "0.8.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
- "derive-new 0.5.5 (registry+https://github.com/rust-lang/crates.io-index)",
+ "derive-new 0.5.6 (registry+https://github.com/rust-lang/crates.io-index)",
  "indexmap 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
  "lock_api 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
  "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -890,7 +888,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
  "proc-macro2 0.4.20 (registry+https://github.com/rust-lang/crates.io-index)",
  "quote 0.6.9 (registry+https://github.com/rust-lang/crates.io-index)",
- "syn 0.15.17 (registry+https://github.com/rust-lang/crates.io-index)",
+ "syn 0.15.18 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
@@ -965,7 +963,7 @@ dependencies = [
 
 [[package]]
 name = "syn"
-version = "0.15.17"
+version = "0.15.18"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
  "proc-macro2 0.4.20 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -980,7 +978,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
  "proc-macro2 0.4.20 (registry+https://github.com/rust-lang/crates.io-index)",
  "quote 0.6.9 (registry+https://github.com/rust-lang/crates.io-index)",
- "syn 0.15.17 (registry+https://github.com/rust-lang/crates.io-index)",
+ "syn 0.15.18 (registry+https://github.com/rust-lang/crates.io-index)",
  "unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
@@ -1002,15 +1000,15 @@ dependencies = [
  "error-chain 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "glob 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)",
  "humansize 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
- "lazy_static 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "lazy_static 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "pest 2.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
  "pest_derive 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
- "regex 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)",
+ "regex 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)",
  "serde 1.0.80 (registry+https://github.com/rust-lang/crates.io-index)",
  "serde_json 1.0.32 (registry+https://github.com/rust-lang/crates.io-index)",
  "slug 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
  "unic-segment 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
- "url 1.7.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "url 1.7.2 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
@@ -1064,7 +1062,7 @@ name = "thread_local"
 version = "0.3.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
- "lazy_static 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "lazy_static 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
@@ -1185,7 +1183,7 @@ dependencies = [
 
 [[package]]
 name = "url"
-version = "1.7.1"
+version = "1.7.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
  "idna 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -1199,7 +1197,7 @@ version = "0.2.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
  "serde 1.0.80 (registry+https://github.com/rust-lang/crates.io-index)",
- "url 1.7.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "url 1.7.2 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
@@ -1284,7 +1282,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 "checksum crossbeam-epoch 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "9c90f1474584f38e270b5b613e898c8c328aa4f3dea85e0a27ac2e642f009416"
 "checksum crossbeam-utils 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "2760899e32a1d58d5abb31129f8fae5de75220bc2176e77ff7c627ae45c918d9"
 "checksum crossbeam-utils 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "677d453a17e8bd2b913fa38e8b9cf04bcdbb5be790aa294f2389661d72036015"
-"checksum derive-new 0.5.5 (registry+https://github.com/rust-lang/crates.io-index)" = "899ec79626c14e00ccc9729b4d750bbe67fe76a8f436824c16e0233bbd9d7daa"
+"checksum derive-new 0.5.6 (registry+https://github.com/rust-lang/crates.io-index)" = "6ca414e896ae072546f4d789f452daaecf60ddee4c9df5dc6d5936d769e3d87c"
 "checksum deunicode 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)" = "850878694b7933ca4c9569d30a34b55031b9b139ee1fc7b94a527c4ef960d690"
 "checksum difference 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "524cbf6897b527295dff137cec09ecf3a05f4fddffd7dfcd1585403449e74198"
 "checksum digest 0.7.6 (registry+https://github.com/rust-lang/crates.io-index)" = "03b072242a8cbaf9c145665af9d250c59af3b958f83ed6824e13533cf76d5b90"
@@ -1294,7 +1292,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 "checksum failure 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "6dd377bcc1b1b7ce911967e3ec24fa19c3224394ec05b54aa7b083d498341ac7"
 "checksum failure_derive 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "64c2d913fe8ed3b6c6518eedf4538255b989945c14c2a7d5cbff62a5e2120596"
 "checksum fake-simd 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e88a8acf291dafb59c2d96e8f59828f3838bb1a70398823ade51a84de6a6deed"
-"checksum flexi_logger 0.9.3 (registry+https://github.com/rust-lang/crates.io-index)" = "7992096ba2290bd35b86b282e72edae518a25aa9a067ff417bc017ae63ac5e22"
+"checksum flexi_logger 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "553854ebfebeae44ba699a9dc7d53a4036ccc01cd1e144aea0e3054c54383733"
 "checksum fst 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "9b0408ab57c1bf7c634b2ac6a165d14f642dc3335a43203090a7f8c78b54577b"
 "checksum fuchsia-zircon 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2e9763c69ebaae630ba35f74888db465e49e259ba1bc0eda7d06f4a067615d82"
 "checksum fuchsia-zircon-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7"
@@ -1308,8 +1306,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 "checksum itertools 0.7.8 (registry+https://github.com/rust-lang/crates.io-index)" = "f58856976b776fedd95533137617a02fb25719f40e7d9b01c7043cd65474f450"
 "checksum itoa 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)" = "1306f3464951f30e30d12373d31c79fbd52d236e5e896fd92f96ec7babbbe60b"
 "checksum join_to_string 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7bddc885f3fd69dd4b5d747c2efe6dd2c36d795ea9938281ed50910e32c95e31"
-"checksum languageserver-types 0.51.0 (registry+https://github.com/rust-lang/crates.io-index)" = "caecadd973c43c93f5ce96fa457da310113d867af28808a8ed74023e9887a39e"
-"checksum lazy_static 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ca488b89a5657b0a2ecd45b95609b3e848cf1755da332a0da46e2b2b1cb371a7"
+"checksum languageserver-types 0.51.1 (registry+https://github.com/rust-lang/crates.io-index)" = "68de833188ada4e175d04a028f03f244f6370eedbcc75a05604d47d925933f69"
+"checksum lazy_static 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a374c89b9db55895453a74c1e38861d9deec0b01b405a82516e9d5de4820dea1"
 "checksum libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)" = "76e3a3ef172f1a0b9a9ff0dd1491ae5e6c948b94479a3021819ba7d860c8645d"
 "checksum lock_api 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "775751a3e69bde4df9b38dd00a1b5d6ac13791e4223d4a0506577f0dd27cfb7a"
 "checksum log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)" = "c84ec4b527950aa83a329754b01dbe3f58361d1c5efacd1f6d68c494d08a17c6"
@@ -1337,11 +1335,11 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 "checksum rand 0.5.5 (registry+https://github.com/rust-lang/crates.io-index)" = "e464cd887e869cddcae8792a4ee31d23c7edd516700695608f5b98c67ee0131c"
 "checksum rand_core 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "1961a422c4d189dfb50ffa9320bf1f2a9bd54ecb92792fb9477f99a1045f3372"
 "checksum rand_core 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "0905b6b7079ec73b314d4c748701f6931eb79fd97c668caa3f1899b22b32c6db"
-"checksum rayon 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "df7a791f788cb4c516f0e091301a29c2b71ef680db5e644a7d68835c8ae6dbfa"
+"checksum rayon 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "373814f27745b2686b350dd261bfd24576a6fb0e2c5919b3a2b6005f820b0473"
 "checksum rayon-core 1.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b055d1e92aba6877574d8fe604a63c8b5df60f60e5982bf7ccbb1338ea527356"
 "checksum redox_syscall 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)" = "c214e91d3ecf43e9a4e41e578973adeb14b474f2bee858742d127af75a0112b1"
 "checksum redox_termios 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7e891cfe48e9100a70a3b6eb652fef28920c117d366339687bd5576160db0f76"
-"checksum regex 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)" = "2069749032ea3ec200ca51e4a31df41759190a88edca0d2d86ee8bedf7073341"
+"checksum regex 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)" = "ee84f70c8c08744ea9641a731c7fadb475bf2ecc52d7f627feb833e0b3990467"
 "checksum regex-syntax 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "747ba3b235651f6e2f67dfa8bcdcd073ddb7c243cb21c442fc12395dfcac212d"
 "checksum relative-path 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "0e7790c7f1cc73d831d28dc5a7deb316a006e7848e6a7f467cdb10a0a9e0fb1c"
 "checksum remove_dir_all 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "3488ba1b9a2084d38645c4c08276a1752dcbf2c7130d74f1569681ad5d2799c5"
@@ -1368,7 +1366,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 "checksum strsim 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bb4f380125926a99e52bc279241539c018323fab05ad6368b56f93d9369ff550"
 "checksum superslice 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b50b13d42370e0f5fc62eafdd5c2d20065eaf5458dab215ff3e20e63eea96b30"
 "checksum syn 0.14.9 (registry+https://github.com/rust-lang/crates.io-index)" = "261ae9ecaa397c42b960649561949d69311f08eeaea86a65696e6e46517cf741"
-"checksum syn 0.15.17 (registry+https://github.com/rust-lang/crates.io-index)" = "3391038ebc3e4ab24eb028cb0ef2f2dc4ba0cbf72ee895ed6a6fad730640b5bc"
+"checksum syn 0.15.18 (registry+https://github.com/rust-lang/crates.io-index)" = "90c39a061e2f412a9f869540471ab679e85e50c6b05604daf28bc3060f75c430"
 "checksum synstructure 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)" = "73687139bf99285483c96ac0add482c3776528beac1d97d444f6e91f203a2015"
 "checksum tempdir 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)" = "15f2b5fb00ccdf689e0149d1b1b3c03fead81c2b37735d812fa8bddbbf41b6d8"
 "checksum tera 0.11.19 (registry+https://github.com/rust-lang/crates.io-index)" = "6ac6d8ad623a7efcfb4367ce2a36f84ef849d5aa3c7bcf2e0324c4cbcc57ebaf"
@@ -1393,7 +1391,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 "checksum unicode-width 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "882386231c45df4700b275c7ff55b6f3698780a650026380e72dabe76fa46526"
 "checksum unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fc72304796d0818e357ead4e000d19c9c174ab23dc11093ac919054d20a6a7fc"
 "checksum unreachable 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "382810877fe448991dfc7f0dd6e3ae5d58088fd0ea5e35189655f84e6814fa56"
-"checksum url 1.7.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2a321979c09843d272956e73700d12c4e7d3d92b2ee112b31548aef0d4efc5a6"
+"checksum url 1.7.2 (registry+https://github.com/rust-lang/crates.io-index)" = "dd4e7c0d531266369519a4aa4f399d748bd37043b00bde1e4ff1f60a120b355a"
 "checksum url_serde 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "74e7d099f1ee52f823d4bdd60c93c3602043c728f5db3b97bdb548467f7bddea"
 "checksum utf8-ranges 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "796f7e48bef87609f7ade7e06495a87d5cd06c7866e6a5cbfceffc558a243737"
 "checksum vec_map 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "05c78687fb1a80548ae3250346c3db86a80a7cdd77bda190189f2d0a0987c81a"
diff --git a/ROADMAP.md b/ROADMAP.md
new file mode 100644 (file)
index 0000000..951a092
--- /dev/null
@@ -0,0 +1,77 @@
+# Rust Analyzer Roadmap 01
+
+Written on 2018-11-06, extends approximately to February 2019.
+After that, we should coordinate with the compiler/rls developers to align goals and share code and experience.
+
+
+# Overall Goals
+
+The mission is:
+  * Provide an excellent "code analyzed as you type" IDE experience for the Rust language,
+  * Implement the bulk of the features in Rust itself.
+
+
+High-level architecture constraints:
+  * Long-term, replace the current rustc frontend.
+    It's *obvious* that the code should be shared, but OTOH, all great IDEs started as from-scratch rewrites.
+  * Don't hard-code a particular protocol or mode of operation.
+    Produce a library which could be used for implementing an LSP server, or for in-process embedding.
+  * As long as possible, stick with stable Rust (NB: we currently use beta for 2018 edition and salsa).
+
+
+# Current Goals
+
+Ideally, we would be coordinating with the compiler/rls teams, but they are busy working on making Rust 2018 at the moment.
+The sync-up point will happen some time after the edition, probably early 2019.
+In the meantime, the goal is to **experiment**, specifically, to figure out how a from-scratch written RLS might look like.
+
+
+## Data Storage and Protocol implementation
+
+The fundamental part of any architecture is who owns which data, how the data is mutated and how the data is exposed to user.
+For storage we use the [salsa](http://github.com/salsa-rs/salsa) library, which provides a solid model that seems to be the way to go.
+
+Modification to source files is mostly driven by the language client, but we also should support watching the file system. The current
+file watching implementation is a stub.
+
+**Action Item:** implement reliable file watching service.
+
+We also should extract LSP bits as a reusable library. There's already `gen_lsp_server`, but it is pretty limited.
+
+**Action Item:** try using `gen_lsp_server` in more than one language server, for example for TOML and Nix.
+
+The ideal architecture for `gen_lsp_server` is still unclear. I'd rather avoid futures: they bring significant runtime complexity
+(call stacks become insane) and the performance benefits are negligible for our use case (one thread per request is perfectly OK given
+the low amount of requests a language server receives). The current interface is based on crossbeam-channel, but it's not clear
+if that is the best choice.
+
+
+## Low-effort, high payoff features
+
+Implementing 20% of type inference will give use 80% of completion.
+Thus it makes sense to partially implement name resolution, type inference and trait matching, even though there is a chance that
+this code is replaced later on when we integrate with the compiler
+
+Specifically, we need to:
+
+* **Action Item:** implement path resolution, so that we get completion in imports and such.
+* **Action Item:** implement simple type inference, so that we get completion for inherent methods.
+* **Action Item:** implement nicer completion infrastructure, so that we have icons, snippets, doc comments, after insert callbacks, ...
+
+
+## Dragons to kill
+
+To make experiments most effective, we should try to prototype solutions for the hardest problems.
+In the case of Rust, the two hardest problems are:
+  * Conditional compilation and source/model mismatch.
+    A single source file might correspond to several entities in the semantic model.
+    For example, different cfg flags produce effectively different crates from the same source.
+  * Macros are intertwined with name resolution in a single fix-point iteration algorithm.
+    This is just plain hard to implement, but also interacts poorly with on-demand.
+
+
+For the first bullet point, we need to design descriptors infra and explicit mapping step between sources and semantic model, which is intentionally fuzzy in one direction.
+The **action item** here is basically "write code, see what works, keep high-level picture in mind".
+
+For the second bullet point, there's hope that salsa with its deep memoization will result in a fast enough solution even without being fully on-demand.
+Again, the **action item** is to write the code and see what works. Salsa itself uses macros heavily, so it should be a great test.
index e45a6b5e27c93b4d76bea97ad09de0b12107c481..5dab8f4086293a488903e13ea733282562c55819 100644 (file)
@@ -1,4 +1,4 @@
-//! A language server scaffold, exposing synchroneous crossbeam-channel based API.
+//! A language server scaffold, exposing a synchronous crossbeam-channel based API.
 //! This crate handles protocol handshaking and parsing messages, while you
 //! control the message dispatch loop yourself.
 //!
diff --git a/crates/ra_analysis/src/completion.rs b/crates/ra_analysis/src/completion.rs
deleted file mode 100644 (file)
index 7c3476e..0000000
+++ /dev/null
@@ -1,690 +0,0 @@
-use ra_editor::find_node_at_offset;
-use ra_syntax::{
-    algo::visit::{visitor, visitor_ctx, Visitor, VisitorCtx},
-    ast::{self, AstChildren, LoopBodyOwner, ModuleItemOwner},
-    AstNode, AtomEdit, File,
-    SyntaxKind::*,
-    SyntaxNodeRef, TextUnit,
-};
-use rustc_hash::{FxHashMap, FxHashSet};
-
-use crate::{
-    db::{self, SyntaxDatabase},
-    descriptors::function::FnScopes,
-    descriptors::module::{ModuleId, ModuleScope, ModuleTree, ModuleSource},
-    descriptors::DescriptorDatabase,
-    input::FilesDatabase,
-    Cancelable, FilePosition,
-};
-
-#[derive(Debug)]
-pub struct CompletionItem {
-    /// What user sees in pop-up
-    pub label: String,
-    /// What string is used for filtering, defaults to label
-    pub lookup: Option<String>,
-    /// What is inserted, defaults to label
-    pub snippet: Option<String>,
-}
-
-pub(crate) fn resolve_based_completion(
-    db: &db::RootDatabase,
-    position: FilePosition,
-) -> Cancelable<Option<Vec<CompletionItem>>> {
-    let source_root_id = db.file_source_root(position.file_id);
-    let file = db.file_syntax(position.file_id);
-    let module_tree = db.module_tree(source_root_id)?;
-    let module_id = match module_tree.any_module_for_source(ModuleSource::File(position.file_id)) {
-        None => return Ok(None),
-        Some(it) => it,
-    };
-    let file = {
-        let edit = AtomEdit::insert(position.offset, "intellijRulezz".to_string());
-        file.reparse(&edit)
-    };
-    let target_module_id = match find_target_module(&module_tree, module_id, &file, position.offset)
-    {
-        None => return Ok(None),
-        Some(it) => it,
-    };
-    let module_scope = db.module_scope(source_root_id, target_module_id)?;
-    let res: Vec<_> = module_scope
-        .entries()
-        .iter()
-        .map(|entry| CompletionItem {
-            label: entry.name().to_string(),
-            lookup: None,
-            snippet: None,
-        })
-        .collect();
-    Ok(Some(res))
-}
-
-pub(crate) fn find_target_module(
-    module_tree: &ModuleTree,
-    module_id: ModuleId,
-    file: &File,
-    offset: TextUnit,
-) -> Option<ModuleId> {
-    let name_ref: ast::NameRef = find_node_at_offset(file.syntax(), offset)?;
-    let mut crate_path = crate_path(name_ref)?;
-
-    crate_path.pop();
-    let mut target_module = module_id.root(&module_tree);
-    for name in crate_path {
-        target_module = target_module.child(module_tree, name.text().as_str())?;
-    }
-    Some(target_module)
-}
-
-fn crate_path(name_ref: ast::NameRef) -> Option<Vec<ast::NameRef>> {
-    let mut path = name_ref
-        .syntax()
-        .parent()
-        .and_then(ast::PathSegment::cast)?
-        .parent_path();
-    let mut res = Vec::new();
-    loop {
-        let segment = path.segment()?;
-        match segment.kind()? {
-            ast::PathSegmentKind::Name(name) => res.push(name),
-            ast::PathSegmentKind::CrateKw => break,
-            ast::PathSegmentKind::SelfKw | ast::PathSegmentKind::SuperKw => return None,
-        }
-        path = path.qualifier()?;
-    }
-    res.reverse();
-    Some(res)
-}
-
-pub(crate) fn scope_completion(
-    db: &db::RootDatabase,
-    position: FilePosition,
-) -> Option<Vec<CompletionItem>> {
-    let original_file = db.file_syntax(position.file_id);
-    // Insert a fake ident to get a valid parse tree
-    let file = {
-        let edit = AtomEdit::insert(position.offset, "intellijRulezz".to_string());
-        original_file.reparse(&edit)
-    };
-    let mut has_completions = false;
-    let mut res = Vec::new();
-    if let Some(name_ref) = find_node_at_offset::<ast::NameRef>(file.syntax(), position.offset) {
-        has_completions = true;
-        complete_name_ref(&file, name_ref, &mut res);
-        // special case, `trait T { fn foo(i_am_a_name_ref) {} }`
-        if is_node::<ast::Param>(name_ref.syntax()) {
-            param_completions(name_ref.syntax(), &mut res);
-        }
-        let name_range = name_ref.syntax().range();
-        let top_node = name_ref
-            .syntax()
-            .ancestors()
-            .take_while(|it| it.range() == name_range)
-            .last()
-            .unwrap();
-        match top_node.parent().map(|it| it.kind()) {
-            Some(ROOT) | Some(ITEM_LIST) => complete_mod_item_snippets(&mut res),
-            _ => (),
-        }
-    }
-    if let Some(name) = find_node_at_offset::<ast::Name>(file.syntax(), position.offset) {
-        if is_node::<ast::Param>(name.syntax()) {
-            has_completions = true;
-            param_completions(name.syntax(), &mut res);
-        }
-    }
-    if has_completions {
-        Some(res)
-    } else {
-        None
-    }
-}
-
-fn complete_module_items(
-    file: &File,
-    items: AstChildren<ast::ModuleItem>,
-    this_item: Option<ast::NameRef>,
-    acc: &mut Vec<CompletionItem>,
-) {
-    let scope = ModuleScope::new(items); // FIXME
-    acc.extend(
-        scope
-            .entries()
-            .iter()
-            .filter(|entry| {
-                let syntax = entry.ptr().resolve(file);
-                Some(syntax.borrowed()) != this_item.map(|it| it.syntax())
-            })
-            .map(|entry| CompletionItem {
-                label: entry.name().to_string(),
-                lookup: None,
-                snippet: None,
-            }),
-    );
-}
-
-fn complete_name_ref(file: &File, name_ref: ast::NameRef, acc: &mut Vec<CompletionItem>) {
-    if !is_node::<ast::Path>(name_ref.syntax()) {
-        return;
-    }
-    let mut visited_fn = false;
-    for node in name_ref.syntax().ancestors() {
-        if let Some(items) = visitor()
-            .visit::<ast::Root, _>(|it| Some(it.items()))
-            .visit::<ast::Module, _>(|it| Some(it.item_list()?.items()))
-            .accept(node)
-        {
-            if let Some(items) = items {
-                complete_module_items(file, items, Some(name_ref), acc);
-            }
-            break;
-        } else if !visited_fn {
-            if let Some(fn_def) = ast::FnDef::cast(node) {
-                visited_fn = true;
-                complete_expr_keywords(&file, fn_def, name_ref, acc);
-                complete_expr_snippets(acc);
-                let scopes = FnScopes::new(fn_def);
-                complete_fn(name_ref, &scopes, acc);
-            }
-        }
-    }
-}
-
-fn param_completions(ctx: SyntaxNodeRef, acc: &mut Vec<CompletionItem>) {
-    let mut params = FxHashMap::default();
-    for node in ctx.ancestors() {
-        let _ = visitor_ctx(&mut params)
-            .visit::<ast::Root, _>(process)
-            .visit::<ast::ItemList, _>(process)
-            .accept(node);
-    }
-    params
-        .into_iter()
-        .filter_map(|(label, (count, param))| {
-            let lookup = param.pat()?.syntax().text().to_string();
-            if count < 2 {
-                None
-            } else {
-                Some((label, lookup))
-            }
-        })
-        .for_each(|(label, lookup)| {
-            acc.push(CompletionItem {
-                label,
-                lookup: Some(lookup),
-                snippet: None,
-            })
-        });
-
-    fn process<'a, N: ast::FnDefOwner<'a>>(
-        node: N,
-        params: &mut FxHashMap<String, (u32, ast::Param<'a>)>,
-    ) {
-        node.functions()
-            .filter_map(|it| it.param_list())
-            .flat_map(|it| it.params())
-            .for_each(|param| {
-                let text = param.syntax().text().to_string();
-                params.entry(text).or_insert((0, param)).0 += 1;
-            })
-    }
-}
-
-fn is_node<'a, N: AstNode<'a>>(node: SyntaxNodeRef<'a>) -> bool {
-    match node.ancestors().filter_map(N::cast).next() {
-        None => false,
-        Some(n) => n.syntax().range() == node.range(),
-    }
-}
-
-fn complete_expr_keywords(
-    file: &File,
-    fn_def: ast::FnDef,
-    name_ref: ast::NameRef,
-    acc: &mut Vec<CompletionItem>,
-) {
-    acc.push(keyword("if", "if $0 {}"));
-    acc.push(keyword("match", "match $0 {}"));
-    acc.push(keyword("while", "while $0 {}"));
-    acc.push(keyword("loop", "loop {$0}"));
-
-    if let Some(off) = name_ref.syntax().range().start().checked_sub(2.into()) {
-        if let Some(if_expr) = find_node_at_offset::<ast::IfExpr>(file.syntax(), off) {
-            if if_expr.syntax().range().end() < name_ref.syntax().range().start() {
-                acc.push(keyword("else", "else {$0}"));
-                acc.push(keyword("else if", "else if $0 {}"));
-            }
-        }
-    }
-    if is_in_loop_body(name_ref) {
-        acc.push(keyword("continue", "continue"));
-        acc.push(keyword("break", "break"));
-    }
-    acc.extend(complete_return(fn_def, name_ref));
-}
-
-fn is_in_loop_body(name_ref: ast::NameRef) -> bool {
-    for node in name_ref.syntax().ancestors() {
-        if node.kind() == FN_DEF || node.kind() == LAMBDA_EXPR {
-            break;
-        }
-        let loop_body = visitor()
-            .visit::<ast::ForExpr, _>(LoopBodyOwner::loop_body)
-            .visit::<ast::WhileExpr, _>(LoopBodyOwner::loop_body)
-            .visit::<ast::LoopExpr, _>(LoopBodyOwner::loop_body)
-            .accept(node);
-        if let Some(Some(body)) = loop_body {
-            if name_ref
-                .syntax()
-                .range()
-                .is_subrange(&body.syntax().range())
-            {
-                return true;
-            }
-        }
-    }
-    false
-}
-
-fn complete_return(fn_def: ast::FnDef, name_ref: ast::NameRef) -> Option<CompletionItem> {
-    // let is_last_in_block = name_ref.syntax().ancestors().filter_map(ast::Expr::cast)
-    //     .next()
-    //     .and_then(|it| it.syntax().parent())
-    //     .and_then(ast::Block::cast)
-    //     .is_some();
-
-    // if is_last_in_block {
-    //     return None;
-    // }
-
-    let is_stmt = match name_ref
-        .syntax()
-        .ancestors()
-        .filter_map(ast::ExprStmt::cast)
-        .next()
-    {
-        None => false,
-        Some(expr_stmt) => expr_stmt.syntax().range() == name_ref.syntax().range(),
-    };
-    let snip = match (is_stmt, fn_def.ret_type().is_some()) {
-        (true, true) => "return $0;",
-        (true, false) => "return;",
-        (false, true) => "return $0",
-        (false, false) => "return",
-    };
-    Some(keyword("return", snip))
-}
-
-fn keyword(kw: &str, snip: &str) -> CompletionItem {
-    CompletionItem {
-        label: kw.to_string(),
-        lookup: None,
-        snippet: Some(snip.to_string()),
-    }
-}
-
-fn complete_expr_snippets(acc: &mut Vec<CompletionItem>) {
-    acc.push(CompletionItem {
-        label: "pd".to_string(),
-        lookup: None,
-        snippet: Some("eprintln!(\"$0 = {:?}\", $0);".to_string()),
-    });
-    acc.push(CompletionItem {
-        label: "ppd".to_string(),
-        lookup: None,
-        snippet: Some("eprintln!(\"$0 = {:#?}\", $0);".to_string()),
-    });
-}
-
-fn complete_mod_item_snippets(acc: &mut Vec<CompletionItem>) {
-    acc.push(CompletionItem {
-        label: "tfn".to_string(),
-        lookup: None,
-        snippet: Some("#[test]\nfn $1() {\n    $0\n}".to_string()),
-    });
-    acc.push(CompletionItem {
-        label: "pub(crate)".to_string(),
-        lookup: None,
-        snippet: Some("pub(crate) $0".to_string()),
-    })
-}
-
-fn complete_fn(name_ref: ast::NameRef, scopes: &FnScopes, acc: &mut Vec<CompletionItem>) {
-    let mut shadowed = FxHashSet::default();
-    acc.extend(
-        scopes
-            .scope_chain(name_ref.syntax())
-            .flat_map(|scope| scopes.entries(scope).iter())
-            .filter(|entry| shadowed.insert(entry.name()))
-            .map(|entry| CompletionItem {
-                label: entry.name().to_string(),
-                lookup: None,
-                snippet: None,
-            }),
-    );
-    if scopes.self_param.is_some() {
-        acc.push(CompletionItem {
-            label: "self".to_string(),
-            lookup: None,
-            snippet: None,
-        })
-    }
-}
-
-#[cfg(test)]
-mod tests {
-    use test_utils::assert_eq_dbg;
-
-    use crate::mock_analysis::single_file_with_position;
-
-    use super::*;
-
-    fn check_scope_completion(code: &str, expected_completions: &str) {
-        let (analysis, position) = single_file_with_position(code);
-        let completions = scope_completion(&analysis.imp.db, position)
-            .unwrap()
-            .into_iter()
-            .filter(|c| c.snippet.is_none())
-            .collect::<Vec<_>>();
-        assert_eq_dbg(expected_completions, &completions);
-    }
-
-    fn check_snippet_completion(code: &str, expected_completions: &str) {
-        let (analysis, position) = single_file_with_position(code);
-        let completions = scope_completion(&analysis.imp.db, position)
-            .unwrap()
-            .into_iter()
-            .filter(|c| c.snippet.is_some())
-            .collect::<Vec<_>>();
-        assert_eq_dbg(expected_completions, &completions);
-    }
-
-    #[test]
-    fn test_completion_let_scope() {
-        check_scope_completion(
-            r"
-            fn quux(x: i32) {
-                let y = 92;
-                1 + <|>;
-                let z = ();
-            }
-            ",
-            r#"[CompletionItem { label: "y", lookup: None, snippet: None },
-                   CompletionItem { label: "x", lookup: None, snippet: None },
-                   CompletionItem { label: "quux", lookup: None, snippet: None }]"#,
-        );
-    }
-
-    #[test]
-    fn test_completion_if_let_scope() {
-        check_scope_completion(
-            r"
-            fn quux() {
-                if let Some(x) = foo() {
-                    let y = 92;
-                };
-                if let Some(a) = bar() {
-                    let b = 62;
-                    1 + <|>
-                }
-            }
-            ",
-            r#"[CompletionItem { label: "b", lookup: None, snippet: None },
-                   CompletionItem { label: "a", lookup: None, snippet: None },
-                   CompletionItem { label: "quux", lookup: None, snippet: None }]"#,
-        );
-    }
-
-    #[test]
-    fn test_completion_for_scope() {
-        check_scope_completion(
-            r"
-            fn quux() {
-                for x in &[1, 2, 3] {
-                    <|>
-                }
-            }
-            ",
-            r#"[CompletionItem { label: "x", lookup: None, snippet: None },
-                   CompletionItem { label: "quux", lookup: None, snippet: None }]"#,
-        );
-    }
-
-    #[test]
-    fn test_completion_mod_scope() {
-        check_scope_completion(
-            r"
-            struct Foo;
-            enum Baz {}
-            fn quux() {
-                <|>
-            }
-            ",
-            r#"[CompletionItem { label: "Foo", lookup: None, snippet: None },
-                   CompletionItem { label: "Baz", lookup: None, snippet: None },
-                   CompletionItem { label: "quux", lookup: None, snippet: None }]"#,
-        );
-    }
-
-    #[test]
-    fn test_completion_mod_scope_no_self_use() {
-        check_scope_completion(
-            r"
-            use foo<|>;
-            ",
-            r#"[]"#,
-        );
-    }
-
-    #[test]
-    fn test_completion_mod_scope_nested() {
-        check_scope_completion(
-            r"
-            struct Foo;
-            mod m {
-                struct Bar;
-                fn quux() { <|> }
-            }
-            ",
-            r#"[CompletionItem { label: "Bar", lookup: None, snippet: None },
-                   CompletionItem { label: "quux", lookup: None, snippet: None }]"#,
-        );
-    }
-
-    #[test]
-    fn test_complete_type() {
-        check_scope_completion(
-            r"
-            struct Foo;
-            fn x() -> <|>
-        ",
-            r#"[CompletionItem { label: "Foo", lookup: None, snippet: None },
-               CompletionItem { label: "x", lookup: None, snippet: None }]"#,
-        )
-    }
-
-    #[test]
-    fn test_complete_shadowing() {
-        check_scope_completion(
-            r"
-            fn foo() -> {
-                let bar = 92;
-                {
-                    let bar = 62;
-                    <|>
-                }
-            }
-        ",
-            r#"[CompletionItem { label: "bar", lookup: None, snippet: None },
-               CompletionItem { label: "foo", lookup: None, snippet: None }]"#,
-        )
-    }
-
-    #[test]
-    fn test_complete_self() {
-        check_scope_completion(
-            r"
-            impl S { fn foo(&self) { <|> } }
-        ",
-            r#"[CompletionItem { label: "self", lookup: None, snippet: None }]"#,
-        )
-    }
-
-    #[test]
-    fn test_completion_kewords() {
-        check_snippet_completion(r"
-            fn quux() {
-                <|>
-            }
-            ", r#"[CompletionItem { label: "if", lookup: None, snippet: Some("if $0 {}") },
-                   CompletionItem { label: "match", lookup: None, snippet: Some("match $0 {}") },
-                   CompletionItem { label: "while", lookup: None, snippet: Some("while $0 {}") },
-                   CompletionItem { label: "loop", lookup: None, snippet: Some("loop {$0}") },
-                   CompletionItem { label: "return", lookup: None, snippet: Some("return") },
-                   CompletionItem { label: "pd", lookup: None, snippet: Some("eprintln!(\"$0 = {:?}\", $0);") },
-                   CompletionItem { label: "ppd", lookup: None, snippet: Some("eprintln!(\"$0 = {:#?}\", $0);") }]"#);
-    }
-
-    #[test]
-    fn test_completion_else() {
-        check_snippet_completion(r"
-            fn quux() {
-                if true {
-                    ()
-                } <|>
-            }
-            ", r#"[CompletionItem { label: "if", lookup: None, snippet: Some("if $0 {}") },
-                   CompletionItem { label: "match", lookup: None, snippet: Some("match $0 {}") },
-                   CompletionItem { label: "while", lookup: None, snippet: Some("while $0 {}") },
-                   CompletionItem { label: "loop", lookup: None, snippet: Some("loop {$0}") },
-                   CompletionItem { label: "else", lookup: None, snippet: Some("else {$0}") },
-                   CompletionItem { label: "else if", lookup: None, snippet: Some("else if $0 {}") },
-                   CompletionItem { label: "return", lookup: None, snippet: Some("return") },
-                   CompletionItem { label: "pd", lookup: None, snippet: Some("eprintln!(\"$0 = {:?}\", $0);") },
-                   CompletionItem { label: "ppd", lookup: None, snippet: Some("eprintln!(\"$0 = {:#?}\", $0);") }]"#);
-    }
-
-    #[test]
-    fn test_completion_return_value() {
-        check_snippet_completion(r"
-            fn quux() -> i32 {
-                <|>
-                92
-            }
-            ", r#"[CompletionItem { label: "if", lookup: None, snippet: Some("if $0 {}") },
-                   CompletionItem { label: "match", lookup: None, snippet: Some("match $0 {}") },
-                   CompletionItem { label: "while", lookup: None, snippet: Some("while $0 {}") },
-                   CompletionItem { label: "loop", lookup: None, snippet: Some("loop {$0}") },
-                   CompletionItem { label: "return", lookup: None, snippet: Some("return $0;") },
-                   CompletionItem { label: "pd", lookup: None, snippet: Some("eprintln!(\"$0 = {:?}\", $0);") },
-                   CompletionItem { label: "ppd", lookup: None, snippet: Some("eprintln!(\"$0 = {:#?}\", $0);") }]"#);
-        check_snippet_completion(r"
-            fn quux() {
-                <|>
-                92
-            }
-            ", r#"[CompletionItem { label: "if", lookup: None, snippet: Some("if $0 {}") },
-                   CompletionItem { label: "match", lookup: None, snippet: Some("match $0 {}") },
-                   CompletionItem { label: "while", lookup: None, snippet: Some("while $0 {}") },
-                   CompletionItem { label: "loop", lookup: None, snippet: Some("loop {$0}") },
-                   CompletionItem { label: "return", lookup: None, snippet: Some("return;") },
-                   CompletionItem { label: "pd", lookup: None, snippet: Some("eprintln!(\"$0 = {:?}\", $0);") },
-                   CompletionItem { label: "ppd", lookup: None, snippet: Some("eprintln!(\"$0 = {:#?}\", $0);") }]"#);
-    }
-
-    #[test]
-    fn test_completion_return_no_stmt() {
-        check_snippet_completion(r"
-            fn quux() -> i32 {
-                match () {
-                    () => <|>
-                }
-            }
-            ", r#"[CompletionItem { label: "if", lookup: None, snippet: Some("if $0 {}") },
-                   CompletionItem { label: "match", lookup: None, snippet: Some("match $0 {}") },
-                   CompletionItem { label: "while", lookup: None, snippet: Some("while $0 {}") },
-                   CompletionItem { label: "loop", lookup: None, snippet: Some("loop {$0}") },
-                   CompletionItem { label: "return", lookup: None, snippet: Some("return $0") },
-                   CompletionItem { label: "pd", lookup: None, snippet: Some("eprintln!(\"$0 = {:?}\", $0);") },
-                   CompletionItem { label: "ppd", lookup: None, snippet: Some("eprintln!(\"$0 = {:#?}\", $0);") }]"#);
-    }
-
-    #[test]
-    fn test_continue_break_completion() {
-        check_snippet_completion(r"
-            fn quux() -> i32 {
-                loop { <|> }
-            }
-            ", r#"[CompletionItem { label: "if", lookup: None, snippet: Some("if $0 {}") },
-                   CompletionItem { label: "match", lookup: None, snippet: Some("match $0 {}") },
-                   CompletionItem { label: "while", lookup: None, snippet: Some("while $0 {}") },
-                   CompletionItem { label: "loop", lookup: None, snippet: Some("loop {$0}") },
-                   CompletionItem { label: "continue", lookup: None, snippet: Some("continue") },
-                   CompletionItem { label: "break", lookup: None, snippet: Some("break") },
-                   CompletionItem { label: "return", lookup: None, snippet: Some("return $0") },
-                   CompletionItem { label: "pd", lookup: None, snippet: Some("eprintln!(\"$0 = {:?}\", $0);") },
-                   CompletionItem { label: "ppd", lookup: None, snippet: Some("eprintln!(\"$0 = {:#?}\", $0);") }]"#);
-        check_snippet_completion(r"
-            fn quux() -> i32 {
-                loop { || { <|> } }
-            }
-            ", r#"[CompletionItem { label: "if", lookup: None, snippet: Some("if $0 {}") },
-                   CompletionItem { label: "match", lookup: None, snippet: Some("match $0 {}") },
-                   CompletionItem { label: "while", lookup: None, snippet: Some("while $0 {}") },
-                   CompletionItem { label: "loop", lookup: None, snippet: Some("loop {$0}") },
-                   CompletionItem { label: "return", lookup: None, snippet: Some("return $0") },
-                   CompletionItem { label: "pd", lookup: None, snippet: Some("eprintln!(\"$0 = {:?}\", $0);") },
-                   CompletionItem { label: "ppd", lookup: None, snippet: Some("eprintln!(\"$0 = {:#?}\", $0);") }]"#);
-    }
-
-    #[test]
-    fn test_param_completion_last_param() {
-        check_scope_completion(r"
-            fn foo(file_id: FileId) {}
-            fn bar(file_id: FileId) {}
-            fn baz(file<|>) {}
-        ", r#"[CompletionItem { label: "file_id: FileId", lookup: Some("file_id"), snippet: None }]"#);
-    }
-
-    #[test]
-    fn test_param_completion_nth_param() {
-        check_scope_completion(r"
-            fn foo(file_id: FileId) {}
-            fn bar(file_id: FileId) {}
-            fn baz(file<|>, x: i32) {}
-        ", r#"[CompletionItem { label: "file_id: FileId", lookup: Some("file_id"), snippet: None }]"#);
-    }
-
-    #[test]
-    fn test_param_completion_trait_param() {
-        check_scope_completion(r"
-            pub(crate) trait SourceRoot {
-                pub fn contains(&self, file_id: FileId) -> bool;
-                pub fn module_map(&self) -> &ModuleMap;
-                pub fn lines(&self, file_id: FileId) -> &LineIndex;
-                pub fn syntax(&self, file<|>)
-            }
-        ", r#"[CompletionItem { label: "self", lookup: None, snippet: None },
-               CompletionItem { label: "SourceRoot", lookup: None, snippet: None },
-               CompletionItem { label: "file_id: FileId", lookup: Some("file_id"), snippet: None }]"#);
-    }
-
-    #[test]
-    fn test_item_snippets() {
-        // check_snippet_completion(r"
-        //     <|>
-        //     ",
-        //     r##"[CompletionItem { label: "tfn", lookup: None, snippet: Some("#[test]\nfn $1() {\n    $0\n}") }]"##,
-        // );
-        check_snippet_completion(r"
-            #[cfg(test)]
-            mod tests {
-                <|>
-            }
-            ",
-            r##"[CompletionItem { label: "tfn", lookup: None, snippet: Some("#[test]\nfn $1() {\n    $0\n}") },
-                 CompletionItem { label: "pub(crate)", lookup: None, snippet: Some("pub(crate) $0") }]"##,
-        );
-    }
-}
diff --git a/crates/ra_analysis/src/completion/mod.rs b/crates/ra_analysis/src/completion/mod.rs
new file mode 100644 (file)
index 0000000..2e08270
--- /dev/null
@@ -0,0 +1,448 @@
+mod reference_completion;
+
+use ra_editor::find_node_at_offset;
+use ra_syntax::{
+    algo::find_leaf_at_offset,
+    algo::visit::{visitor_ctx, VisitorCtx},
+    ast,
+    AstNode, AtomEdit,
+    SyntaxNodeRef,
+};
+use rustc_hash::{FxHashMap};
+
+use crate::{
+    db::{self, SyntaxDatabase},
+    descriptors::{DescriptorDatabase, module::ModuleSource},
+    input::{FilesDatabase},
+    Cancelable, FilePosition
+};
+
+#[derive(Debug)]
+pub struct CompletionItem {
+    /// What user sees in pop-up
+    pub label: String,
+    /// What string is used for filtering, defaults to label
+    pub lookup: Option<String>,
+    /// What is inserted, defaults to label
+    pub snippet: Option<String>,
+}
+
+pub(crate) fn completions(
+    db: &db::RootDatabase,
+    position: FilePosition,
+) -> Cancelable<Option<Vec<CompletionItem>>> {
+    let original_file = db.file_syntax(position.file_id);
+    // Insert a fake ident to get a valid parse tree
+    let file = {
+        let edit = AtomEdit::insert(position.offset, "intellijRulezz".to_string());
+        original_file.reparse(&edit)
+    };
+
+    let leaf = match find_leaf_at_offset(original_file.syntax(), position.offset).left_biased() {
+        None => return Ok(None),
+        Some(it) => it,
+    };
+    let source_root_id = db.file_source_root(position.file_id);
+    let module_tree = db.module_tree(source_root_id)?;
+    let module_source = ModuleSource::for_node(position.file_id, leaf);
+    let module_id = match module_tree.any_module_for_source(module_source) {
+        None => return Ok(None),
+        Some(it) => it,
+    };
+
+    let mut res = Vec::new();
+    let mut has_completions = false;
+    // First, let's try to complete a reference to some declaration.
+    if let Some(name_ref) = find_node_at_offset::<ast::NameRef>(file.syntax(), position.offset) {
+        has_completions = true;
+        reference_completion::completions(
+            &mut res,
+            db,
+            source_root_id,
+            &module_tree,
+            module_id,
+            &file,
+            name_ref,
+        )?;
+        // special case, `trait T { fn foo(i_am_a_name_ref) {} }`
+        if is_node::<ast::Param>(name_ref.syntax()) {
+            param_completions(name_ref.syntax(), &mut res);
+        }
+    }
+
+    // Otherwise, if this is a declaration, use heuristics to suggest a name.
+    if let Some(name) = find_node_at_offset::<ast::Name>(file.syntax(), position.offset) {
+        if is_node::<ast::Param>(name.syntax()) {
+            has_completions = true;
+            param_completions(name.syntax(), &mut res);
+        }
+    }
+    let res = if has_completions { Some(res) } else { None };
+    Ok(res)
+}
+
+fn param_completions(ctx: SyntaxNodeRef, acc: &mut Vec<CompletionItem>) {
+    let mut params = FxHashMap::default();
+    for node in ctx.ancestors() {
+        let _ = visitor_ctx(&mut params)
+            .visit::<ast::SourceFile, _>(process)
+            .visit::<ast::ItemList, _>(process)
+            .accept(node);
+    }
+    params
+        .into_iter()
+        .filter_map(|(label, (count, param))| {
+            let lookup = param.pat()?.syntax().text().to_string();
+            if count < 2 {
+                None
+            } else {
+                Some((label, lookup))
+            }
+        })
+        .for_each(|(label, lookup)| {
+            acc.push(CompletionItem {
+                label,
+                lookup: Some(lookup),
+                snippet: None,
+            })
+        });
+
+    fn process<'a, N: ast::FnDefOwner<'a>>(
+        node: N,
+        params: &mut FxHashMap<String, (u32, ast::Param<'a>)>,
+    ) {
+        node.functions()
+            .filter_map(|it| it.param_list())
+            .flat_map(|it| it.params())
+            .for_each(|param| {
+                let text = param.syntax().text().to_string();
+                params.entry(text).or_insert((0, param)).0 += 1;
+            })
+    }
+}
+
+fn is_node<'a, N: AstNode<'a>>(node: SyntaxNodeRef<'a>) -> bool {
+    match node.ancestors().filter_map(N::cast).next() {
+        None => false,
+        Some(n) => n.syntax().range() == node.range(),
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use test_utils::assert_eq_dbg;
+
+    use crate::mock_analysis::single_file_with_position;
+
+    use super::*;
+
+    fn check_scope_completion(code: &str, expected_completions: &str) {
+        let (analysis, position) = single_file_with_position(code);
+        let completions = completions(&analysis.imp.db, position)
+            .unwrap()
+            .unwrap()
+            .into_iter()
+            .filter(|c| c.snippet.is_none())
+            .collect::<Vec<_>>();
+        assert_eq_dbg(expected_completions, &completions);
+    }
+
+    fn check_snippet_completion(code: &str, expected_completions: &str) {
+        let (analysis, position) = single_file_with_position(code);
+        let completions = completions(&analysis.imp.db, position)
+            .unwrap()
+            .unwrap()
+            .into_iter()
+            .filter(|c| c.snippet.is_some())
+            .collect::<Vec<_>>();
+        assert_eq_dbg(expected_completions, &completions);
+    }
+
+    #[test]
+    fn test_completion_let_scope() {
+        check_scope_completion(
+            r"
+            fn quux(x: i32) {
+                let y = 92;
+                1 + <|>;
+                let z = ();
+            }
+            ",
+            r#"[CompletionItem { label: "y", lookup: None, snippet: None },
+                   CompletionItem { label: "x", lookup: None, snippet: None },
+                   CompletionItem { label: "quux", lookup: None, snippet: None }]"#,
+        );
+    }
+
+    #[test]
+    fn test_completion_if_let_scope() {
+        check_scope_completion(
+            r"
+            fn quux() {
+                if let Some(x) = foo() {
+                    let y = 92;
+                };
+                if let Some(a) = bar() {
+                    let b = 62;
+                    1 + <|>
+                }
+            }
+            ",
+            r#"[CompletionItem { label: "b", lookup: None, snippet: None },
+                   CompletionItem { label: "a", lookup: None, snippet: None },
+                   CompletionItem { label: "quux", lookup: None, snippet: None }]"#,
+        );
+    }
+
+    #[test]
+    fn test_completion_for_scope() {
+        check_scope_completion(
+            r"
+            fn quux() {
+                for x in &[1, 2, 3] {
+                    <|>
+                }
+            }
+            ",
+            r#"[CompletionItem { label: "x", lookup: None, snippet: None },
+                   CompletionItem { label: "quux", lookup: None, snippet: None }]"#,
+        );
+    }
+
+    #[test]
+    fn test_completion_mod_scope() {
+        check_scope_completion(
+            r"
+            struct Foo;
+            enum Baz {}
+            fn quux() {
+                <|>
+            }
+            ",
+            r#"[CompletionItem { label: "Foo", lookup: None, snippet: None },
+                   CompletionItem { label: "Baz", lookup: None, snippet: None },
+                   CompletionItem { label: "quux", lookup: None, snippet: None }]"#,
+        );
+    }
+
+    #[test]
+    fn test_completion_mod_scope_no_self_use() {
+        check_scope_completion(
+            r"
+            use foo<|>;
+            ",
+            r#"[]"#,
+        );
+    }
+
+    #[test]
+    fn test_completion_mod_scope_nested() {
+        check_scope_completion(
+            r"
+            struct Foo;
+            mod m {
+                struct Bar;
+                fn quux() { <|> }
+            }
+            ",
+            r#"[CompletionItem { label: "Bar", lookup: None, snippet: None },
+                   CompletionItem { label: "quux", lookup: None, snippet: None }]"#,
+        );
+    }
+
+    #[test]
+    fn test_complete_type() {
+        check_scope_completion(
+            r"
+            struct Foo;
+            fn x() -> <|>
+        ",
+            r#"[CompletionItem { label: "Foo", lookup: None, snippet: None },
+               CompletionItem { label: "x", lookup: None, snippet: None }]"#,
+        )
+    }
+
+    #[test]
+    fn test_complete_shadowing() {
+        check_scope_completion(
+            r"
+            fn foo() -> {
+                let bar = 92;
+                {
+                    let bar = 62;
+                    <|>
+                }
+            }
+        ",
+            r#"[CompletionItem { label: "bar", lookup: None, snippet: None },
+               CompletionItem { label: "foo", lookup: None, snippet: None }]"#,
+        )
+    }
+
+    #[test]
+    fn test_complete_self() {
+        check_scope_completion(
+            r"
+            impl S { fn foo(&self) { <|> } }
+        ",
+            r#"[CompletionItem { label: "self", lookup: None, snippet: None }]"#,
+        )
+    }
+
+    #[test]
+    fn test_completion_kewords() {
+        check_snippet_completion(r"
+            fn quux() {
+                <|>
+            }
+            ", r#"[CompletionItem { label: "if", lookup: None, snippet: Some("if $0 {}") },
+                   CompletionItem { label: "match", lookup: None, snippet: Some("match $0 {}") },
+                   CompletionItem { label: "while", lookup: None, snippet: Some("while $0 {}") },
+                   CompletionItem { label: "loop", lookup: None, snippet: Some("loop {$0}") },
+                   CompletionItem { label: "return", lookup: None, snippet: Some("return") },
+                   CompletionItem { label: "pd", lookup: None, snippet: Some("eprintln!(\"$0 = {:?}\", $0);") },
+                   CompletionItem { label: "ppd", lookup: None, snippet: Some("eprintln!(\"$0 = {:#?}\", $0);") }]"#);
+    }
+
+    #[test]
+    fn test_completion_else() {
+        check_snippet_completion(r"
+            fn quux() {
+                if true {
+                    ()
+                } <|>
+            }
+            ", r#"[CompletionItem { label: "if", lookup: None, snippet: Some("if $0 {}") },
+                   CompletionItem { label: "match", lookup: None, snippet: Some("match $0 {}") },
+                   CompletionItem { label: "while", lookup: None, snippet: Some("while $0 {}") },
+                   CompletionItem { label: "loop", lookup: None, snippet: Some("loop {$0}") },
+                   CompletionItem { label: "else", lookup: None, snippet: Some("else {$0}") },
+                   CompletionItem { label: "else if", lookup: None, snippet: Some("else if $0 {}") },
+                   CompletionItem { label: "return", lookup: None, snippet: Some("return") },
+                   CompletionItem { label: "pd", lookup: None, snippet: Some("eprintln!(\"$0 = {:?}\", $0);") },
+                   CompletionItem { label: "ppd", lookup: None, snippet: Some("eprintln!(\"$0 = {:#?}\", $0);") }]"#);
+    }
+
+    #[test]
+    fn test_completion_return_value() {
+        check_snippet_completion(r"
+            fn quux() -> i32 {
+                <|>
+                92
+            }
+            ", r#"[CompletionItem { label: "if", lookup: None, snippet: Some("if $0 {}") },
+                   CompletionItem { label: "match", lookup: None, snippet: Some("match $0 {}") },
+                   CompletionItem { label: "while", lookup: None, snippet: Some("while $0 {}") },
+                   CompletionItem { label: "loop", lookup: None, snippet: Some("loop {$0}") },
+                   CompletionItem { label: "return", lookup: None, snippet: Some("return $0;") },
+                   CompletionItem { label: "pd", lookup: None, snippet: Some("eprintln!(\"$0 = {:?}\", $0);") },
+                   CompletionItem { label: "ppd", lookup: None, snippet: Some("eprintln!(\"$0 = {:#?}\", $0);") }]"#);
+        check_snippet_completion(r"
+            fn quux() {
+                <|>
+                92
+            }
+            ", r#"[CompletionItem { label: "if", lookup: None, snippet: Some("if $0 {}") },
+                   CompletionItem { label: "match", lookup: None, snippet: Some("match $0 {}") },
+                   CompletionItem { label: "while", lookup: None, snippet: Some("while $0 {}") },
+                   CompletionItem { label: "loop", lookup: None, snippet: Some("loop {$0}") },
+                   CompletionItem { label: "return", lookup: None, snippet: Some("return;") },
+                   CompletionItem { label: "pd", lookup: None, snippet: Some("eprintln!(\"$0 = {:?}\", $0);") },
+                   CompletionItem { label: "ppd", lookup: None, snippet: Some("eprintln!(\"$0 = {:#?}\", $0);") }]"#);
+    }
+
+    #[test]
+    fn test_completion_return_no_stmt() {
+        check_snippet_completion(r"
+            fn quux() -> i32 {
+                match () {
+                    () => <|>
+                }
+            }
+            ", r#"[CompletionItem { label: "if", lookup: None, snippet: Some("if $0 {}") },
+                   CompletionItem { label: "match", lookup: None, snippet: Some("match $0 {}") },
+                   CompletionItem { label: "while", lookup: None, snippet: Some("while $0 {}") },
+                   CompletionItem { label: "loop", lookup: None, snippet: Some("loop {$0}") },
+                   CompletionItem { label: "return", lookup: None, snippet: Some("return $0") },
+                   CompletionItem { label: "pd", lookup: None, snippet: Some("eprintln!(\"$0 = {:?}\", $0);") },
+                   CompletionItem { label: "ppd", lookup: None, snippet: Some("eprintln!(\"$0 = {:#?}\", $0);") }]"#);
+    }
+
+    #[test]
+    fn test_continue_break_completion() {
+        check_snippet_completion(r"
+            fn quux() -> i32 {
+                loop { <|> }
+            }
+            ", r#"[CompletionItem { label: "if", lookup: None, snippet: Some("if $0 {}") },
+                   CompletionItem { label: "match", lookup: None, snippet: Some("match $0 {}") },
+                   CompletionItem { label: "while", lookup: None, snippet: Some("while $0 {}") },
+                   CompletionItem { label: "loop", lookup: None, snippet: Some("loop {$0}") },
+                   CompletionItem { label: "continue", lookup: None, snippet: Some("continue") },
+                   CompletionItem { label: "break", lookup: None, snippet: Some("break") },
+                   CompletionItem { label: "return", lookup: None, snippet: Some("return $0") },
+                   CompletionItem { label: "pd", lookup: None, snippet: Some("eprintln!(\"$0 = {:?}\", $0);") },
+                   CompletionItem { label: "ppd", lookup: None, snippet: Some("eprintln!(\"$0 = {:#?}\", $0);") }]"#);
+        check_snippet_completion(r"
+            fn quux() -> i32 {
+                loop { || { <|> } }
+            }
+            ", r#"[CompletionItem { label: "if", lookup: None, snippet: Some("if $0 {}") },
+                   CompletionItem { label: "match", lookup: None, snippet: Some("match $0 {}") },
+                   CompletionItem { label: "while", lookup: None, snippet: Some("while $0 {}") },
+                   CompletionItem { label: "loop", lookup: None, snippet: Some("loop {$0}") },
+                   CompletionItem { label: "return", lookup: None, snippet: Some("return $0") },
+                   CompletionItem { label: "pd", lookup: None, snippet: Some("eprintln!(\"$0 = {:?}\", $0);") },
+                   CompletionItem { label: "ppd", lookup: None, snippet: Some("eprintln!(\"$0 = {:#?}\", $0);") }]"#);
+    }
+
+    #[test]
+    fn test_param_completion_last_param() {
+        check_scope_completion(r"
+            fn foo(file_id: FileId) {}
+            fn bar(file_id: FileId) {}
+            fn baz(file<|>) {}
+        ", r#"[CompletionItem { label: "file_id: FileId", lookup: Some("file_id"), snippet: None }]"#);
+    }
+
+    #[test]
+    fn test_param_completion_nth_param() {
+        check_scope_completion(r"
+            fn foo(file_id: FileId) {}
+            fn bar(file_id: FileId) {}
+            fn baz(file<|>, x: i32) {}
+        ", r#"[CompletionItem { label: "file_id: FileId", lookup: Some("file_id"), snippet: None }]"#);
+    }
+
+    #[test]
+    fn test_param_completion_trait_param() {
+        check_scope_completion(r"
+            pub(crate) trait SourceRoot {
+                pub fn contains(&self, file_id: FileId) -> bool;
+                pub fn module_map(&self) -> &ModuleMap;
+                pub fn lines(&self, file_id: FileId) -> &LineIndex;
+                pub fn syntax(&self, file<|>)
+            }
+        ", r#"[CompletionItem { label: "self", lookup: None, snippet: None },
+               CompletionItem { label: "SourceRoot", lookup: None, snippet: None },
+               CompletionItem { label: "file_id: FileId", lookup: Some("file_id"), snippet: None }]"#);
+    }
+
+    #[test]
+    fn test_item_snippets() {
+        // check_snippet_completion(r"
+        //     <|>
+        //     ",
+        //     r##"[CompletionItem { label: "tfn", lookup: None, snippet: Some("#[test]\nfn $1() {\n    $0\n}") }]"##,
+        // );
+        check_snippet_completion(r"
+            #[cfg(test)]
+            mod tests {
+                <|>
+            }
+            ",
+            r##"[CompletionItem { label: "tfn", lookup: None, snippet: Some("#[test]\nfn $1() {\n    $0\n}") },
+                 CompletionItem { label: "pub(crate)", lookup: None, snippet: Some("pub(crate) $0") }]"##,
+        );
+    }
+}
diff --git a/crates/ra_analysis/src/completion/reference_completion.rs b/crates/ra_analysis/src/completion/reference_completion.rs
new file mode 100644 (file)
index 0000000..6c5fd0b
--- /dev/null
@@ -0,0 +1,316 @@
+use rustc_hash::{FxHashSet};
+use ra_editor::find_node_at_offset;
+use ra_syntax::{
+    algo::visit::{visitor, Visitor},
+    SourceFileNode, AstNode,
+    ast::{self, LoopBodyOwner},
+    SyntaxKind::*,
+};
+
+use crate::{
+    db::RootDatabase,
+    input::{SourceRootId},
+    completion::CompletionItem,
+    descriptors::module::{ModuleId, ModuleTree},
+    descriptors::function::FnScopes,
+    descriptors::DescriptorDatabase,
+    Cancelable
+};
+
+pub(super) fn completions(
+    acc: &mut Vec<CompletionItem>,
+    db: &RootDatabase,
+    source_root_id: SourceRootId,
+    module_tree: &ModuleTree,
+    module_id: ModuleId,
+    file: &SourceFileNode,
+    name_ref: ast::NameRef,
+) -> Cancelable<()> {
+    let kind = match classify_name_ref(name_ref) {
+        Some(it) => it,
+        None => return Ok(()),
+    };
+
+    match kind {
+        NameRefKind::LocalRef { enclosing_fn } => {
+            if let Some(fn_def) = enclosing_fn {
+                let scopes = FnScopes::new(fn_def);
+                complete_fn(name_ref, &scopes, acc);
+                complete_expr_keywords(&file, fn_def, name_ref, acc);
+                complete_expr_snippets(acc);
+            }
+
+            let module_scope = db.module_scope(source_root_id, module_id)?;
+            acc.extend(
+                module_scope
+                    .entries()
+                    .iter()
+                    .filter(|entry| {
+                        // Don't expose this item
+                        !entry.ptr().range().is_subrange(&name_ref.syntax().range())
+                    })
+                    .map(|entry| CompletionItem {
+                        label: entry.name().to_string(),
+                        lookup: None,
+                        snippet: None,
+                    }),
+            );
+        }
+        NameRefKind::CratePath(path) => {
+            complete_path(acc, db, source_root_id, module_tree, module_id, path)?
+        }
+        NameRefKind::BareIdentInMod => {
+            let name_range = name_ref.syntax().range();
+            let top_node = name_ref
+                .syntax()
+                .ancestors()
+                .take_while(|it| it.range() == name_range)
+                .last()
+                .unwrap();
+            match top_node.parent().map(|it| it.kind()) {
+                Some(SOURCE_FILE) | Some(ITEM_LIST) => complete_mod_item_snippets(acc),
+                _ => (),
+            }
+        }
+    }
+    Ok(())
+}
+
+enum NameRefKind<'a> {
+    /// NameRef is a part of single-segment path, for example, a refernece to a
+    /// local variable.
+    LocalRef {
+        enclosing_fn: Option<ast::FnDef<'a>>,
+    },
+    /// NameRef is the last segment in crate:: path
+    CratePath(Vec<ast::NameRef<'a>>),
+    /// NameRef is bare identifier at the module's root.
+    /// Used for keyword completion
+    BareIdentInMod,
+}
+
+fn classify_name_ref(name_ref: ast::NameRef) -> Option<NameRefKind> {
+    let name_range = name_ref.syntax().range();
+    let top_node = name_ref
+        .syntax()
+        .ancestors()
+        .take_while(|it| it.range() == name_range)
+        .last()
+        .unwrap();
+    match top_node.parent().map(|it| it.kind()) {
+        Some(SOURCE_FILE) | Some(ITEM_LIST) => return Some(NameRefKind::BareIdentInMod),
+        _ => (),
+    }
+
+    let parent = name_ref.syntax().parent()?;
+    if let Some(segment) = ast::PathSegment::cast(parent) {
+        let path = segment.parent_path();
+        if let Some(crate_path) = crate_path(path) {
+            return Some(NameRefKind::CratePath(crate_path));
+        }
+        if path.qualifier().is_none() {
+            let enclosing_fn = name_ref
+                .syntax()
+                .ancestors()
+                .take_while(|it| it.kind() != SOURCE_FILE && it.kind() != MODULE)
+                .find_map(ast::FnDef::cast);
+            return Some(NameRefKind::LocalRef { enclosing_fn });
+        }
+    }
+    None
+}
+
+fn crate_path(mut path: ast::Path) -> Option<Vec<ast::NameRef>> {
+    let mut res = Vec::new();
+    loop {
+        let segment = path.segment()?;
+        match segment.kind()? {
+            ast::PathSegmentKind::Name(name) => res.push(name),
+            ast::PathSegmentKind::CrateKw => break,
+            ast::PathSegmentKind::SelfKw | ast::PathSegmentKind::SuperKw => return None,
+        }
+        path = qualifier(path)?;
+    }
+    res.reverse();
+    return Some(res);
+
+    fn qualifier(path: ast::Path) -> Option<ast::Path> {
+        if let Some(q) = path.qualifier() {
+            return Some(q);
+        }
+        // TODO: this bottom up traversal is not too precise.
+        // Should we handle do a top-down analysiss, recording results?
+        let use_tree_list = path.syntax().ancestors().find_map(ast::UseTreeList::cast)?;
+        let use_tree = use_tree_list.parent_use_tree();
+        use_tree.path()
+    }
+}
+
+fn complete_fn(name_ref: ast::NameRef, scopes: &FnScopes, acc: &mut Vec<CompletionItem>) {
+    let mut shadowed = FxHashSet::default();
+    acc.extend(
+        scopes
+            .scope_chain(name_ref.syntax())
+            .flat_map(|scope| scopes.entries(scope).iter())
+            .filter(|entry| shadowed.insert(entry.name()))
+            .map(|entry| CompletionItem {
+                label: entry.name().to_string(),
+                lookup: None,
+                snippet: None,
+            }),
+    );
+    if scopes.self_param.is_some() {
+        acc.push(CompletionItem {
+            label: "self".to_string(),
+            lookup: None,
+            snippet: None,
+        })
+    }
+}
+
+fn complete_path(
+    acc: &mut Vec<CompletionItem>,
+    db: &RootDatabase,
+    source_root_id: SourceRootId,
+    module_tree: &ModuleTree,
+    module_id: ModuleId,
+    crate_path: Vec<ast::NameRef>,
+) -> Cancelable<()> {
+    let target_module_id = match find_target_module(module_tree, module_id, crate_path) {
+        None => return Ok(()),
+        Some(it) => it,
+    };
+    let module_scope = db.module_scope(source_root_id, target_module_id)?;
+    let completions = module_scope.entries().iter().map(|entry| CompletionItem {
+        label: entry.name().to_string(),
+        lookup: None,
+        snippet: None,
+    });
+    acc.extend(completions);
+    Ok(())
+}
+
+fn find_target_module(
+    module_tree: &ModuleTree,
+    module_id: ModuleId,
+    mut crate_path: Vec<ast::NameRef>,
+) -> Option<ModuleId> {
+    crate_path.pop();
+    let mut target_module = module_id.root(&module_tree);
+    for name in crate_path {
+        target_module = target_module.child(module_tree, name.text().as_str())?;
+    }
+    Some(target_module)
+}
+
+fn complete_mod_item_snippets(acc: &mut Vec<CompletionItem>) {
+    acc.push(CompletionItem {
+        label: "tfn".to_string(),
+        lookup: None,
+        snippet: Some("#[test]\nfn $1() {\n    $0\n}".to_string()),
+    });
+    acc.push(CompletionItem {
+        label: "pub(crate)".to_string(),
+        lookup: None,
+        snippet: Some("pub(crate) $0".to_string()),
+    })
+}
+
+fn complete_expr_keywords(
+    file: &SourceFileNode,
+    fn_def: ast::FnDef,
+    name_ref: ast::NameRef,
+    acc: &mut Vec<CompletionItem>,
+) {
+    acc.push(keyword("if", "if $0 {}"));
+    acc.push(keyword("match", "match $0 {}"));
+    acc.push(keyword("while", "while $0 {}"));
+    acc.push(keyword("loop", "loop {$0}"));
+
+    if let Some(off) = name_ref.syntax().range().start().checked_sub(2.into()) {
+        if let Some(if_expr) = find_node_at_offset::<ast::IfExpr>(file.syntax(), off) {
+            if if_expr.syntax().range().end() < name_ref.syntax().range().start() {
+                acc.push(keyword("else", "else {$0}"));
+                acc.push(keyword("else if", "else if $0 {}"));
+            }
+        }
+    }
+    if is_in_loop_body(name_ref) {
+        acc.push(keyword("continue", "continue"));
+        acc.push(keyword("break", "break"));
+    }
+    acc.extend(complete_return(fn_def, name_ref));
+}
+
+fn is_in_loop_body(name_ref: ast::NameRef) -> bool {
+    for node in name_ref.syntax().ancestors() {
+        if node.kind() == FN_DEF || node.kind() == LAMBDA_EXPR {
+            break;
+        }
+        let loop_body = visitor()
+            .visit::<ast::ForExpr, _>(LoopBodyOwner::loop_body)
+            .visit::<ast::WhileExpr, _>(LoopBodyOwner::loop_body)
+            .visit::<ast::LoopExpr, _>(LoopBodyOwner::loop_body)
+            .accept(node);
+        if let Some(Some(body)) = loop_body {
+            if name_ref
+                .syntax()
+                .range()
+                .is_subrange(&body.syntax().range())
+            {
+                return true;
+            }
+        }
+    }
+    false
+}
+
+fn complete_return(fn_def: ast::FnDef, name_ref: ast::NameRef) -> Option<CompletionItem> {
+    // let is_last_in_block = name_ref.syntax().ancestors().filter_map(ast::Expr::cast)
+    //     .next()
+    //     .and_then(|it| it.syntax().parent())
+    //     .and_then(ast::Block::cast)
+    //     .is_some();
+
+    // if is_last_in_block {
+    //     return None;
+    // }
+
+    let is_stmt = match name_ref
+        .syntax()
+        .ancestors()
+        .filter_map(ast::ExprStmt::cast)
+        .next()
+    {
+        None => false,
+        Some(expr_stmt) => expr_stmt.syntax().range() == name_ref.syntax().range(),
+    };
+    let snip = match (is_stmt, fn_def.ret_type().is_some()) {
+        (true, true) => "return $0;",
+        (true, false) => "return;",
+        (false, true) => "return $0",
+        (false, false) => "return",
+    };
+    Some(keyword("return", snip))
+}
+
+fn keyword(kw: &str, snip: &str) -> CompletionItem {
+    CompletionItem {
+        label: kw.to_string(),
+        lookup: None,
+        snippet: Some(snip.to_string()),
+    }
+}
+
+fn complete_expr_snippets(acc: &mut Vec<CompletionItem>) {
+    acc.push(CompletionItem {
+        label: "pd".to_string(),
+        lookup: None,
+        snippet: Some("eprintln!(\"$0 = {:?}\", $0);".to_string()),
+    });
+    acc.push(CompletionItem {
+        label: "ppd".to_string(),
+        lookup: None,
+        snippet: Some("eprintln!(\"$0 = {:#?}\", $0);".to_string()),
+    });
+}
index 627512553024fcddaf5e8dc838f0967b67f4fb40..194f1a6b082fb53c28d0a840c1cea5674d69c8bb 100644 (file)
@@ -1,7 +1,7 @@
 use std::sync::Arc;
 
 use ra_editor::LineIndex;
-use ra_syntax::{File, SyntaxNode};
+use ra_syntax::{SourceFileNode, SyntaxNode};
 use salsa::{self, Database};
 
 use crate::{
@@ -85,7 +85,7 @@ impl DescriptorDatabase {
 
 salsa::query_group! {
     pub(crate) trait SyntaxDatabase: crate::input::FilesDatabase {
-        fn file_syntax(file_id: FileId) -> File {
+        fn file_syntax(file_id: FileId) -> SourceFileNode {
             type FileSyntaxQuery;
         }
         fn file_lines(file_id: FileId) -> Arc<LineIndex> {
@@ -103,9 +103,9 @@ fn resolve_syntax_ptr(ptr: SyntaxPtr) -> SyntaxNode {
     }
 }
 
-fn file_syntax(db: &impl SyntaxDatabase, file_id: FileId) -> File {
+fn file_syntax(db: &impl SyntaxDatabase, file_id: FileId) -> SourceFileNode {
     let text = db.file_text(file_id);
-    File::parse(&*text)
+    SourceFileNode::parse(&*text)
 }
 fn file_lines(db: &impl SyntaxDatabase, file_id: FileId) -> Arc<LineIndex> {
     let text = db.file_text(file_id);
index 62b46ffba7773824d38a3fee53861bc78af1ed01..bbe16947cc49c918e0eebb2d9cf9c5cc64ca1aa3 100644 (file)
@@ -272,7 +272,7 @@ pub fn resolve_local_name<'a>(
 #[cfg(test)]
 mod tests {
     use ra_editor::find_node_at_offset;
-    use ra_syntax::File;
+    use ra_syntax::SourceFileNode;
     use test_utils::extract_offset;
 
     use super::*;
@@ -287,7 +287,7 @@ fn do_check(code: &str, expected: &[&str]) {
             buf.push_str(&code[off..]);
             buf
         };
-        let file = File::parse(&code);
+        let file = SourceFileNode::parse(&code);
         let marker: ast::PathExpr = find_node_at_offset(file.syntax(), off).unwrap();
         let fn_def: ast::FnDef = find_node_at_offset(file.syntax(), off).unwrap();
         let scopes = FnScopes::new(fn_def);
@@ -376,7 +376,7 @@ fn foo(x: String) {
 
     fn do_check_local_name(code: &str, expected_offset: u32) {
         let (off, code) = extract_offset(code);
-        let file = File::parse(&code);
+        let file = SourceFileNode::parse(&code);
         let fn_def: ast::FnDef = find_node_at_offset(file.syntax(), off).unwrap();
         let name_ref: ast::NameRef = find_node_at_offset(file.syntax(), off).unwrap();
 
index b5c232ea435317426ad27d5722c94363adba46eb..ade96ddc094fb9c1c1caf0c054b9454bd23aa534 100644 (file)
@@ -41,8 +41,8 @@ pub(crate) fn submodules(
     db::check_canceled(db)?;
     let file_id = source.file_id();
     let submodules = match source.resolve(db) {
-        ModuleSourceNode::Root(it) => collect_submodules(file_id, it.borrowed()),
-        ModuleSourceNode::Inline(it) => it
+        ModuleSourceNode::SourceFile(it) => collect_submodules(file_id, it.borrowed()),
+        ModuleSourceNode::Module(it) => it
             .borrowed()
             .item_list()
             .map(|it| collect_submodules(file_id, it))
@@ -89,8 +89,8 @@ pub(crate) fn module_scope(
     let tree = db.module_tree(source_root_id)?;
     let source = module_id.source(&tree).resolve(db);
     let res = match source {
-        ModuleSourceNode::Root(root) => ModuleScope::new(root.borrowed().items()),
-        ModuleSourceNode::Inline(inline) => match inline.borrowed().item_list() {
+        ModuleSourceNode::SourceFile(it) => ModuleScope::new(it.borrowed().items()),
+        ModuleSourceNode::Module(it) => match it.borrowed().item_list() {
             Some(items) => ModuleScope::new(items.items()),
             None => ModuleScope::new(std::iter::empty()),
         },
@@ -121,7 +121,7 @@ fn create_module_tree<'a>(
 
     let source_root = db.source_root(source_root);
     for &file_id in source_root.files.iter() {
-        let source = ModuleSource::File(file_id);
+        let source = ModuleSource::SourceFile(file_id);
         if visited.contains(&source) {
             continue; // TODO: use explicit crate_roots here
         }
@@ -181,7 +181,7 @@ fn build_subtree(
                             visited,
                             roots,
                             Some(link),
-                            ModuleSource::File(file_id),
+                            ModuleSource::SourceFile(file_id),
                         ),
                     })
                     .collect::<Cancelable<Vec<_>>>()?;
@@ -213,8 +213,8 @@ fn resolve_submodule(
     file_resolver: &FileResolverImp,
 ) -> (Vec<FileId>, Option<Problem>) {
     let file_id = match source {
-        ModuleSource::File(it) => it,
-        ModuleSource::Inline(..) => {
+        ModuleSource::SourceFile(it) => it,
+        ModuleSource::Module(..) => {
             // TODO
             return (Vec::new(), None);
         }
index 03330240da5b51ec7da1710057037a6eceb00223..bc1148b22842c5b6852461f33501c1cec9050658 100644 (file)
@@ -3,7 +3,7 @@
 
 use ra_syntax::{
     ast::{self, AstNode, NameOwner},
-    SmolStr, SyntaxNode,
+    SmolStr, SyntaxNode, SyntaxNodeRef,
 };
 use relative_path::RelativePathBuf;
 
@@ -41,19 +41,18 @@ pub(crate) fn any_module_for_source(&self, source: ModuleSource) -> Option<Modul
 
 /// `ModuleSource` is the syntax tree element that produced this module:
 /// either a file, or an inlinde module.
-/// TODO: we don't produce Inline modules yet
 #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
 pub(crate) enum ModuleSource {
-    File(FileId),
+    SourceFile(FileId),
     #[allow(dead_code)]
-    Inline(SyntaxPtr),
+    Module(SyntaxPtr),
 }
 
 /// An owned syntax node for a module. Unlike `ModuleSource`,
 /// this holds onto the AST for the whole file.
 enum ModuleSourceNode {
-    Root(ast::RootNode),
-    Inline(ast::ModuleNode),
+    SourceFile(ast::SourceFileNode),
+    Module(ast::ModuleNode),
 }
 
 #[derive(Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, Debug)]
@@ -135,14 +134,14 @@ pub(crate) fn bind_source<'a>(
     ) -> ast::ModuleNode {
         let owner = self.owner(tree);
         match owner.source(tree).resolve(db) {
-            ModuleSourceNode::Root(root) => {
+            ModuleSourceNode::SourceFile(root) => {
                 let ast = imp::modules(root.borrowed())
                     .find(|(name, _)| name == &tree.link(self).name)
                     .unwrap()
                     .1;
                 ast.owned()
             }
-            ModuleSourceNode::Inline(it) => it,
+            ModuleSourceNode::Module(it) => it,
         }
     }
 }
@@ -155,37 +154,47 @@ struct ModuleData {
 }
 
 impl ModuleSource {
+    pub(crate) fn for_node(file_id: FileId, node: SyntaxNodeRef) -> ModuleSource {
+        for node in node.ancestors() {
+            if let Some(m) = ast::Module::cast(node) {
+                if !m.has_semi() {
+                    return ModuleSource::new_inline(file_id, m);
+                }
+            }
+        }
+        ModuleSource::SourceFile(file_id)
+    }
     pub(crate) fn new_inline(file_id: FileId, module: ast::Module) -> ModuleSource {
         assert!(!module.has_semi());
         let ptr = SyntaxPtr::new(file_id, module.syntax());
-        ModuleSource::Inline(ptr)
+        ModuleSource::Module(ptr)
     }
 
     pub(crate) fn as_file(self) -> Option<FileId> {
         match self {
-            ModuleSource::File(f) => Some(f),
-            ModuleSource::Inline(..) => None,
+            ModuleSource::SourceFile(f) => Some(f),
+            ModuleSource::Module(..) => None,
         }
     }
 
     pub(crate) fn file_id(self) -> FileId {
         match self {
-            ModuleSource::File(f) => f,
-            ModuleSource::Inline(ptr) => ptr.file_id(),
+            ModuleSource::SourceFile(f) => f,
+            ModuleSource::Module(ptr) => ptr.file_id(),
         }
     }
 
     fn resolve(self, db: &impl SyntaxDatabase) -> ModuleSourceNode {
         match self {
-            ModuleSource::File(file_id) => {
+            ModuleSource::SourceFile(file_id) => {
                 let syntax = db.file_syntax(file_id);
-                ModuleSourceNode::Root(syntax.ast().owned())
+                ModuleSourceNode::SourceFile(syntax.ast().owned())
             }
-            ModuleSource::Inline(ptr) => {
+            ModuleSource::Module(ptr) => {
                 let syntax = db.resolve_syntax_ptr(ptr);
                 let syntax = syntax.borrowed();
                 let module = ast::Module::cast(syntax).unwrap();
-                ModuleSourceNode::Inline(module.owned())
+                ModuleSourceNode::Module(module.owned())
             }
         }
     }
index 215b31f8efe088f8c390201297392a5f32ec4295..4490228e41aa6ba4ba202a58cb190e6d33e9a78b 100644 (file)
@@ -25,7 +25,7 @@ enum EntryKind {
 }
 
 impl ModuleScope {
-    pub(crate) fn new<'a>(items: impl Iterator<Item = ast::ModuleItem<'a>>) -> ModuleScope {
+    pub(super) fn new<'a>(items: impl Iterator<Item = ast::ModuleItem<'a>>) -> ModuleScope {
         let mut entries = Vec::new();
         for item in items {
             let entry = match item {
@@ -95,10 +95,10 @@ fn collect_imports(tree: ast::UseTree, acc: &mut Vec<Entry>) {
 #[cfg(test)]
 mod tests {
     use super::*;
-    use ra_syntax::{ast::ModuleItemOwner, File};
+    use ra_syntax::{ast::ModuleItemOwner, SourceFileNode};
 
     fn do_check(code: &str, expected: &[&str]) {
-        let file = File::parse(&code);
+        let file = SourceFileNode::parse(&code);
         let scope = ModuleScope::new(file.ast().items());
         let actual = scope.entries.iter().map(|it| it.name()).collect::<Vec<_>>();
         assert_eq!(expected, actual.as_slice());
index 819827b95bb637732c8e77edf503c5a727a200d1..74c248a96c71c1a626f56b879aaa510fc6b122d7 100644 (file)
@@ -7,7 +7,7 @@
 use ra_editor::{self, find_node_at_offset, FileSymbol, LineIndex, LocalEdit};
 use ra_syntax::{
     ast::{self, ArgListOwner, Expr, NameOwner},
-    AstNode, File, SmolStr,
+    AstNode, SourceFileNode, SmolStr,
     SyntaxKind::*,
     SyntaxNodeRef, TextRange, TextUnit,
 };
@@ -17,7 +17,7 @@
 use salsa::{Database, ParallelDatabase};
 
 use crate::{
-    completion::{resolve_based_completion, scope_completion, CompletionItem},
+    completion::{completions, CompletionItem},
     db::{self, FileSyntaxQuery, SyntaxDatabase},
     descriptors::{
         function::{FnDescriptor, FnId},
@@ -27,7 +27,7 @@
     input::{FilesDatabase, SourceRoot, SourceRootId, WORKSPACE},
     symbol_index::SymbolIndex,
     AnalysisChange, Cancelable, CrateGraph, CrateId, Diagnostic, FileId, FileResolver,
-    FileSystemEdit, FilePosition, Query, SourceChange, SourceFileEdit,
+    FileSystemEdit, FilePosition, Query, SourceChange, SourceFileNodeEdit,
 };
 
 #[derive(Clone, Debug)]
@@ -180,7 +180,7 @@ fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
 }
 
 impl AnalysisImpl {
-    pub fn file_syntax(&self, file_id: FileId) -> File {
+    pub fn file_syntax(&self, file_id: FileId) -> SourceFileNode {
         self.db.file_syntax(file_id)
     }
     pub fn file_line_index(&self, file_id: FileId) -> Arc<LineIndex> {
@@ -226,7 +226,7 @@ pub fn parent_module(&self, position: FilePosition) -> Cancelable<Vec<(FileId, F
         let module_source = match find_node_at_offset::<ast::Module>(file.syntax(), position.offset)
         {
             Some(m) if !m.has_semi() => ModuleSource::new_inline(position.file_id, m),
-            _ => ModuleSource::File(position.file_id),
+            _ => ModuleSource::SourceFile(position.file_id),
         };
 
         let res = module_tree
@@ -254,7 +254,7 @@ pub fn crate_for(&self, file_id: FileId) -> Cancelable<Vec<CrateId>> {
         let module_tree = self.module_tree(file_id)?;
         let crate_graph = self.db.crate_graph();
         let res = module_tree
-            .modules_for_source(ModuleSource::File(file_id))
+            .modules_for_source(ModuleSource::SourceFile(file_id))
             .into_iter()
             .map(|it| it.root(&module_tree))
             .filter_map(|it| it.source(&module_tree).as_file())
@@ -267,18 +267,7 @@ pub fn crate_root(&self, crate_id: CrateId) -> FileId {
         self.db.crate_graph().crate_roots[&crate_id]
     }
     pub fn completions(&self, position: FilePosition) -> Cancelable<Option<Vec<CompletionItem>>> {
-        let mut res = Vec::new();
-        let mut has_completions = false;
-        if let Some(scope_based) = scope_completion(&self.db, position) {
-            res.extend(scope_based);
-            has_completions = true;
-        }
-        if let Some(scope_based) = resolve_based_completion(&self.db, position)? {
-            res.extend(scope_based);
-            has_completions = true;
-        }
-        let res = if has_completions { Some(res) } else { None };
-        Ok(res)
+        completions(&self.db, position)
     }
     pub fn approximately_resolve_symbol(
         &self,
@@ -364,6 +353,16 @@ pub fn find_all_refs(&self, position: FilePosition) -> Vec<(FileId, TextRange)>
         ret
     }
 
+    pub fn doc_comment_for(
+        &self,
+        file_id: FileId,
+        symbol: FileSymbol,
+    ) -> Cancelable<Option<String>> {
+        let file = self.db.file_syntax(file_id);
+
+        Ok(symbol.docs(&file))
+    }
+
     pub fn diagnostics(&self, file_id: FileId) -> Cancelable<Vec<Diagnostic>> {
         let module_tree = self.module_tree(file_id)?;
         let syntax = self.db.file_syntax(file_id);
@@ -376,7 +375,7 @@ pub fn diagnostics(&self, file_id: FileId) -> Cancelable<Vec<Diagnostic>> {
                 fix: None,
             })
             .collect::<Vec<_>>();
-        if let Some(m) = module_tree.any_module_for_source(ModuleSource::File(file_id)) {
+        if let Some(m) = module_tree.any_module_for_source(ModuleSource::SourceFile(file_id)) {
             for (name_node, problem) in m.problems(&module_tree, &*self.db) {
                 let diag = match problem {
                     Problem::UnresolvedModule { candidate } => {
@@ -538,7 +537,7 @@ fn resolve_module(
             Some(name) => name.text(),
             None => return Vec::new(),
         };
-        let module_id = match module_tree.any_module_for_source(ModuleSource::File(file_id)) {
+        let module_id = match module_tree.any_module_for_source(ModuleSource::SourceFile(file_id)) {
             Some(id) => id,
             None => return Vec::new(),
         };
@@ -552,7 +551,7 @@ fn resolve_module(
 
 impl SourceChange {
     pub(crate) fn from_local_edit(file_id: FileId, label: &str, edit: LocalEdit) -> SourceChange {
-        let file_edit = SourceFileEdit {
+        let file_edit = SourceFileNodeEdit {
             file_id,
             edits: edit.edit.into_atoms(),
         };
index 0ea9ebee7b5e3720ae901f389a703badf59b3a99..ab0e3cb0cd165cb0f9b1e171bba8aa79ad44c9b2 100644 (file)
@@ -1,5 +1,5 @@
 //! ra_analyzer crate is the brain of Rust analyzer. It relies on the `salsa`
-//! crate, which provides and incremental on-deman database of facts.
+//! crate, which provides and incremental on-demand database of facts.
 
 extern crate fst;
 extern crate ra_editor;
@@ -20,7 +20,7 @@
 
 use std::{fmt, sync::Arc};
 
-use ra_syntax::{AtomEdit, File, TextRange, TextUnit};
+use ra_syntax::{AtomEdit, SourceFileNode, TextRange, TextUnit};
 use rayon::prelude::*;
 use relative_path::RelativePathBuf;
 
@@ -128,13 +128,13 @@ pub struct FilePosition {
 #[derive(Debug)]
 pub struct SourceChange {
     pub label: String,
-    pub source_file_edits: Vec<SourceFileEdit>,
+    pub source_file_edits: Vec<SourceFileNodeEdit>,
     pub file_system_edits: Vec<FileSystemEdit>,
     pub cursor_position: Option<FilePosition>,
 }
 
 #[derive(Debug)]
-pub struct SourceFileEdit {
+pub struct SourceFileNodeEdit {
     pub file_id: FileId,
     pub edits: Vec<AtomEdit>,
 }
@@ -204,16 +204,16 @@ pub struct Analysis {
 }
 
 impl Analysis {
-    pub fn file_syntax(&self, file_id: FileId) -> File {
+    pub fn file_syntax(&self, file_id: FileId) -> SourceFileNode {
         self.imp.file_syntax(file_id).clone()
     }
     pub fn file_line_index(&self, file_id: FileId) -> Arc<LineIndex> {
         self.imp.file_line_index(file_id)
     }
-    pub fn extend_selection(&self, file: &File, range: TextRange) -> TextRange {
+    pub fn extend_selection(&self, file: &SourceFileNode, range: TextRange) -> TextRange {
         ra_editor::extend_selection(file, range).unwrap_or(range)
     }
-    pub fn matching_brace(&self, file: &File, offset: TextUnit) -> Option<TextUnit> {
+    pub fn matching_brace(&self, file: &SourceFileNode, offset: TextUnit) -> Option<TextUnit> {
         ra_editor::matching_brace(file, offset)
     }
     pub fn syntax_tree(&self, file_id: FileId) -> String {
@@ -258,6 +258,13 @@ pub fn approximately_resolve_symbol(
     pub fn find_all_refs(&self, position: FilePosition) -> Cancelable<Vec<(FileId, TextRange)>> {
         Ok(self.imp.find_all_refs(position))
     }
+    pub fn doc_comment_for(
+        &self,
+        file_id: FileId,
+        symbol: FileSymbol,
+    ) -> Cancelable<Option<String>> {
+        self.imp.doc_comment_for(file_id, symbol)
+    }
     pub fn parent_module(&self, position: FilePosition) -> Cancelable<Vec<(FileId, FileSymbol)>> {
         self.imp.parent_module(position)
     }
@@ -302,7 +309,7 @@ pub struct LibraryData {
 impl LibraryData {
     pub fn prepare(files: Vec<(FileId, String)>, file_resolver: Arc<FileResolver>) -> LibraryData {
         let symbol_index = SymbolIndex::for_files(files.par_iter().map(|(file_id, text)| {
-            let file = File::parse(text);
+            let file = SourceFileNode::parse(text);
             (*file_id, file)
         }));
         LibraryData {
index b57ad5d33137434b78ab2a1a6fef2a00d89e5cbb..3a0667ecda90f6e8eb482dcbd5aec5dd35944dd2 100644 (file)
@@ -6,7 +6,7 @@
 use fst::{self, Streamer};
 use ra_editor::{file_symbols, FileSymbol};
 use ra_syntax::{
-    File,
+    SourceFileNode,
     SyntaxKind::{self, *},
 };
 use rayon::prelude::*;
@@ -34,7 +34,9 @@ fn hash<H: Hasher>(&self, hasher: &mut H) {
 }
 
 impl SymbolIndex {
-    pub(crate) fn for_files(files: impl ParallelIterator<Item = (FileId, File)>) -> SymbolIndex {
+    pub(crate) fn for_files(
+        files: impl ParallelIterator<Item = (FileId, SourceFileNode)>,
+    ) -> SymbolIndex {
         let mut symbols = files
             .flat_map(|(file_id, file)| {
                 file_symbols(&file)
@@ -51,7 +53,7 @@ pub(crate) fn for_files(files: impl ParallelIterator<Item = (FileId, File)>) ->
         SymbolIndex { symbols, map }
     }
 
-    pub(crate) fn for_file(file_id: FileId, file: File) -> SymbolIndex {
+    pub(crate) fn for_file(file_id: FileId, file: SourceFileNode) -> SymbolIndex {
         SymbolIndex::for_files(rayon::iter::once((file_id, file)))
     }
 }
index 4afb1fc93032a2711c4a164f917fa0361b4e7343..194b94584d11eb70f08684b783f0f0a2d614d19a 100644 (file)
@@ -1,4 +1,4 @@
-use ra_syntax::{File, SyntaxKind, SyntaxNode, SyntaxNodeRef, TextRange};
+use ra_syntax::{SourceFileNode, SyntaxKind, SyntaxNode, SyntaxNodeRef, TextRange};
 
 use crate::db::SyntaxDatabase;
 use crate::FileId;
@@ -43,7 +43,7 @@ pub(crate) fn new(node: SyntaxNodeRef) -> LocalSyntaxPtr {
         }
     }
 
-    pub(crate) fn resolve(self, file: &File) -> SyntaxNode {
+    pub(crate) fn resolve(self, file: &SourceFileNode) -> SyntaxNode {
         let mut curr = file.syntax();
         loop {
             if curr.range() == self.range && curr.kind() == self.kind {
@@ -62,12 +62,17 @@ pub(crate) fn into_global(self, file_id: FileId) -> SyntaxPtr {
             local: self,
         }
     }
+
+    // Seems unfortunate to expose
+    pub(crate) fn range(self) -> TextRange {
+        self.range
+    }
 }
 
 #[test]
 fn test_local_syntax_ptr() {
     use ra_syntax::{ast, AstNode};
-    let file = File::parse("struct Foo { f: u32, }");
+    let file = SourceFileNode::parse("struct Foo { f: u32, }");
     let field = file
         .syntax()
         .descendants()
index c605d34f0ba8d338b30936948c585af76c6ea270..719c166b5cb33a58f63b147b7bf4651a64520c60 100644 (file)
@@ -452,3 +452,44 @@ fn test_complete_crate_path() {
         &completions,
     );
 }
+
+#[test]
+fn test_complete_crate_path_with_braces() {
+    let (analysis, position) = analysis_and_position(
+        "
+        //- /lib.rs
+        mod foo;
+        struct Spam;
+        //- /foo.rs
+        use crate::{Sp<|>};
+    ",
+    );
+    let completions = analysis.completions(position).unwrap().unwrap();
+    assert_eq_dbg(
+        r#"[CompletionItem { label: "foo", lookup: None, snippet: None },
+            CompletionItem { label: "Spam", lookup: None, snippet: None }]"#,
+        &completions,
+    );
+}
+
+#[test]
+fn test_complete_crate_path_in_nested_tree() {
+    let (analysis, position) = analysis_and_position(
+        "
+        //- /lib.rs
+        mod foo;
+        pub mod bar {
+            pub mod baz {
+                pub struct Spam;
+            }
+        }
+        //- /foo.rs
+        use crate::{bar::{baz::Sp<|>}};
+    ",
+    );
+    let completions = analysis.completions(position).unwrap().unwrap();
+    assert_eq_dbg(
+        r#"[CompletionItem { label: "Spam", lookup: None, snippet: None }]"#,
+        &completions,
+    );
+}
index 239d846b323ff588083cf60225db4e6dad492e8d..5ca86df4dd523ccb83dc4e6eab2aafcc898eb3c3 100644 (file)
@@ -11,7 +11,7 @@
 use clap::{App, Arg, SubCommand};
 use join_to_string::join;
 use ra_editor::{extend_selection, file_structure, syntax_tree};
-use ra_syntax::{File, TextRange};
+use ra_syntax::{SourceFileNode, TextRange};
 use tools::collect_tests;
 
 type Result<T> = ::std::result::Result<T, failure::Error>;
@@ -79,9 +79,9 @@ fn main() -> Result<()> {
     Ok(())
 }
 
-fn file() -> Result<File> {
+fn file() -> Result<SourceFileNode> {
     let text = read_stdin()?;
-    Ok(File::parse(&text))
+    Ok(SourceFileNode::parse(&text))
 }
 
 fn read_stdin() -> Result<String> {
@@ -100,12 +100,12 @@ fn render_test(file: &Path, line: usize) -> Result<(String, String)> {
         None => bail!("No test found at line {} at {}", line, file.display()),
         Some((_start_line, test)) => test,
     };
-    let file = File::parse(&test.text);
+    let file = SourceFileNode::parse(&test.text);
     let tree = syntax_tree(&file);
     Ok((test.text, tree))
 }
 
-fn selections(file: &File, start: u32, end: u32) -> String {
+fn selections(file: &SourceFileNode, start: u32, end: u32) -> String {
     let mut ranges = Vec::new();
     let mut cur = Some(TextRange::from_to((start - 1).into(), (end - 1).into()));
     while let Some(r) = cur {
index ef6df0d53e2e0c292fb7ff8be779b6ad5af304cf..0139b19d3a0c1416cb2569599b07c13a1b2e0db8 100644 (file)
@@ -3,7 +3,7 @@
 use ra_syntax::{
     algo::{find_covering_node, find_leaf_at_offset},
     ast::{self, AstNode, AttrsOwner, NameOwner, TypeParamsOwner},
-    Direction, File,
+    Direction, SourceFileNode,
     SyntaxKind::{COMMA, WHITESPACE},
     SyntaxNodeRef, TextRange, TextUnit,
 };
@@ -16,7 +16,10 @@ pub struct LocalEdit {
     pub cursor_position: Option<TextUnit>,
 }
 
-pub fn flip_comma<'a>(file: &'a File, offset: TextUnit) -> Option<impl FnOnce() -> LocalEdit + 'a> {
+pub fn flip_comma<'a>(
+    file: &'a SourceFileNode,
+    offset: TextUnit,
+) -> Option<impl FnOnce() -> LocalEdit + 'a> {
     let syntax = file.syntax();
 
     let comma = find_leaf_at_offset(syntax, offset).find(|leaf| leaf.kind() == COMMA)?;
@@ -33,7 +36,10 @@ pub fn flip_comma<'a>(file: &'a File, offset: TextUnit) -> Option<impl FnOnce()
     })
 }
 
-pub fn add_derive<'a>(file: &'a File, offset: TextUnit) -> Option<impl FnOnce() -> LocalEdit + 'a> {
+pub fn add_derive<'a>(
+    file: &'a SourceFileNode,
+    offset: TextUnit,
+) -> Option<impl FnOnce() -> LocalEdit + 'a> {
     let nominal = find_node_at_offset::<ast::NominalDef>(file.syntax(), offset)?;
     Some(move || {
         let derive_attr = nominal
@@ -58,7 +64,10 @@ pub fn add_derive<'a>(file: &'a File, offset: TextUnit) -> Option<impl FnOnce()
     })
 }
 
-pub fn add_impl<'a>(file: &'a File, offset: TextUnit) -> Option<impl FnOnce() -> LocalEdit + 'a> {
+pub fn add_impl<'a>(
+    file: &'a SourceFileNode,
+    offset: TextUnit,
+) -> Option<impl FnOnce() -> LocalEdit + 'a> {
     let nominal = find_node_at_offset::<ast::NominalDef>(file.syntax(), offset)?;
     let name = nominal.name()?;
 
@@ -98,7 +107,7 @@ pub fn add_impl<'a>(file: &'a File, offset: TextUnit) -> Option<impl FnOnce() ->
 }
 
 pub fn introduce_variable<'a>(
-    file: &'a File,
+    file: &'a SourceFileNode,
     range: TextRange,
 ) -> Option<impl FnOnce() -> LocalEdit + 'a> {
     let node = find_covering_node(file.syntax(), range);
index 9d8df25c3adb5a3599c9cb2384cbe4c39e52335a..8f11d5364b749f89785b271b87dc2ca387900e91 100644 (file)
@@ -1,11 +1,11 @@
 use ra_syntax::{
     algo::{find_covering_node, find_leaf_at_offset, LeafAtOffset},
-    Direction, File,
+    Direction, SourceFileNode,
     SyntaxKind::*,
     SyntaxNodeRef, TextRange, TextUnit,
 };
 
-pub fn extend_selection(file: &File, range: TextRange) -> Option<TextRange> {
+pub fn extend_selection(file: &SourceFileNode, range: TextRange) -> Option<TextRange> {
     let syntax = file.syntax();
     extend(syntax.borrowed(), range)
 }
@@ -120,7 +120,7 @@ mod tests {
 
     fn do_check(before: &str, afters: &[&str]) {
         let (cursor, before) = extract_offset(before);
-        let file = File::parse(&before);
+        let file = SourceFileNode::parse(&before);
         let mut range = TextRange::offset_len(cursor, 0.into());
         for &after in afters {
             range = extend_selection(&file, range).unwrap();
index 0803c88913698c936053fc928f2d038dec962a2c..2a8fa3cda95df5dcde2a7aa9d9272f123acc179b 100644 (file)
@@ -1,7 +1,7 @@
 use rustc_hash::FxHashSet;
 
 use ra_syntax::{
-    ast, AstNode, Direction, File,
+    ast, AstNode, Direction, SourceFileNode,
     SyntaxKind::{self, *},
     SyntaxNodeRef, TextRange,
 };
@@ -18,7 +18,7 @@ pub struct Fold {
     pub kind: FoldKind,
 }
 
-pub fn folding_ranges(file: &File) -> Vec<Fold> {
+pub fn folding_ranges(file: &SourceFileNode) -> Vec<Fold> {
     let mut res = vec![];
     let mut visited_comments = FxHashSet::default();
     let mut visited_imports = FxHashSet::default();
@@ -171,7 +171,7 @@ mod tests {
 
     fn do_check(text: &str, fold_kinds: &[FoldKind]) {
         let (ranges, text) = extract_ranges(text);
-        let file = File::parse(&text);
+        let file = SourceFileNode::parse(&text);
         let folds = folding_ranges(&file);
 
         assert_eq!(
index f92181b867cca5be593a0ebb6c7fad23247e6904..ff4e8303de67635681a7e22a116b275aa3fe7eda 100644 (file)
@@ -30,7 +30,7 @@
 use ra_syntax::{
     algo::find_leaf_at_offset,
     ast::{self, AstNode, NameOwner},
-    File,
+    SourceFileNode,
     Location,
     SyntaxKind::{self, *},
     SyntaxNodeRef, TextRange, TextUnit,
@@ -60,7 +60,7 @@ pub enum RunnableKind {
     Bin,
 }
 
-pub fn matching_brace(file: &File, offset: TextUnit) -> Option<TextUnit> {
+pub fn matching_brace(file: &SourceFileNode, offset: TextUnit) -> Option<TextUnit> {
     const BRACES: &[SyntaxKind] = &[
         L_CURLY, R_CURLY, L_BRACK, R_BRACK, L_PAREN, R_PAREN, L_ANGLE, R_ANGLE,
     ];
@@ -78,7 +78,7 @@ pub fn matching_brace(file: &File, offset: TextUnit) -> Option<TextUnit> {
     Some(matching_node.range().start())
 }
 
-pub fn highlight(file: &File) -> Vec<HighlightedRange> {
+pub fn highlight(file: &SourceFileNode) -> Vec<HighlightedRange> {
     let mut res = Vec::new();
     for node in file.syntax().descendants() {
         let tag = match node.kind() {
@@ -100,7 +100,7 @@ pub fn highlight(file: &File) -> Vec<HighlightedRange> {
     res
 }
 
-pub fn diagnostics(file: &File) -> Vec<Diagnostic> {
+pub fn diagnostics(file: &SourceFileNode) -> Vec<Diagnostic> {
     fn location_to_range(location: Location) -> TextRange {
         match location {
             Location::Offset(offset) => TextRange::offset_len(offset, 1.into()),
@@ -117,11 +117,11 @@ fn location_to_range(location: Location) -> TextRange {
         .collect()
 }
 
-pub fn syntax_tree(file: &File) -> String {
+pub fn syntax_tree(file: &SourceFileNode) -> String {
     ::ra_syntax::utils::dump_tree(file.syntax())
 }
 
-pub fn runnables(file: &File) -> Vec<Runnable> {
+pub fn runnables(file: &SourceFileNode) -> Vec<Runnable> {
     file.syntax()
         .descendants()
         .filter_map(ast::FnDef::cast)
@@ -163,7 +163,7 @@ mod tests {
 
     #[test]
     fn test_highlighting() {
-        let file = File::parse(
+        let file = SourceFileNode::parse(
             r#"
 // comment
 fn main() {}
@@ -184,7 +184,7 @@ fn main() {}
 
     #[test]
     fn test_runnables() {
-        let file = File::parse(
+        let file = SourceFileNode::parse(
             r#"
 fn main() {}
 
@@ -209,7 +209,7 @@ fn test_foo() {}
     fn test_matching_brace() {
         fn do_check(before: &str, after: &str) {
             let (pos, before) = extract_offset(before);
-            let file = File::parse(&before);
+            let file = SourceFileNode::parse(&before);
             let new_pos = match matching_brace(&file, pos) {
                 None => pos,
                 Some(pos) => pos,
index 9abbb0d0955185edf22a6f1559559dbfffd071e6..aab7e4081c6c667764f872556bdc353317e290dd 100644 (file)
 use crate::TextUnit;
+use rustc_hash::FxHashMap;
 use superslice::Ext;
 
-#[derive(Clone, Debug, Hash, PartialEq, Eq)]
+#[derive(Clone, Debug, PartialEq, Eq)]
 pub struct LineIndex {
     newlines: Vec<TextUnit>,
+    utf16_lines: FxHashMap<u32, Vec<Utf16Char>>,
 }
 
 #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
 pub struct LineCol {
     pub line: u32,
-    pub col: TextUnit,
+    pub col_utf16: u32,
+}
+
+#[derive(Clone, Debug, Hash, PartialEq, Eq)]
+struct Utf16Char {
+    start: TextUnit,
+    end: TextUnit,
+}
+
+impl Utf16Char {
+    fn len(&self) -> TextUnit {
+        self.end - self.start
+    }
 }
 
 impl LineIndex {
     pub fn new(text: &str) -> LineIndex {
+        let mut utf16_lines = FxHashMap::default();
+        let mut utf16_chars = Vec::new();
+
         let mut newlines = vec![0.into()];
-        let mut curr = 0.into();
+        let mut curr_row = 0.into();
+        let mut curr_col = 0.into();
+        let mut line = 0;
         for c in text.chars() {
-            curr += TextUnit::of_char(c);
+            curr_row += TextUnit::of_char(c);
             if c == '\n' {
-                newlines.push(curr);
+                newlines.push(curr_row);
+
+                // Save any utf-16 characters seen in the previous line
+                if utf16_chars.len() > 0 {
+                    utf16_lines.insert(line, utf16_chars);
+                    utf16_chars = Vec::new();
+                }
+
+                // Prepare for processing the next line
+                curr_col = 0.into();
+                line += 1;
+                continue;
             }
+
+            let char_len = TextUnit::of_char(c);
+            if char_len.to_usize() > 1 {
+                utf16_chars.push(Utf16Char {
+                    start: curr_col,
+                    end: curr_col + char_len,
+                });
+            }
+
+            curr_col += char_len;
+        }
+        LineIndex {
+            newlines,
+            utf16_lines,
         }
-        LineIndex { newlines }
     }
 
     pub fn line_col(&self, offset: TextUnit) -> LineCol {
         let line = self.newlines.upper_bound(&offset) - 1;
         let line_start_offset = self.newlines[line];
         let col = offset - line_start_offset;
+
         LineCol {
             line: line as u32,
-            col,
+            col_utf16: self.utf8_to_utf16_col(line as u32, col) as u32,
         }
     }
 
     pub fn offset(&self, line_col: LineCol) -> TextUnit {
         //TODO: return Result
-        self.newlines[line_col.line as usize] + line_col.col
+        let col = self.utf16_to_utf8_col(line_col.line, line_col.col_utf16);
+        self.newlines[line_col.line as usize] + col
+    }
+
+    fn utf8_to_utf16_col(&self, line: u32, mut col: TextUnit) -> usize {
+        if let Some(utf16_chars) = self.utf16_lines.get(&line) {
+            let mut correction = TextUnit::from_usize(0);
+            for c in utf16_chars {
+                if col >= c.end {
+                    correction += c.len() - TextUnit::from_usize(1);
+                } else {
+                    // From here on, all utf16 characters come *after* the character we are mapping,
+                    // so we don't need to take them into account
+                    break;
+                }
+            }
+
+            col -= correction;
+        }
+
+        col.to_usize()
+    }
+
+    fn utf16_to_utf8_col(&self, line: u32, col: u32) -> TextUnit {
+        let mut col: TextUnit = col.into();
+        if let Some(utf16_chars) = self.utf16_lines.get(&line) {
+            for c in utf16_chars {
+                if col >= c.start {
+                    col += c.len() - TextUnit::from_usize(1);
+                } else {
+                    // From here on, all utf16 characters come *after* the character we are mapping,
+                    // so we don't need to take them into account
+                    break;
+                }
+            }
+        }
+
+        col
     }
 }
 
@@ -49,63 +130,63 @@ fn test_line_index() {
         index.line_col(0.into()),
         LineCol {
             line: 0,
-            col: 0.into()
+            col_utf16: 0
         }
     );
     assert_eq!(
         index.line_col(1.into()),
         LineCol {
             line: 0,
-            col: 1.into()
+            col_utf16: 1
         }
     );
     assert_eq!(
         index.line_col(5.into()),
         LineCol {
             line: 0,
-            col: 5.into()
+            col_utf16: 5
         }
     );
     assert_eq!(
         index.line_col(6.into()),
         LineCol {
             line: 1,
-            col: 0.into()
+            col_utf16: 0
         }
     );
     assert_eq!(
         index.line_col(7.into()),
         LineCol {
             line: 1,
-            col: 1.into()
+            col_utf16: 1
         }
     );
     assert_eq!(
         index.line_col(8.into()),
         LineCol {
             line: 1,
-            col: 2.into()
+            col_utf16: 2
         }
     );
     assert_eq!(
         index.line_col(10.into()),
         LineCol {
             line: 1,
-            col: 4.into()
+            col_utf16: 4
         }
     );
     assert_eq!(
         index.line_col(11.into()),
         LineCol {
             line: 1,
-            col: 5.into()
+            col_utf16: 5
         }
     );
     assert_eq!(
         index.line_col(12.into()),
         LineCol {
             line: 1,
-            col: 6.into()
+            col_utf16: 6
         }
     );
 
@@ -115,35 +196,129 @@ fn test_line_index() {
         index.line_col(0.into()),
         LineCol {
             line: 0,
-            col: 0.into()
+            col_utf16: 0
         }
     );
     assert_eq!(
         index.line_col(1.into()),
         LineCol {
             line: 1,
-            col: 0.into()
+            col_utf16: 0
         }
     );
     assert_eq!(
         index.line_col(2.into()),
         LineCol {
             line: 1,
-            col: 1.into()
+            col_utf16: 1
         }
     );
     assert_eq!(
         index.line_col(6.into()),
         LineCol {
             line: 1,
-            col: 5.into()
+            col_utf16: 5
         }
     );
     assert_eq!(
         index.line_col(7.into()),
         LineCol {
             line: 2,
-            col: 0.into()
+            col_utf16: 0
         }
     );
 }
+
+#[cfg(test)]
+mod test_utf8_utf16_conv {
+    use super::*;
+
+    #[test]
+    fn test_char_len() {
+        assert_eq!('メ'.len_utf8(), 3);
+        assert_eq!('メ'.len_utf16(), 1);
+    }
+
+    #[test]
+    fn test_empty_index() {
+        let col_index = LineIndex::new(
+            "
+const C: char = 'x';
+",
+        );
+        assert_eq!(col_index.utf16_lines.len(), 0);
+    }
+
+    #[test]
+    fn test_single_char() {
+        let col_index = LineIndex::new(
+            "
+const C: char = 'メ';
+",
+        );
+
+        assert_eq!(col_index.utf16_lines.len(), 1);
+        assert_eq!(col_index.utf16_lines[&1].len(), 1);
+        assert_eq!(
+            col_index.utf16_lines[&1][0],
+            Utf16Char {
+                start: 17.into(),
+                end: 20.into()
+            }
+        );
+
+        // UTF-8 to UTF-16, no changes
+        assert_eq!(col_index.utf8_to_utf16_col(1, 15.into()), 15);
+
+        // UTF-8 to UTF-16
+        assert_eq!(col_index.utf8_to_utf16_col(1, 22.into()), 20);
+
+        // UTF-16 to UTF-8, no changes
+        assert_eq!(col_index.utf16_to_utf8_col(1, 15), TextUnit::from(15));
+
+        // UTF-16 to UTF-8
+        assert_eq!(col_index.utf16_to_utf8_col(1, 19), TextUnit::from(21));
+    }
+
+    #[test]
+    fn test_string() {
+        let col_index = LineIndex::new(
+            "
+const C: char = \"メ メ\";
+",
+        );
+
+        assert_eq!(col_index.utf16_lines.len(), 1);
+        assert_eq!(col_index.utf16_lines[&1].len(), 2);
+        assert_eq!(
+            col_index.utf16_lines[&1][0],
+            Utf16Char {
+                start: 17.into(),
+                end: 20.into()
+            }
+        );
+        assert_eq!(
+            col_index.utf16_lines[&1][1],
+            Utf16Char {
+                start: 21.into(),
+                end: 24.into()
+            }
+        );
+
+        // UTF-8 to UTF-16
+        assert_eq!(col_index.utf8_to_utf16_col(1, 15.into()), 15);
+
+        assert_eq!(col_index.utf8_to_utf16_col(1, 21.into()), 19);
+        assert_eq!(col_index.utf8_to_utf16_col(1, 25.into()), 21);
+
+        assert!(col_index.utf8_to_utf16_col(2, 15.into()) == 15);
+
+        // UTF-16 to UTF-8
+        assert_eq!(col_index.utf16_to_utf8_col(1, 15), TextUnit::from_usize(15));
+
+        assert_eq!(col_index.utf16_to_utf8_col(1, 18), TextUnit::from_usize(20));
+        assert_eq!(col_index.utf16_to_utf8_col(1, 19), TextUnit::from_usize(23));
+
+        assert_eq!(col_index.utf16_to_utf8_col(2, 15), TextUnit::from_usize(15));
+    }
+}
index 4e602d0e318efad2c741ceb4f9e297753aa0097a..6d3b0514a2321332331c16fb28382ecbf0284754 100644 (file)
@@ -2,8 +2,8 @@
 
 use ra_syntax::{
     algo::visit::{visitor, Visitor},
-    ast::{self, NameOwner},
-    AstNode, File, SmolStr, SyntaxKind, SyntaxNodeRef, WalkEvent,
+    ast::{self, DocCommentsOwner, NameOwner},
+    AstNode, SourceFileNode, SmolStr, SyntaxKind, SyntaxNodeRef, WalkEvent,
 };
 
 #[derive(Debug, Clone)]
@@ -22,7 +22,37 @@ pub struct FileSymbol {
     pub kind: SyntaxKind,
 }
 
-pub fn file_symbols(file: &File) -> Vec<FileSymbol> {
+impl FileSymbol {
+    pub fn docs(&self, file: &SourceFileNode) -> Option<String> {
+        file.syntax()
+            .descendants()
+            .filter(|node| node.kind() == self.kind && node.range() == self.node_range)
+            .filter_map(|node: SyntaxNodeRef| {
+                fn doc_comments<'a, N: DocCommentsOwner<'a>>(node: N) -> Option<String> {
+                    let comments = node.doc_comment_text();
+                    if comments.is_empty() {
+                        None
+                    } else {
+                        Some(comments)
+                    }
+                }
+
+                visitor()
+                    .visit(doc_comments::<ast::FnDef>)
+                    .visit(doc_comments::<ast::StructDef>)
+                    .visit(doc_comments::<ast::EnumDef>)
+                    .visit(doc_comments::<ast::TraitDef>)
+                    .visit(doc_comments::<ast::Module>)
+                    .visit(doc_comments::<ast::TypeDef>)
+                    .visit(doc_comments::<ast::ConstDef>)
+                    .visit(doc_comments::<ast::StaticDef>)
+                    .accept(node)?
+            })
+            .nth(0)
+    }
+}
+
+pub fn file_symbols(file: &SourceFileNode) -> Vec<FileSymbol> {
     file.syntax().descendants().filter_map(to_symbol).collect()
 }
 
@@ -47,7 +77,7 @@ fn decl<'a, N: NameOwner<'a>>(node: N) -> Option<FileSymbol> {
         .accept(node)?
 }
 
-pub fn file_structure(file: &File) -> Vec<StructureNode> {
+pub fn file_structure(file: &SourceFileNode) -> Vec<StructureNode> {
     let mut res = Vec::new();
     let mut stack = Vec::new();
 
@@ -123,7 +153,7 @@ mod tests {
 
     #[test]
     fn test_file_structure() {
-        let file = File::parse(
+        let file = SourceFileNode::parse(
             r#"
 struct Foo {
     x: i32
index bc3d700f64d097f9346f18cfcfe738f42c0f1d23..cbeb6433b1b7d37bdb78224e6e580e65f0b7c300 100644 (file)
@@ -1,10 +1,14 @@
 use crate::LocalEdit;
 pub use crate::_test_utils::*;
-use ra_syntax::{File, TextRange, TextUnit};
+use ra_syntax::{SourceFileNode, TextRange, TextUnit};
 
-pub fn check_action<F: Fn(&File, TextUnit) -> Option<LocalEdit>>(before: &str, after: &str, f: F) {
+pub fn check_action<F: Fn(&SourceFileNode, TextUnit) -> Option<LocalEdit>>(
+    before: &str,
+    after: &str,
+    f: F,
+) {
     let (before_cursor_pos, before) = extract_offset(before);
-    let file = File::parse(&before);
+    let file = SourceFileNode::parse(&before);
     let result = f(&file, before_cursor_pos).expect("code action is not applicable");
     let actual = result.edit.apply(&before);
     let actual_cursor_pos = match result.cursor_position {
@@ -15,13 +19,13 @@ pub fn check_action<F: Fn(&File, TextUnit) -> Option<LocalEdit>>(before: &str, a
     assert_eq_text!(after, &actual);
 }
 
-pub fn check_action_range<F: Fn(&File, TextRange) -> Option<LocalEdit>>(
+pub fn check_action_range<F: Fn(&SourceFileNode, TextRange) -> Option<LocalEdit>>(
     before: &str,
     after: &str,
     f: F,
 ) {
     let (range, before) = extract_range(before);
-    let file = File::parse(&before);
+    let file = SourceFileNode::parse(&before);
     let result = f(&file, range).expect("code action is not applicable");
     let actual = result.edit.apply(&before);
     let actual_cursor_pos = match result.cursor_position {
index 5a457d14829b4793c919429f50735c9b7662dee6..f894d83922a6901f682ad0bef1e30ac2fb2f8169 100644 (file)
@@ -4,14 +4,14 @@
     algo::{find_covering_node, find_leaf_at_offset, LeafAtOffset},
     ast,
     text_utils::{contains_offset_nonstrict, intersect},
-    AstNode, File, SyntaxKind,
+    AstNode, SourceFileNode, SyntaxKind,
     SyntaxKind::*,
     SyntaxNodeRef, TextRange, TextUnit,
 };
 
 use crate::{find_node_at_offset, EditBuilder, LocalEdit};
 
-pub fn join_lines(file: &File, range: TextRange) -> LocalEdit {
+pub fn join_lines(file: &SourceFileNode, range: TextRange) -> LocalEdit {
     let range = if range.is_empty() {
         let syntax = file.syntax();
         let text = syntax.text().slice(range.start()..);
@@ -55,7 +55,7 @@ pub fn join_lines(file: &File, range: TextRange) -> LocalEdit {
     }
 }
 
-pub fn on_enter(file: &File, offset: TextUnit) -> Option<LocalEdit> {
+pub fn on_enter(file: &SourceFileNode, offset: TextUnit) -> Option<LocalEdit> {
     let comment = find_leaf_at_offset(file.syntax(), offset)
         .left_biased()
         .and_then(ast::Comment::cast)?;
@@ -80,7 +80,7 @@ pub fn on_enter(file: &File, offset: TextUnit) -> Option<LocalEdit> {
     })
 }
 
-fn node_indent<'a>(file: &'a File, node: SyntaxNodeRef) -> Option<&'a str> {
+fn node_indent<'a>(file: &'a SourceFileNode, node: SyntaxNodeRef) -> Option<&'a str> {
     let ws = match find_leaf_at_offset(file.syntax(), node.range().start()) {
         LeafAtOffset::Between(l, r) => {
             assert!(r == node);
@@ -100,7 +100,7 @@ fn node_indent<'a>(file: &'a File, node: SyntaxNodeRef) -> Option<&'a str> {
     Some(&text[pos..])
 }
 
-pub fn on_eq_typed(file: &File, offset: TextUnit) -> Option<LocalEdit> {
+pub fn on_eq_typed(file: &SourceFileNode, offset: TextUnit) -> Option<LocalEdit> {
     let let_stmt: ast::LetStmt = find_node_at_offset(file.syntax(), offset)?;
     if let_stmt.has_semi() {
         return None;
@@ -390,7 +390,7 @@ fn foo() {
 
     fn check_join_lines_sel(before: &str, after: &str) {
         let (sel, before) = extract_range(before);
-        let file = File::parse(&before);
+        let file = SourceFileNode::parse(&before);
         let result = join_lines(&file, sel);
         let actual = result.edit.apply(&before);
         assert_eq_text!(after, &actual);
@@ -469,7 +469,7 @@ pub fn handle_find_matching_brace() {
     fn test_on_eq_typed() {
         fn do_check(before: &str, after: &str) {
             let (offset, before) = extract_offset(before);
-            let file = File::parse(&before);
+            let file = SourceFileNode::parse(&before);
             let result = on_eq_typed(&file, offset).unwrap();
             let actual = result.edit.apply(&before);
             assert_eq_text!(after, &actual);
@@ -513,7 +513,7 @@ fn foo() {
     fn test_on_enter() {
         fn apply_on_enter(before: &str) -> Option<String> {
             let (offset, before) = extract_offset(before);
-            let file = File::parse(&before);
+            let file = SourceFileNode::parse(&before);
             let result = on_enter(&file, offset)?;
             let actual = result.edit.apply(&before);
             let actual = add_cursor(&actual, result.cursor_position.unwrap());
index f29dafc17cdafc116a5d10c6842729048ccad3a5..79d86f9b2d4d99fb104cfe0680d7708c7d655b4e 100644 (file)
@@ -14,7 +14,7 @@ serde = "1.0.71"
 serde_derive = "1.0.71"
 drop_bomb = "0.1.0"
 crossbeam-channel = "0.2.4"
-flexi_logger = "0.9.1"
+flexi_logger = "0.10.0"
 log = "0.4.3"
 url_serde = "0.2.0"
 languageserver-types = "0.51.0"
index 0d02c8073177c4d31ffe12c4db465d5a37d5a199..bcf857fce831f82dd7f00c7006fda46cd47285a2 100644 (file)
@@ -16,7 +16,7 @@ pub fn server_capabilities() -> ServerCapabilities {
                 save: None,
             },
         )),
-        hover_provider: None,
+        hover_provider: Some(true),
         completion_provider: Some(CompletionOptions {
             resolve_provider: None,
             trigger_characters: None,
index fa04f4b00bd998e819e2ca33033a16f890bdd22d..5d5a0c55ee7e24b0ebce792af38bd762e3a05d81 100644 (file)
@@ -2,7 +2,7 @@
     Location, Position, Range, SymbolKind, TextDocumentEdit, TextDocumentIdentifier,
     TextDocumentItem, TextDocumentPositionParams, TextEdit, Url, VersionedTextDocumentIdentifier,
 };
-use ra_analysis::{FileId, FileSystemEdit, SourceChange, SourceFileEdit, FilePosition};
+use ra_analysis::{FileId, FileSystemEdit, SourceChange, SourceFileNodeEdit, FilePosition};
 use ra_editor::{AtomEdit, Edit, LineCol, LineIndex};
 use ra_syntax::{SyntaxKind, TextRange, TextUnit};
 
@@ -49,10 +49,9 @@ impl ConvWith for Position {
     type Output = TextUnit;
 
     fn conv_with(self, line_index: &LineIndex) -> TextUnit {
-        // TODO: UTF-16
         let line_col = LineCol {
             line: self.line as u32,
-            col: (self.character as u32).into(),
+            col_utf16: self.character as u32,
         };
         line_index.offset(line_col)
     }
@@ -64,8 +63,10 @@ impl ConvWith for TextUnit {
 
     fn conv_with(self, line_index: &LineIndex) -> Position {
         let line_col = line_index.line_col(self);
-        // TODO: UTF-16
-        Position::new(u64::from(line_col.line), u64::from(u32::from(line_col.col)))
+        Position::new(
+            u64::from(line_col.line),
+            u64::from(u32::from(line_col.col_utf16)),
+        )
     }
 }
 
@@ -203,8 +204,10 @@ fn try_conv_with(self, world: &ServerWorld) -> Result<req::SourceChange> {
                     .map(|it| it.edits.as_slice())
                     .unwrap_or(&[]);
                 let line_col = translate_offset_with_edit(&*line_index, pos.offset, edits);
-                let position =
-                    Position::new(u64::from(line_col.line), u64::from(u32::from(line_col.col)));
+                let position = Position::new(
+                    u64::from(line_col.line),
+                    u64::from(u32::from(line_col.col_utf16)),
+                );
                 Some(TextDocumentPositionParams {
                     text_document: TextDocumentIdentifier::new(pos.file_id.try_conv_with(world)?),
                     position,
@@ -247,17 +250,17 @@ fn translate_offset_with_edit(
     if in_edit_line_col.line == 0 {
         LineCol {
             line: edit_line_col.line,
-            col: edit_line_col.col + in_edit_line_col.col,
+            col_utf16: edit_line_col.col_utf16 + in_edit_line_col.col_utf16,
         }
     } else {
         LineCol {
             line: edit_line_col.line + in_edit_line_col.line,
-            col: in_edit_line_col.col,
+            col_utf16: in_edit_line_col.col_utf16,
         }
     }
 }
 
-impl TryConvWith for SourceFileEdit {
+impl TryConvWith for SourceFileNodeEdit {
     type Ctx = ServerWorld;
     type Output = TextDocumentEdit;
     fn try_conv_with(self, world: &ServerWorld) -> Result<TextDocumentEdit> {
index c07eb01404dcb63bf1e9ca30eca66ce8b94c2189..26bcddd8e67b8fb721ebe01c97c8901b3382a595 100644 (file)
@@ -2,10 +2,14 @@
 extern crate log;
 #[macro_use]
 extern crate failure;
+#[macro_use]
+extern crate serde_derive;
+extern crate serde;
 extern crate flexi_logger;
 extern crate gen_lsp_server;
 extern crate ra_lsp_server;
 
+use serde::Deserialize;
 use flexi_logger::{Duplicate, Logger};
 use gen_lsp_server::{run_server, stdio_transport};
 use ra_lsp_server::Result;
@@ -30,6 +34,12 @@ fn main() -> Result<()> {
     }
 }
 
+#[derive(Deserialize)]
+#[serde(rename_all = "camelCase")]
+struct InitializationOptions {
+    publish_decorations: bool,
+}
+
 fn main_inner() -> Result<()> {
     let (receiver, sender, threads) = stdio_transport();
     let cwd = ::std::env::current_dir()?;
@@ -42,7 +52,12 @@ fn main_inner() -> Result<()> {
                 .root_uri
                 .and_then(|it| it.to_file_path().ok())
                 .unwrap_or(cwd);
-            ra_lsp_server::main_loop(false, root, r, s)
+            let publish_decorations = params
+                .initialization_options
+                .and_then(|v| InitializationOptions::deserialize(v).ok())
+                .map(|it| it.publish_decorations)
+                == Some(true);
+            ra_lsp_server::main_loop(false, root, publish_decorations, r, s)
         },
     )?;
     info!("shutting down IO...");
@@ -50,3 +65,27 @@ fn main_inner() -> Result<()> {
     info!("... IO is down");
     Ok(())
 }
+
+/*
+                    (let ((backend (eglot-xref-backend)))
+                      (mapcar
+                       (lambda (xref)
+                         (let ((loc (xref-item-location xref)))
+                           (propertize
+                            (concat
+                             (when (xref-file-location-p loc)
+                               (with-slots (file line column) loc
+                                 (format "%s:%s:%s:"
+                                         (propertize (file-relative-name file)
+                                                     'face 'compilation-info)
+                                         (propertize (format "%s" line)
+                                                     'face 'compilation-line
+                                                     )
+                                         column)))
+                             (xref-item-summary xref))
+                            'xref xref)))
+                       (xref-backend-apropos backend "Analysis"))
+                      )
+
+
+*/
index 5314a333e8283ae65755dbf79101d9c2c34b4184..c872b0dc4078b49aa9eb934a118cfdfa3b83f2cc 100644 (file)
@@ -4,9 +4,9 @@
 use languageserver_types::{
     CodeActionResponse, Command, CompletionItem, CompletionItemKind, Diagnostic,
     DiagnosticSeverity, DocumentSymbol, Documentation, FoldingRange, FoldingRangeKind,
-    FoldingRangeParams, InsertTextFormat, Location, MarkupContent, MarkupKind, Position,
+    FoldingRangeParams, InsertTextFormat, Location, MarkupContent, MarkupKind, MarkedString, Position,
     PrepareRenameResponse, RenameParams, SymbolInformation, TextDocumentIdentifier, TextEdit,
-    WorkspaceEdit, ParameterInformation, SignatureInformation,
+    WorkspaceEdit, ParameterInformation, SignatureInformation, Hover, HoverContents,
 };
 use ra_analysis::{FileId, FoldKind, Query, RunnableKind, FilePosition};
 use ra_syntax::text_utils::contains_offset_nonstrict;
@@ -478,6 +478,30 @@ pub fn handle_signature_help(
     }
 }
 
+pub fn handle_hover(
+    world: ServerWorld,
+    params: req::TextDocumentPositionParams,
+) -> Result<Option<Hover>> {
+    let position = params.try_conv_with(&world)?;
+    let line_index = world.analysis().file_line_index(position.file_id);
+
+    for (file_id, symbol) in world.analysis().approximately_resolve_symbol(position)? {
+        let range = symbol.node_range.conv_with(&line_index);
+        let comment = world.analysis.doc_comment_for(file_id, symbol)?;
+
+        if comment.is_some() {
+            let contents = HoverContents::Scalar(MarkedString::String(comment.unwrap()));
+
+            return Ok(Some(Hover {
+                contents,
+                range: Some(range),
+            }));
+        }
+    }
+
+    Ok(None)
+}
+
 pub fn handle_prepare_rename(
     world: ServerWorld,
     params: req::TextDocumentPositionParams,
index 229d1b0f70f5ad2c911ed9854c3052f6c9cf8d88..78d93741a08d61e5683fecf85f64238596070487 100644 (file)
@@ -48,6 +48,7 @@ enum Task {
 pub fn main_loop(
     internal_mode: bool,
     root: PathBuf,
+    publish_decorations: bool,
     msg_receiver: &Receiver<RawMessage>,
     msg_sender: &Sender<RawMessage>,
 ) -> Result<()> {
@@ -67,6 +68,7 @@ pub fn main_loop(
     let mut subs = Subscriptions::new();
     let main_res = main_loop_inner(
         internal_mode,
+        publish_decorations,
         root,
         &pool,
         msg_sender,
@@ -99,6 +101,7 @@ pub fn main_loop(
 
 fn main_loop_inner(
     internal_mode: bool,
+    publish_decorations: bool,
     ws_root: PathBuf,
     pool: &ThreadPool,
     msg_sender: &Sender<RawMessage>,
@@ -210,6 +213,7 @@ enum Event {
             update_file_notifications_on_threadpool(
                 pool,
                 state.snapshot(),
+                publish_decorations,
                 task_sender.clone(),
                 subs.subscriptions(),
             )
@@ -259,6 +263,7 @@ fn on_request(
         .on::<req::CodeActionRequest>(handlers::handle_code_action)?
         .on::<req::FoldingRangeRequest>(handlers::handle_folding_range)?
         .on::<req::SignatureHelpRequest>(handlers::handle_signature_help)?
+        .on::<req::HoverRequest>(handlers::handle_hover)?
         .on::<req::PrepareRenameRequest>(handlers::handle_prepare_rename)?
         .on::<req::Rename>(handlers::handle_rename)?
         .on::<req::References>(handlers::handle_references)?
@@ -415,6 +420,7 @@ fn finish(&mut self) -> ::std::result::Result<u64, RawRequest> {
 fn update_file_notifications_on_threadpool(
     pool: &ThreadPool,
     world: ServerWorld,
+    publish_decorations: bool,
     sender: Sender<Task>,
     subscriptions: Vec<FileId>,
 ) {
@@ -431,15 +437,17 @@ fn update_file_notifications_on_threadpool(
                     sender.send(Task::Notify(not));
                 }
             }
-            match handlers::publish_decorations(&world, file_id) {
-                Err(e) => {
-                    if !is_canceled(&e) {
-                        error!("failed to compute decorations: {:?}", e);
+            if publish_decorations {
+                match handlers::publish_decorations(&world, file_id) {
+                    Err(e) => {
+                        if !is_canceled(&e) {
+                            error!("failed to compute decorations: {:?}", e);
+                        }
+                    }
+                    Ok(params) => {
+                        let not = RawNotification::new::<req::PublishDecorations>(&params);
+                        sender.send(Task::Notify(not))
                     }
-                }
-                Ok(params) => {
-                    let not = RawNotification::new::<req::PublishDecorations>(&params);
-                    sender.send(Task::Notify(not))
                 }
             }
         }
index b90d211357052b66c4c22d86b3e374430afdad61..88d379bfaa597797d575bccb4ac03db91a5dc086 100644 (file)
@@ -55,7 +55,7 @@ fn new(dir: TempDir, files: Vec<(PathBuf, String)>) -> Server {
             "test server",
             128,
             move |mut msg_receiver, mut msg_sender| {
-                main_loop(true, path, &mut msg_receiver, &mut msg_sender).unwrap()
+                main_loop(true, path, true, &mut msg_receiver, &mut msg_sender).unwrap()
             },
         );
         let res = Server {
index 97d25957092f0ae2a3863055a67ec1de884b3b23..54ee72386170656978c27808aa0cdb06250b06f2 100644 (file)
@@ -8,6 +8,7 @@ description = "Comment and whitespace preserving parser for the Rust langauge"
 repository = "https://github.com/rust-analyzer/rust-analyzer"
 
 [dependencies]
+arrayvec = "0.4.7"
 unicode-xid = "0.1.0"
 itertools = "0.7.8"
 drop_bomb = "0.1.4"
index 420d9090c2f5e759f3c854e2c5a9b888c973e9c9..bf056131efc4409ffc3880ba1e923f3b5caba1c3 100644 (file)
@@ -9,6 +9,8 @@
 
 #![cfg_attr(rustfmt, rustfmt_skip)]
 
+use std::hash::{Hash, Hasher};
+
 use crate::{
     ast,
     SyntaxNode, SyntaxNodeRef, AstNode,
 };
 
 // ArgList
-#[derive(Debug, Clone, Copy)]
+#[derive(Debug, Clone, Copy,)]
 pub struct ArgListNode<R: TreeRoot<RaTypes> = OwnedRoot> {
-    syntax: SyntaxNode<R>,
+    pub(crate) syntax: SyntaxNode<R>,
 }
 pub type ArgList<'a> = ArgListNode<RefRoot<'a>>;
 
+impl<R1: TreeRoot<RaTypes>, R2: TreeRoot<RaTypes>> PartialEq<ArgListNode<R1>> for ArgListNode<R2> {
+    fn eq(&self, other: &ArgListNode<R1>) -> bool { self.syntax == other.syntax }
+}
+impl<R: TreeRoot<RaTypes>> Eq for ArgListNode<R> {}
+impl<R: TreeRoot<RaTypes>> Hash for ArgListNode<R> {
+    fn hash<H: Hasher>(&self, state: &mut H) { self.syntax.hash(state) }
+}
+
 impl<'a> AstNode<'a> for ArgList<'a> {
     fn cast(syntax: SyntaxNodeRef<'a>) -> Option<Self> {
         match syntax.kind() {
@@ -50,12 +60,20 @@ pub fn args(self) -> impl Iterator<Item = Expr<'a>> + 'a {
 }
 
 // ArrayExpr
-#[derive(Debug, Clone, Copy)]
+#[derive(Debug, Clone, Copy,)]
 pub struct ArrayExprNode<R: TreeRoot<RaTypes> = OwnedRoot> {
-    syntax: SyntaxNode<R>,
+    pub(crate) syntax: SyntaxNode<R>,
 }
 pub type ArrayExpr<'a> = ArrayExprNode<RefRoot<'a>>;
 
+impl<R1: TreeRoot<RaTypes>, R2: TreeRoot<RaTypes>> PartialEq<ArrayExprNode<R1>> for ArrayExprNode<R2> {
+    fn eq(&self, other: &ArrayExprNode<R1>) -> bool { self.syntax == other.syntax }
+}
+impl<R: TreeRoot<RaTypes>> Eq for ArrayExprNode<R> {}
+impl<R: TreeRoot<RaTypes>> Hash for ArrayExprNode<R> {
+    fn hash<H: Hasher>(&self, state: &mut H) { self.syntax.hash(state) }
+}
+
 impl<'a> AstNode<'a> for ArrayExpr<'a> {
     fn cast(syntax: SyntaxNodeRef<'a>) -> Option<Self> {
         match syntax.kind() {
@@ -79,12 +97,20 @@ pub fn owned(&self) -> ArrayExprNode {
 impl<'a> ArrayExpr<'a> {}
 
 // ArrayType
-#[derive(Debug, Clone, Copy)]
+#[derive(Debug, Clone, Copy,)]
 pub struct ArrayTypeNode<R: TreeRoot<RaTypes> = OwnedRoot> {
-    syntax: SyntaxNode<R>,
+    pub(crate) syntax: SyntaxNode<R>,
 }
 pub type ArrayType<'a> = ArrayTypeNode<RefRoot<'a>>;
 
+impl<R1: TreeRoot<RaTypes>, R2: TreeRoot<RaTypes>> PartialEq<ArrayTypeNode<R1>> for ArrayTypeNode<R2> {
+    fn eq(&self, other: &ArrayTypeNode<R1>) -> bool { self.syntax == other.syntax }
+}
+impl<R: TreeRoot<RaTypes>> Eq for ArrayTypeNode<R> {}
+impl<R: TreeRoot<RaTypes>> Hash for ArrayTypeNode<R> {
+    fn hash<H: Hasher>(&self, state: &mut H) { self.syntax.hash(state) }
+}
+
 impl<'a> AstNode<'a> for ArrayType<'a> {
     fn cast(syntax: SyntaxNodeRef<'a>) -> Option<Self> {
         match syntax.kind() {
@@ -108,12 +134,20 @@ pub fn owned(&self) -> ArrayTypeNode {
 impl<'a> ArrayType<'a> {}
 
 // Attr
-#[derive(Debug, Clone, Copy)]
+#[derive(Debug, Clone, Copy,)]
 pub struct AttrNode<R: TreeRoot<RaTypes> = OwnedRoot> {
-    syntax: SyntaxNode<R>,
+    pub(crate) syntax: SyntaxNode<R>,
 }
 pub type Attr<'a> = AttrNode<RefRoot<'a>>;
 
+impl<R1: TreeRoot<RaTypes>, R2: TreeRoot<RaTypes>> PartialEq<AttrNode<R1>> for AttrNode<R2> {
+    fn eq(&self, other: &AttrNode<R1>) -> bool { self.syntax == other.syntax }
+}
+impl<R: TreeRoot<RaTypes>> Eq for AttrNode<R> {}
+impl<R: TreeRoot<RaTypes>> Hash for AttrNode<R> {
+    fn hash<H: Hasher>(&self, state: &mut H) { self.syntax.hash(state) }
+}
+
 impl<'a> AstNode<'a> for Attr<'a> {
     fn cast(syntax: SyntaxNodeRef<'a>) -> Option<Self> {
         match syntax.kind() {
@@ -141,12 +175,20 @@ pub fn value(self) -> Option<TokenTree<'a>> {
 }
 
 // BinExpr
-#[derive(Debug, Clone, Copy)]
+#[derive(Debug, Clone, Copy,)]
 pub struct BinExprNode<R: TreeRoot<RaTypes> = OwnedRoot> {
-    syntax: SyntaxNode<R>,
+    pub(crate) syntax: SyntaxNode<R>,
 }
 pub type BinExpr<'a> = BinExprNode<RefRoot<'a>>;
 
+impl<R1: TreeRoot<RaTypes>, R2: TreeRoot<RaTypes>> PartialEq<BinExprNode<R1>> for BinExprNode<R2> {
+    fn eq(&self, other: &BinExprNode<R1>) -> bool { self.syntax == other.syntax }
+}
+impl<R: TreeRoot<RaTypes>> Eq for BinExprNode<R> {}
+impl<R: TreeRoot<RaTypes>> Hash for BinExprNode<R> {
+    fn hash<H: Hasher>(&self, state: &mut H) { self.syntax.hash(state) }
+}
+
 impl<'a> AstNode<'a> for BinExpr<'a> {
     fn cast(syntax: SyntaxNodeRef<'a>) -> Option<Self> {
         match syntax.kind() {
@@ -170,12 +212,20 @@ pub fn owned(&self) -> BinExprNode {
 impl<'a> BinExpr<'a> {}
 
 // BindPat
-#[derive(Debug, Clone, Copy)]
+#[derive(Debug, Clone, Copy,)]
 pub struct BindPatNode<R: TreeRoot<RaTypes> = OwnedRoot> {
-    syntax: SyntaxNode<R>,
+    pub(crate) syntax: SyntaxNode<R>,
 }
 pub type BindPat<'a> = BindPatNode<RefRoot<'a>>;
 
+impl<R1: TreeRoot<RaTypes>, R2: TreeRoot<RaTypes>> PartialEq<BindPatNode<R1>> for BindPatNode<R2> {
+    fn eq(&self, other: &BindPatNode<R1>) -> bool { self.syntax == other.syntax }
+}
+impl<R: TreeRoot<RaTypes>> Eq for BindPatNode<R> {}
+impl<R: TreeRoot<RaTypes>> Hash for BindPatNode<R> {
+    fn hash<H: Hasher>(&self, state: &mut H) { self.syntax.hash(state) }
+}
+
 impl<'a> AstNode<'a> for BindPat<'a> {
     fn cast(syntax: SyntaxNodeRef<'a>) -> Option<Self> {
         match syntax.kind() {
@@ -200,12 +250,20 @@ impl<'a> ast::NameOwner<'a> for BindPat<'a> {}
 impl<'a> BindPat<'a> {}
 
 // Block
-#[derive(Debug, Clone, Copy)]
+#[derive(Debug, Clone, Copy,)]
 pub struct BlockNode<R: TreeRoot<RaTypes> = OwnedRoot> {
-    syntax: SyntaxNode<R>,
+    pub(crate) syntax: SyntaxNode<R>,
 }
 pub type Block<'a> = BlockNode<RefRoot<'a>>;
 
+impl<R1: TreeRoot<RaTypes>, R2: TreeRoot<RaTypes>> PartialEq<BlockNode<R1>> for BlockNode<R2> {
+    fn eq(&self, other: &BlockNode<R1>) -> bool { self.syntax == other.syntax }
+}
+impl<R: TreeRoot<RaTypes>> Eq for BlockNode<R> {}
+impl<R: TreeRoot<RaTypes>> Hash for BlockNode<R> {
+    fn hash<H: Hasher>(&self, state: &mut H) { self.syntax.hash(state) }
+}
+
 impl<'a> AstNode<'a> for Block<'a> {
     fn cast(syntax: SyntaxNodeRef<'a>) -> Option<Self> {
         match syntax.kind() {
@@ -237,12 +295,20 @@ pub fn expr(self) -> Option<Expr<'a>> {
 }
 
 // BlockExpr
-#[derive(Debug, Clone, Copy)]
+#[derive(Debug, Clone, Copy,)]
 pub struct BlockExprNode<R: TreeRoot<RaTypes> = OwnedRoot> {
-    syntax: SyntaxNode<R>,
+    pub(crate) syntax: SyntaxNode<R>,
 }
 pub type BlockExpr<'a> = BlockExprNode<RefRoot<'a>>;
 
+impl<R1: TreeRoot<RaTypes>, R2: TreeRoot<RaTypes>> PartialEq<BlockExprNode<R1>> for BlockExprNode<R2> {
+    fn eq(&self, other: &BlockExprNode<R1>) -> bool { self.syntax == other.syntax }
+}
+impl<R: TreeRoot<RaTypes>> Eq for BlockExprNode<R> {}
+impl<R: TreeRoot<RaTypes>> Hash for BlockExprNode<R> {
+    fn hash<H: Hasher>(&self, state: &mut H) { self.syntax.hash(state) }
+}
+
 impl<'a> AstNode<'a> for BlockExpr<'a> {
     fn cast(syntax: SyntaxNodeRef<'a>) -> Option<Self> {
         match syntax.kind() {
@@ -270,12 +336,20 @@ pub fn block(self) -> Option<Block<'a>> {
 }
 
 // BreakExpr
-#[derive(Debug, Clone, Copy)]
+#[derive(Debug, Clone, Copy,)]
 pub struct BreakExprNode<R: TreeRoot<RaTypes> = OwnedRoot> {
-    syntax: SyntaxNode<R>,
+    pub(crate) syntax: SyntaxNode<R>,
 }
 pub type BreakExpr<'a> = BreakExprNode<RefRoot<'a>>;
 
+impl<R1: TreeRoot<RaTypes>, R2: TreeRoot<RaTypes>> PartialEq<BreakExprNode<R1>> for BreakExprNode<R2> {
+    fn eq(&self, other: &BreakExprNode<R1>) -> bool { self.syntax == other.syntax }
+}
+impl<R: TreeRoot<RaTypes>> Eq for BreakExprNode<R> {}
+impl<R: TreeRoot<RaTypes>> Hash for BreakExprNode<R> {
+    fn hash<H: Hasher>(&self, state: &mut H) { self.syntax.hash(state) }
+}
+
 impl<'a> AstNode<'a> for BreakExpr<'a> {
     fn cast(syntax: SyntaxNodeRef<'a>) -> Option<Self> {
         match syntax.kind() {
@@ -298,13 +372,95 @@ pub fn owned(&self) -> BreakExprNode {
 
 impl<'a> BreakExpr<'a> {}
 
+// Byte
+#[derive(Debug, Clone, Copy,)]
+pub struct ByteNode<R: TreeRoot<RaTypes> = OwnedRoot> {
+    pub(crate) syntax: SyntaxNode<R>,
+}
+pub type Byte<'a> = ByteNode<RefRoot<'a>>;
+
+impl<R1: TreeRoot<RaTypes>, R2: TreeRoot<RaTypes>> PartialEq<ByteNode<R1>> for ByteNode<R2> {
+    fn eq(&self, other: &ByteNode<R1>) -> bool { self.syntax == other.syntax }
+}
+impl<R: TreeRoot<RaTypes>> Eq for ByteNode<R> {}
+impl<R: TreeRoot<RaTypes>> Hash for ByteNode<R> {
+    fn hash<H: Hasher>(&self, state: &mut H) { self.syntax.hash(state) }
+}
+
+impl<'a> AstNode<'a> for Byte<'a> {
+    fn cast(syntax: SyntaxNodeRef<'a>) -> Option<Self> {
+        match syntax.kind() {
+            BYTE => Some(Byte { syntax }),
+            _ => None,
+        }
+    }
+    fn syntax(self) -> SyntaxNodeRef<'a> { self.syntax }
+}
+
+impl<R: TreeRoot<RaTypes>> ByteNode<R> {
+    pub fn borrowed(&self) -> Byte {
+        ByteNode { syntax: self.syntax.borrowed() }
+    }
+    pub fn owned(&self) -> ByteNode {
+        ByteNode { syntax: self.syntax.owned() }
+    }
+}
+
+
+impl<'a> Byte<'a> {}
+
+// ByteString
+#[derive(Debug, Clone, Copy,)]
+pub struct ByteStringNode<R: TreeRoot<RaTypes> = OwnedRoot> {
+    pub(crate) syntax: SyntaxNode<R>,
+}
+pub type ByteString<'a> = ByteStringNode<RefRoot<'a>>;
+
+impl<R1: TreeRoot<RaTypes>, R2: TreeRoot<RaTypes>> PartialEq<ByteStringNode<R1>> for ByteStringNode<R2> {
+    fn eq(&self, other: &ByteStringNode<R1>) -> bool { self.syntax == other.syntax }
+}
+impl<R: TreeRoot<RaTypes>> Eq for ByteStringNode<R> {}
+impl<R: TreeRoot<RaTypes>> Hash for ByteStringNode<R> {
+    fn hash<H: Hasher>(&self, state: &mut H) { self.syntax.hash(state) }
+}
+
+impl<'a> AstNode<'a> for ByteString<'a> {
+    fn cast(syntax: SyntaxNodeRef<'a>) -> Option<Self> {
+        match syntax.kind() {
+            BYTE_STRING => Some(ByteString { syntax }),
+            _ => None,
+        }
+    }
+    fn syntax(self) -> SyntaxNodeRef<'a> { self.syntax }
+}
+
+impl<R: TreeRoot<RaTypes>> ByteStringNode<R> {
+    pub fn borrowed(&self) -> ByteString {
+        ByteStringNode { syntax: self.syntax.borrowed() }
+    }
+    pub fn owned(&self) -> ByteStringNode {
+        ByteStringNode { syntax: self.syntax.owned() }
+    }
+}
+
+
+impl<'a> ByteString<'a> {}
+
 // CallExpr
-#[derive(Debug, Clone, Copy)]
+#[derive(Debug, Clone, Copy,)]
 pub struct CallExprNode<R: TreeRoot<RaTypes> = OwnedRoot> {
-    syntax: SyntaxNode<R>,
+    pub(crate) syntax: SyntaxNode<R>,
 }
 pub type CallExpr<'a> = CallExprNode<RefRoot<'a>>;
 
+impl<R1: TreeRoot<RaTypes>, R2: TreeRoot<RaTypes>> PartialEq<CallExprNode<R1>> for CallExprNode<R2> {
+    fn eq(&self, other: &CallExprNode<R1>) -> bool { self.syntax == other.syntax }
+}
+impl<R: TreeRoot<RaTypes>> Eq for CallExprNode<R> {}
+impl<R: TreeRoot<RaTypes>> Hash for CallExprNode<R> {
+    fn hash<H: Hasher>(&self, state: &mut H) { self.syntax.hash(state) }
+}
+
 impl<'a> AstNode<'a> for CallExpr<'a> {
     fn cast(syntax: SyntaxNodeRef<'a>) -> Option<Self> {
         match syntax.kind() {
@@ -333,12 +489,20 @@ pub fn expr(self) -> Option<Expr<'a>> {
 }
 
 // CastExpr
-#[derive(Debug, Clone, Copy)]
+#[derive(Debug, Clone, Copy,)]
 pub struct CastExprNode<R: TreeRoot<RaTypes> = OwnedRoot> {
-    syntax: SyntaxNode<R>,
+    pub(crate) syntax: SyntaxNode<R>,
 }
 pub type CastExpr<'a> = CastExprNode<RefRoot<'a>>;
 
+impl<R1: TreeRoot<RaTypes>, R2: TreeRoot<RaTypes>> PartialEq<CastExprNode<R1>> for CastExprNode<R2> {
+    fn eq(&self, other: &CastExprNode<R1>) -> bool { self.syntax == other.syntax }
+}
+impl<R: TreeRoot<RaTypes>> Eq for CastExprNode<R> {}
+impl<R: TreeRoot<RaTypes>> Hash for CastExprNode<R> {
+    fn hash<H: Hasher>(&self, state: &mut H) { self.syntax.hash(state) }
+}
+
 impl<'a> AstNode<'a> for CastExpr<'a> {
     fn cast(syntax: SyntaxNodeRef<'a>) -> Option<Self> {
         match syntax.kind() {
@@ -362,12 +526,20 @@ pub fn owned(&self) -> CastExprNode {
 impl<'a> CastExpr<'a> {}
 
 // Char
-#[derive(Debug, Clone, Copy)]
+#[derive(Debug, Clone, Copy,)]
 pub struct CharNode<R: TreeRoot<RaTypes> = OwnedRoot> {
-    syntax: SyntaxNode<R>,
+    pub(crate) syntax: SyntaxNode<R>,
 }
 pub type Char<'a> = CharNode<RefRoot<'a>>;
 
+impl<R1: TreeRoot<RaTypes>, R2: TreeRoot<RaTypes>> PartialEq<CharNode<R1>> for CharNode<R2> {
+    fn eq(&self, other: &CharNode<R1>) -> bool { self.syntax == other.syntax }
+}
+impl<R: TreeRoot<RaTypes>> Eq for CharNode<R> {}
+impl<R: TreeRoot<RaTypes>> Hash for CharNode<R> {
+    fn hash<H: Hasher>(&self, state: &mut H) { self.syntax.hash(state) }
+}
+
 impl<'a> AstNode<'a> for Char<'a> {
     fn cast(syntax: SyntaxNodeRef<'a>) -> Option<Self> {
         match syntax.kind() {
@@ -391,12 +563,20 @@ pub fn owned(&self) -> CharNode {
 impl<'a> Char<'a> {}
 
 // Comment
-#[derive(Debug, Clone, Copy)]
+#[derive(Debug, Clone, Copy,)]
 pub struct CommentNode<R: TreeRoot<RaTypes> = OwnedRoot> {
-    syntax: SyntaxNode<R>,
+    pub(crate) syntax: SyntaxNode<R>,
 }
 pub type Comment<'a> = CommentNode<RefRoot<'a>>;
 
+impl<R1: TreeRoot<RaTypes>, R2: TreeRoot<RaTypes>> PartialEq<CommentNode<R1>> for CommentNode<R2> {
+    fn eq(&self, other: &CommentNode<R1>) -> bool { self.syntax == other.syntax }
+}
+impl<R: TreeRoot<RaTypes>> Eq for CommentNode<R> {}
+impl<R: TreeRoot<RaTypes>> Hash for CommentNode<R> {
+    fn hash<H: Hasher>(&self, state: &mut H) { self.syntax.hash(state) }
+}
+
 impl<'a> AstNode<'a> for Comment<'a> {
     fn cast(syntax: SyntaxNodeRef<'a>) -> Option<Self> {
         match syntax.kind() {
@@ -420,12 +600,20 @@ pub fn owned(&self) -> CommentNode {
 impl<'a> Comment<'a> {}
 
 // Condition
-#[derive(Debug, Clone, Copy)]
+#[derive(Debug, Clone, Copy,)]
 pub struct ConditionNode<R: TreeRoot<RaTypes> = OwnedRoot> {
-    syntax: SyntaxNode<R>,
+    pub(crate) syntax: SyntaxNode<R>,
 }
 pub type Condition<'a> = ConditionNode<RefRoot<'a>>;
 
+impl<R1: TreeRoot<RaTypes>, R2: TreeRoot<RaTypes>> PartialEq<ConditionNode<R1>> for ConditionNode<R2> {
+    fn eq(&self, other: &ConditionNode<R1>) -> bool { self.syntax == other.syntax }
+}
+impl<R: TreeRoot<RaTypes>> Eq for ConditionNode<R> {}
+impl<R: TreeRoot<RaTypes>> Hash for ConditionNode<R> {
+    fn hash<H: Hasher>(&self, state: &mut H) { self.syntax.hash(state) }
+}
+
 impl<'a> AstNode<'a> for Condition<'a> {
     fn cast(syntax: SyntaxNodeRef<'a>) -> Option<Self> {
         match syntax.kind() {
@@ -457,12 +645,20 @@ pub fn expr(self) -> Option<Expr<'a>> {
 }
 
 // ConstDef
-#[derive(Debug, Clone, Copy)]
+#[derive(Debug, Clone, Copy,)]
 pub struct ConstDefNode<R: TreeRoot<RaTypes> = OwnedRoot> {
-    syntax: SyntaxNode<R>,
+    pub(crate) syntax: SyntaxNode<R>,
 }
 pub type ConstDef<'a> = ConstDefNode<RefRoot<'a>>;
 
+impl<R1: TreeRoot<RaTypes>, R2: TreeRoot<RaTypes>> PartialEq<ConstDefNode<R1>> for ConstDefNode<R2> {
+    fn eq(&self, other: &ConstDefNode<R1>) -> bool { self.syntax == other.syntax }
+}
+impl<R: TreeRoot<RaTypes>> Eq for ConstDefNode<R> {}
+impl<R: TreeRoot<RaTypes>> Hash for ConstDefNode<R> {
+    fn hash<H: Hasher>(&self, state: &mut H) { self.syntax.hash(state) }
+}
+
 impl<'a> AstNode<'a> for ConstDef<'a> {
     fn cast(syntax: SyntaxNodeRef<'a>) -> Option<Self> {
         match syntax.kind() {
@@ -486,15 +682,24 @@ pub fn owned(&self) -> ConstDefNode {
 impl<'a> ast::NameOwner<'a> for ConstDef<'a> {}
 impl<'a> ast::TypeParamsOwner<'a> for ConstDef<'a> {}
 impl<'a> ast::AttrsOwner<'a> for ConstDef<'a> {}
+impl<'a> ast::DocCommentsOwner<'a> for ConstDef<'a> {}
 impl<'a> ConstDef<'a> {}
 
 // ContinueExpr
-#[derive(Debug, Clone, Copy)]
+#[derive(Debug, Clone, Copy,)]
 pub struct ContinueExprNode<R: TreeRoot<RaTypes> = OwnedRoot> {
-    syntax: SyntaxNode<R>,
+    pub(crate) syntax: SyntaxNode<R>,
 }
 pub type ContinueExpr<'a> = ContinueExprNode<RefRoot<'a>>;
 
+impl<R1: TreeRoot<RaTypes>, R2: TreeRoot<RaTypes>> PartialEq<ContinueExprNode<R1>> for ContinueExprNode<R2> {
+    fn eq(&self, other: &ContinueExprNode<R1>) -> bool { self.syntax == other.syntax }
+}
+impl<R: TreeRoot<RaTypes>> Eq for ContinueExprNode<R> {}
+impl<R: TreeRoot<RaTypes>> Hash for ContinueExprNode<R> {
+    fn hash<H: Hasher>(&self, state: &mut H) { self.syntax.hash(state) }
+}
+
 impl<'a> AstNode<'a> for ContinueExpr<'a> {
     fn cast(syntax: SyntaxNodeRef<'a>) -> Option<Self> {
         match syntax.kind() {
@@ -518,12 +723,20 @@ pub fn owned(&self) -> ContinueExprNode {
 impl<'a> ContinueExpr<'a> {}
 
 // DynTraitType
-#[derive(Debug, Clone, Copy)]
+#[derive(Debug, Clone, Copy,)]
 pub struct DynTraitTypeNode<R: TreeRoot<RaTypes> = OwnedRoot> {
-    syntax: SyntaxNode<R>,
+    pub(crate) syntax: SyntaxNode<R>,
 }
 pub type DynTraitType<'a> = DynTraitTypeNode<RefRoot<'a>>;
 
+impl<R1: TreeRoot<RaTypes>, R2: TreeRoot<RaTypes>> PartialEq<DynTraitTypeNode<R1>> for DynTraitTypeNode<R2> {
+    fn eq(&self, other: &DynTraitTypeNode<R1>) -> bool { self.syntax == other.syntax }
+}
+impl<R: TreeRoot<RaTypes>> Eq for DynTraitTypeNode<R> {}
+impl<R: TreeRoot<RaTypes>> Hash for DynTraitTypeNode<R> {
+    fn hash<H: Hasher>(&self, state: &mut H) { self.syntax.hash(state) }
+}
+
 impl<'a> AstNode<'a> for DynTraitType<'a> {
     fn cast(syntax: SyntaxNodeRef<'a>) -> Option<Self> {
         match syntax.kind() {
@@ -547,12 +760,20 @@ pub fn owned(&self) -> DynTraitTypeNode {
 impl<'a> DynTraitType<'a> {}
 
 // EnumDef
-#[derive(Debug, Clone, Copy)]
+#[derive(Debug, Clone, Copy,)]
 pub struct EnumDefNode<R: TreeRoot<RaTypes> = OwnedRoot> {
-    syntax: SyntaxNode<R>,
+    pub(crate) syntax: SyntaxNode<R>,
 }
 pub type EnumDef<'a> = EnumDefNode<RefRoot<'a>>;
 
+impl<R1: TreeRoot<RaTypes>, R2: TreeRoot<RaTypes>> PartialEq<EnumDefNode<R1>> for EnumDefNode<R2> {
+    fn eq(&self, other: &EnumDefNode<R1>) -> bool { self.syntax == other.syntax }
+}
+impl<R: TreeRoot<RaTypes>> Eq for EnumDefNode<R> {}
+impl<R: TreeRoot<RaTypes>> Hash for EnumDefNode<R> {
+    fn hash<H: Hasher>(&self, state: &mut H) { self.syntax.hash(state) }
+}
+
 impl<'a> AstNode<'a> for EnumDef<'a> {
     fn cast(syntax: SyntaxNodeRef<'a>) -> Option<Self> {
         match syntax.kind() {
@@ -576,10 +797,11 @@ pub fn owned(&self) -> EnumDefNode {
 impl<'a> ast::NameOwner<'a> for EnumDef<'a> {}
 impl<'a> ast::TypeParamsOwner<'a> for EnumDef<'a> {}
 impl<'a> ast::AttrsOwner<'a> for EnumDef<'a> {}
+impl<'a> ast::DocCommentsOwner<'a> for EnumDef<'a> {}
 impl<'a> EnumDef<'a> {}
 
 // Expr
-#[derive(Debug, Clone, Copy)]
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
 pub enum Expr<'a> {
     TupleExpr(TupleExpr<'a>),
     ArrayExpr(ArrayExpr<'a>),
@@ -694,12 +916,20 @@ fn syntax(self) -> SyntaxNodeRef<'a> {
 impl<'a> Expr<'a> {}
 
 // ExprStmt
-#[derive(Debug, Clone, Copy)]
+#[derive(Debug, Clone, Copy,)]
 pub struct ExprStmtNode<R: TreeRoot<RaTypes> = OwnedRoot> {
-    syntax: SyntaxNode<R>,
+    pub(crate) syntax: SyntaxNode<R>,
 }
 pub type ExprStmt<'a> = ExprStmtNode<RefRoot<'a>>;
 
+impl<R1: TreeRoot<RaTypes>, R2: TreeRoot<RaTypes>> PartialEq<ExprStmtNode<R1>> for ExprStmtNode<R2> {
+    fn eq(&self, other: &ExprStmtNode<R1>) -> bool { self.syntax == other.syntax }
+}
+impl<R: TreeRoot<RaTypes>> Eq for ExprStmtNode<R> {}
+impl<R: TreeRoot<RaTypes>> Hash for ExprStmtNode<R> {
+    fn hash<H: Hasher>(&self, state: &mut H) { self.syntax.hash(state) }
+}
+
 impl<'a> AstNode<'a> for ExprStmt<'a> {
     fn cast(syntax: SyntaxNodeRef<'a>) -> Option<Self> {
         match syntax.kind() {
@@ -727,12 +957,20 @@ pub fn expr(self) -> Option<Expr<'a>> {
 }
 
 // ExternCrateItem
-#[derive(Debug, Clone, Copy)]
+#[derive(Debug, Clone, Copy,)]
 pub struct ExternCrateItemNode<R: TreeRoot<RaTypes> = OwnedRoot> {
-    syntax: SyntaxNode<R>,
+    pub(crate) syntax: SyntaxNode<R>,
 }
 pub type ExternCrateItem<'a> = ExternCrateItemNode<RefRoot<'a>>;
 
+impl<R1: TreeRoot<RaTypes>, R2: TreeRoot<RaTypes>> PartialEq<ExternCrateItemNode<R1>> for ExternCrateItemNode<R2> {
+    fn eq(&self, other: &ExternCrateItemNode<R1>) -> bool { self.syntax == other.syntax }
+}
+impl<R: TreeRoot<RaTypes>> Eq for ExternCrateItemNode<R> {}
+impl<R: TreeRoot<RaTypes>> Hash for ExternCrateItemNode<R> {
+    fn hash<H: Hasher>(&self, state: &mut H) { self.syntax.hash(state) }
+}
+
 impl<'a> AstNode<'a> for ExternCrateItem<'a> {
     fn cast(syntax: SyntaxNodeRef<'a>) -> Option<Self> {
         match syntax.kind() {
@@ -756,12 +994,20 @@ pub fn owned(&self) -> ExternCrateItemNode {
 impl<'a> ExternCrateItem<'a> {}
 
 // FieldExpr
-#[derive(Debug, Clone, Copy)]
+#[derive(Debug, Clone, Copy,)]
 pub struct FieldExprNode<R: TreeRoot<RaTypes> = OwnedRoot> {
-    syntax: SyntaxNode<R>,
+    pub(crate) syntax: SyntaxNode<R>,
 }
 pub type FieldExpr<'a> = FieldExprNode<RefRoot<'a>>;
 
+impl<R1: TreeRoot<RaTypes>, R2: TreeRoot<RaTypes>> PartialEq<FieldExprNode<R1>> for FieldExprNode<R2> {
+    fn eq(&self, other: &FieldExprNode<R1>) -> bool { self.syntax == other.syntax }
+}
+impl<R: TreeRoot<RaTypes>> Eq for FieldExprNode<R> {}
+impl<R: TreeRoot<RaTypes>> Hash for FieldExprNode<R> {
+    fn hash<H: Hasher>(&self, state: &mut H) { self.syntax.hash(state) }
+}
+
 impl<'a> AstNode<'a> for FieldExpr<'a> {
     fn cast(syntax: SyntaxNodeRef<'a>) -> Option<Self> {
         match syntax.kind() {
@@ -785,12 +1031,20 @@ pub fn owned(&self) -> FieldExprNode {
 impl<'a> FieldExpr<'a> {}
 
 // FieldPatList
-#[derive(Debug, Clone, Copy)]
+#[derive(Debug, Clone, Copy,)]
 pub struct FieldPatListNode<R: TreeRoot<RaTypes> = OwnedRoot> {
-    syntax: SyntaxNode<R>,
+    pub(crate) syntax: SyntaxNode<R>,
 }
 pub type FieldPatList<'a> = FieldPatListNode<RefRoot<'a>>;
 
+impl<R1: TreeRoot<RaTypes>, R2: TreeRoot<RaTypes>> PartialEq<FieldPatListNode<R1>> for FieldPatListNode<R2> {
+    fn eq(&self, other: &FieldPatListNode<R1>) -> bool { self.syntax == other.syntax }
+}
+impl<R: TreeRoot<RaTypes>> Eq for FieldPatListNode<R> {}
+impl<R: TreeRoot<RaTypes>> Hash for FieldPatListNode<R> {
+    fn hash<H: Hasher>(&self, state: &mut H) { self.syntax.hash(state) }
+}
+
 impl<'a> AstNode<'a> for FieldPatList<'a> {
     fn cast(syntax: SyntaxNodeRef<'a>) -> Option<Self> {
         match syntax.kind() {
@@ -814,12 +1068,20 @@ pub fn owned(&self) -> FieldPatListNode {
 impl<'a> FieldPatList<'a> {}
 
 // FnDef
-#[derive(Debug, Clone, Copy)]
+#[derive(Debug, Clone, Copy,)]
 pub struct FnDefNode<R: TreeRoot<RaTypes> = OwnedRoot> {
-    syntax: SyntaxNode<R>,
+    pub(crate) syntax: SyntaxNode<R>,
 }
 pub type FnDef<'a> = FnDefNode<RefRoot<'a>>;
 
+impl<R1: TreeRoot<RaTypes>, R2: TreeRoot<RaTypes>> PartialEq<FnDefNode<R1>> for FnDefNode<R2> {
+    fn eq(&self, other: &FnDefNode<R1>) -> bool { self.syntax == other.syntax }
+}
+impl<R: TreeRoot<RaTypes>> Eq for FnDefNode<R> {}
+impl<R: TreeRoot<RaTypes>> Hash for FnDefNode<R> {
+    fn hash<H: Hasher>(&self, state: &mut H) { self.syntax.hash(state) }
+}
+
 impl<'a> AstNode<'a> for FnDef<'a> {
     fn cast(syntax: SyntaxNodeRef<'a>) -> Option<Self> {
         match syntax.kind() {
@@ -859,12 +1121,20 @@ pub fn ret_type(self) -> Option<RetType<'a>> {
 }
 
 // FnPointerType
-#[derive(Debug, Clone, Copy)]
+#[derive(Debug, Clone, Copy,)]
 pub struct FnPointerTypeNode<R: TreeRoot<RaTypes> = OwnedRoot> {
-    syntax: SyntaxNode<R>,
+    pub(crate) syntax: SyntaxNode<R>,
 }
 pub type FnPointerType<'a> = FnPointerTypeNode<RefRoot<'a>>;
 
+impl<R1: TreeRoot<RaTypes>, R2: TreeRoot<RaTypes>> PartialEq<FnPointerTypeNode<R1>> for FnPointerTypeNode<R2> {
+    fn eq(&self, other: &FnPointerTypeNode<R1>) -> bool { self.syntax == other.syntax }
+}
+impl<R: TreeRoot<RaTypes>> Eq for FnPointerTypeNode<R> {}
+impl<R: TreeRoot<RaTypes>> Hash for FnPointerTypeNode<R> {
+    fn hash<H: Hasher>(&self, state: &mut H) { self.syntax.hash(state) }
+}
+
 impl<'a> AstNode<'a> for FnPointerType<'a> {
     fn cast(syntax: SyntaxNodeRef<'a>) -> Option<Self> {
         match syntax.kind() {
@@ -888,12 +1158,20 @@ pub fn owned(&self) -> FnPointerTypeNode {
 impl<'a> FnPointerType<'a> {}
 
 // ForExpr
-#[derive(Debug, Clone, Copy)]
+#[derive(Debug, Clone, Copy,)]
 pub struct ForExprNode<R: TreeRoot<RaTypes> = OwnedRoot> {
-    syntax: SyntaxNode<R>,
+    pub(crate) syntax: SyntaxNode<R>,
 }
 pub type ForExpr<'a> = ForExprNode<RefRoot<'a>>;
 
+impl<R1: TreeRoot<RaTypes>, R2: TreeRoot<RaTypes>> PartialEq<ForExprNode<R1>> for ForExprNode<R2> {
+    fn eq(&self, other: &ForExprNode<R1>) -> bool { self.syntax == other.syntax }
+}
+impl<R: TreeRoot<RaTypes>> Eq for ForExprNode<R> {}
+impl<R: TreeRoot<RaTypes>> Hash for ForExprNode<R> {
+    fn hash<H: Hasher>(&self, state: &mut H) { self.syntax.hash(state) }
+}
+
 impl<'a> AstNode<'a> for ForExpr<'a> {
     fn cast(syntax: SyntaxNodeRef<'a>) -> Option<Self> {
         match syntax.kind() {
@@ -926,12 +1204,20 @@ pub fn iterable(self) -> Option<Expr<'a>> {
 }
 
 // ForType
-#[derive(Debug, Clone, Copy)]
+#[derive(Debug, Clone, Copy,)]
 pub struct ForTypeNode<R: TreeRoot<RaTypes> = OwnedRoot> {
-    syntax: SyntaxNode<R>,
+    pub(crate) syntax: SyntaxNode<R>,
 }
 pub type ForType<'a> = ForTypeNode<RefRoot<'a>>;
 
+impl<R1: TreeRoot<RaTypes>, R2: TreeRoot<RaTypes>> PartialEq<ForTypeNode<R1>> for ForTypeNode<R2> {
+    fn eq(&self, other: &ForTypeNode<R1>) -> bool { self.syntax == other.syntax }
+}
+impl<R: TreeRoot<RaTypes>> Eq for ForTypeNode<R> {}
+impl<R: TreeRoot<RaTypes>> Hash for ForTypeNode<R> {
+    fn hash<H: Hasher>(&self, state: &mut H) { self.syntax.hash(state) }
+}
+
 impl<'a> AstNode<'a> for ForType<'a> {
     fn cast(syntax: SyntaxNodeRef<'a>) -> Option<Self> {
         match syntax.kind() {
@@ -955,12 +1241,20 @@ pub fn owned(&self) -> ForTypeNode {
 impl<'a> ForType<'a> {}
 
 // IfExpr
-#[derive(Debug, Clone, Copy)]
+#[derive(Debug, Clone, Copy,)]
 pub struct IfExprNode<R: TreeRoot<RaTypes> = OwnedRoot> {
-    syntax: SyntaxNode<R>,
+    pub(crate) syntax: SyntaxNode<R>,
 }
 pub type IfExpr<'a> = IfExprNode<RefRoot<'a>>;
 
+impl<R1: TreeRoot<RaTypes>, R2: TreeRoot<RaTypes>> PartialEq<IfExprNode<R1>> for IfExprNode<R2> {
+    fn eq(&self, other: &IfExprNode<R1>) -> bool { self.syntax == other.syntax }
+}
+impl<R: TreeRoot<RaTypes>> Eq for IfExprNode<R> {}
+impl<R: TreeRoot<RaTypes>> Hash for IfExprNode<R> {
+    fn hash<H: Hasher>(&self, state: &mut H) { self.syntax.hash(state) }
+}
+
 impl<'a> AstNode<'a> for IfExpr<'a> {
     fn cast(syntax: SyntaxNodeRef<'a>) -> Option<Self> {
         match syntax.kind() {
@@ -988,12 +1282,20 @@ pub fn condition(self) -> Option<Condition<'a>> {
 }
 
 // ImplItem
-#[derive(Debug, Clone, Copy)]
+#[derive(Debug, Clone, Copy,)]
 pub struct ImplItemNode<R: TreeRoot<RaTypes> = OwnedRoot> {
-    syntax: SyntaxNode<R>,
+    pub(crate) syntax: SyntaxNode<R>,
 }
 pub type ImplItem<'a> = ImplItemNode<RefRoot<'a>>;
 
+impl<R1: TreeRoot<RaTypes>, R2: TreeRoot<RaTypes>> PartialEq<ImplItemNode<R1>> for ImplItemNode<R2> {
+    fn eq(&self, other: &ImplItemNode<R1>) -> bool { self.syntax == other.syntax }
+}
+impl<R: TreeRoot<RaTypes>> Eq for ImplItemNode<R> {}
+impl<R: TreeRoot<RaTypes>> Hash for ImplItemNode<R> {
+    fn hash<H: Hasher>(&self, state: &mut H) { self.syntax.hash(state) }
+}
+
 impl<'a> AstNode<'a> for ImplItem<'a> {
     fn cast(syntax: SyntaxNodeRef<'a>) -> Option<Self> {
         match syntax.kind() {
@@ -1017,12 +1319,20 @@ pub fn owned(&self) -> ImplItemNode {
 impl<'a> ImplItem<'a> {}
 
 // ImplTraitType
-#[derive(Debug, Clone, Copy)]
+#[derive(Debug, Clone, Copy,)]
 pub struct ImplTraitTypeNode<R: TreeRoot<RaTypes> = OwnedRoot> {
-    syntax: SyntaxNode<R>,
+    pub(crate) syntax: SyntaxNode<R>,
 }
 pub type ImplTraitType<'a> = ImplTraitTypeNode<RefRoot<'a>>;
 
+impl<R1: TreeRoot<RaTypes>, R2: TreeRoot<RaTypes>> PartialEq<ImplTraitTypeNode<R1>> for ImplTraitTypeNode<R2> {
+    fn eq(&self, other: &ImplTraitTypeNode<R1>) -> bool { self.syntax == other.syntax }
+}
+impl<R: TreeRoot<RaTypes>> Eq for ImplTraitTypeNode<R> {}
+impl<R: TreeRoot<RaTypes>> Hash for ImplTraitTypeNode<R> {
+    fn hash<H: Hasher>(&self, state: &mut H) { self.syntax.hash(state) }
+}
+
 impl<'a> AstNode<'a> for ImplTraitType<'a> {
     fn cast(syntax: SyntaxNodeRef<'a>) -> Option<Self> {
         match syntax.kind() {
@@ -1046,12 +1356,20 @@ pub fn owned(&self) -> ImplTraitTypeNode {
 impl<'a> ImplTraitType<'a> {}
 
 // IndexExpr
-#[derive(Debug, Clone, Copy)]
+#[derive(Debug, Clone, Copy,)]
 pub struct IndexExprNode<R: TreeRoot<RaTypes> = OwnedRoot> {
-    syntax: SyntaxNode<R>,
+    pub(crate) syntax: SyntaxNode<R>,
 }
 pub type IndexExpr<'a> = IndexExprNode<RefRoot<'a>>;
 
+impl<R1: TreeRoot<RaTypes>, R2: TreeRoot<RaTypes>> PartialEq<IndexExprNode<R1>> for IndexExprNode<R2> {
+    fn eq(&self, other: &IndexExprNode<R1>) -> bool { self.syntax == other.syntax }
+}
+impl<R: TreeRoot<RaTypes>> Eq for IndexExprNode<R> {}
+impl<R: TreeRoot<RaTypes>> Hash for IndexExprNode<R> {
+    fn hash<H: Hasher>(&self, state: &mut H) { self.syntax.hash(state) }
+}
+
 impl<'a> AstNode<'a> for IndexExpr<'a> {
     fn cast(syntax: SyntaxNodeRef<'a>) -> Option<Self> {
         match syntax.kind() {
@@ -1075,12 +1393,20 @@ pub fn owned(&self) -> IndexExprNode {
 impl<'a> IndexExpr<'a> {}
 
 // ItemList
-#[derive(Debug, Clone, Copy)]
+#[derive(Debug, Clone, Copy,)]
 pub struct ItemListNode<R: TreeRoot<RaTypes> = OwnedRoot> {
-    syntax: SyntaxNode<R>,
+    pub(crate) syntax: SyntaxNode<R>,
 }
 pub type ItemList<'a> = ItemListNode<RefRoot<'a>>;
 
+impl<R1: TreeRoot<RaTypes>, R2: TreeRoot<RaTypes>> PartialEq<ItemListNode<R1>> for ItemListNode<R2> {
+    fn eq(&self, other: &ItemListNode<R1>) -> bool { self.syntax == other.syntax }
+}
+impl<R: TreeRoot<RaTypes>> Eq for ItemListNode<R> {}
+impl<R: TreeRoot<RaTypes>> Hash for ItemListNode<R> {
+    fn hash<H: Hasher>(&self, state: &mut H) { self.syntax.hash(state) }
+}
+
 impl<'a> AstNode<'a> for ItemList<'a> {
     fn cast(syntax: SyntaxNodeRef<'a>) -> Option<Self> {
         match syntax.kind() {
@@ -1106,12 +1432,20 @@ impl<'a> ast::ModuleItemOwner<'a> for ItemList<'a> {}
 impl<'a> ItemList<'a> {}
 
 // Label
-#[derive(Debug, Clone, Copy)]
+#[derive(Debug, Clone, Copy,)]
 pub struct LabelNode<R: TreeRoot<RaTypes> = OwnedRoot> {
-    syntax: SyntaxNode<R>,
+    pub(crate) syntax: SyntaxNode<R>,
 }
 pub type Label<'a> = LabelNode<RefRoot<'a>>;
 
+impl<R1: TreeRoot<RaTypes>, R2: TreeRoot<RaTypes>> PartialEq<LabelNode<R1>> for LabelNode<R2> {
+    fn eq(&self, other: &LabelNode<R1>) -> bool { self.syntax == other.syntax }
+}
+impl<R: TreeRoot<RaTypes>> Eq for LabelNode<R> {}
+impl<R: TreeRoot<RaTypes>> Hash for LabelNode<R> {
+    fn hash<H: Hasher>(&self, state: &mut H) { self.syntax.hash(state) }
+}
+
 impl<'a> AstNode<'a> for Label<'a> {
     fn cast(syntax: SyntaxNodeRef<'a>) -> Option<Self> {
         match syntax.kind() {
@@ -1135,12 +1469,20 @@ pub fn owned(&self) -> LabelNode {
 impl<'a> Label<'a> {}
 
 // LambdaExpr
-#[derive(Debug, Clone, Copy)]
+#[derive(Debug, Clone, Copy,)]
 pub struct LambdaExprNode<R: TreeRoot<RaTypes> = OwnedRoot> {
-    syntax: SyntaxNode<R>,
+    pub(crate) syntax: SyntaxNode<R>,
 }
 pub type LambdaExpr<'a> = LambdaExprNode<RefRoot<'a>>;
 
+impl<R1: TreeRoot<RaTypes>, R2: TreeRoot<RaTypes>> PartialEq<LambdaExprNode<R1>> for LambdaExprNode<R2> {
+    fn eq(&self, other: &LambdaExprNode<R1>) -> bool { self.syntax == other.syntax }
+}
+impl<R: TreeRoot<RaTypes>> Eq for LambdaExprNode<R> {}
+impl<R: TreeRoot<RaTypes>> Hash for LambdaExprNode<R> {
+    fn hash<H: Hasher>(&self, state: &mut H) { self.syntax.hash(state) }
+}
+
 impl<'a> AstNode<'a> for LambdaExpr<'a> {
     fn cast(syntax: SyntaxNodeRef<'a>) -> Option<Self> {
         match syntax.kind() {
@@ -1172,12 +1514,20 @@ pub fn body(self) -> Option<Expr<'a>> {
 }
 
 // LetStmt
-#[derive(Debug, Clone, Copy)]
+#[derive(Debug, Clone, Copy,)]
 pub struct LetStmtNode<R: TreeRoot<RaTypes> = OwnedRoot> {
-    syntax: SyntaxNode<R>,
+    pub(crate) syntax: SyntaxNode<R>,
 }
 pub type LetStmt<'a> = LetStmtNode<RefRoot<'a>>;
 
+impl<R1: TreeRoot<RaTypes>, R2: TreeRoot<RaTypes>> PartialEq<LetStmtNode<R1>> for LetStmtNode<R2> {
+    fn eq(&self, other: &LetStmtNode<R1>) -> bool { self.syntax == other.syntax }
+}
+impl<R: TreeRoot<RaTypes>> Eq for LetStmtNode<R> {}
+impl<R: TreeRoot<RaTypes>> Hash for LetStmtNode<R> {
+    fn hash<H: Hasher>(&self, state: &mut H) { self.syntax.hash(state) }
+}
+
 impl<'a> AstNode<'a> for LetStmt<'a> {
     fn cast(syntax: SyntaxNodeRef<'a>) -> Option<Self> {
         match syntax.kind() {
@@ -1209,12 +1559,20 @@ pub fn initializer(self) -> Option<Expr<'a>> {
 }
 
 // Lifetime
-#[derive(Debug, Clone, Copy)]
+#[derive(Debug, Clone, Copy,)]
 pub struct LifetimeNode<R: TreeRoot<RaTypes> = OwnedRoot> {
-    syntax: SyntaxNode<R>,
+    pub(crate) syntax: SyntaxNode<R>,
 }
 pub type Lifetime<'a> = LifetimeNode<RefRoot<'a>>;
 
+impl<R1: TreeRoot<RaTypes>, R2: TreeRoot<RaTypes>> PartialEq<LifetimeNode<R1>> for LifetimeNode<R2> {
+    fn eq(&self, other: &LifetimeNode<R1>) -> bool { self.syntax == other.syntax }
+}
+impl<R: TreeRoot<RaTypes>> Eq for LifetimeNode<R> {}
+impl<R: TreeRoot<RaTypes>> Hash for LifetimeNode<R> {
+    fn hash<H: Hasher>(&self, state: &mut H) { self.syntax.hash(state) }
+}
+
 impl<'a> AstNode<'a> for Lifetime<'a> {
     fn cast(syntax: SyntaxNodeRef<'a>) -> Option<Self> {
         match syntax.kind() {
@@ -1238,12 +1596,20 @@ pub fn owned(&self) -> LifetimeNode {
 impl<'a> Lifetime<'a> {}
 
 // LifetimeParam
-#[derive(Debug, Clone, Copy)]
+#[derive(Debug, Clone, Copy,)]
 pub struct LifetimeParamNode<R: TreeRoot<RaTypes> = OwnedRoot> {
-    syntax: SyntaxNode<R>,
+    pub(crate) syntax: SyntaxNode<R>,
 }
 pub type LifetimeParam<'a> = LifetimeParamNode<RefRoot<'a>>;
 
+impl<R1: TreeRoot<RaTypes>, R2: TreeRoot<RaTypes>> PartialEq<LifetimeParamNode<R1>> for LifetimeParamNode<R2> {
+    fn eq(&self, other: &LifetimeParamNode<R1>) -> bool { self.syntax == other.syntax }
+}
+impl<R: TreeRoot<RaTypes>> Eq for LifetimeParamNode<R> {}
+impl<R: TreeRoot<RaTypes>> Hash for LifetimeParamNode<R> {
+    fn hash<H: Hasher>(&self, state: &mut H) { self.syntax.hash(state) }
+}
+
 impl<'a> AstNode<'a> for LifetimeParam<'a> {
     fn cast(syntax: SyntaxNodeRef<'a>) -> Option<Self> {
         match syntax.kind() {
@@ -1271,12 +1637,20 @@ pub fn lifetime(self) -> Option<Lifetime<'a>> {
 }
 
 // Literal
-#[derive(Debug, Clone, Copy)]
+#[derive(Debug, Clone, Copy,)]
 pub struct LiteralNode<R: TreeRoot<RaTypes> = OwnedRoot> {
-    syntax: SyntaxNode<R>,
+    pub(crate) syntax: SyntaxNode<R>,
 }
 pub type Literal<'a> = LiteralNode<RefRoot<'a>>;
 
+impl<R1: TreeRoot<RaTypes>, R2: TreeRoot<RaTypes>> PartialEq<LiteralNode<R1>> for LiteralNode<R2> {
+    fn eq(&self, other: &LiteralNode<R1>) -> bool { self.syntax == other.syntax }
+}
+impl<R: TreeRoot<RaTypes>> Eq for LiteralNode<R> {}
+impl<R: TreeRoot<RaTypes>> Hash for LiteralNode<R> {
+    fn hash<H: Hasher>(&self, state: &mut H) { self.syntax.hash(state) }
+}
+
 impl<'a> AstNode<'a> for Literal<'a> {
     fn cast(syntax: SyntaxNodeRef<'a>) -> Option<Self> {
         match syntax.kind() {
@@ -1300,12 +1674,20 @@ pub fn owned(&self) -> LiteralNode {
 impl<'a> Literal<'a> {}
 
 // LoopExpr
-#[derive(Debug, Clone, Copy)]
+#[derive(Debug, Clone, Copy,)]
 pub struct LoopExprNode<R: TreeRoot<RaTypes> = OwnedRoot> {
-    syntax: SyntaxNode<R>,
+    pub(crate) syntax: SyntaxNode<R>,
 }
 pub type LoopExpr<'a> = LoopExprNode<RefRoot<'a>>;
 
+impl<R1: TreeRoot<RaTypes>, R2: TreeRoot<RaTypes>> PartialEq<LoopExprNode<R1>> for LoopExprNode<R2> {
+    fn eq(&self, other: &LoopExprNode<R1>) -> bool { self.syntax == other.syntax }
+}
+impl<R: TreeRoot<RaTypes>> Eq for LoopExprNode<R> {}
+impl<R: TreeRoot<RaTypes>> Hash for LoopExprNode<R> {
+    fn hash<H: Hasher>(&self, state: &mut H) { self.syntax.hash(state) }
+}
+
 impl<'a> AstNode<'a> for LoopExpr<'a> {
     fn cast(syntax: SyntaxNodeRef<'a>) -> Option<Self> {
         match syntax.kind() {
@@ -1330,12 +1712,20 @@ impl<'a> ast::LoopBodyOwner<'a> for LoopExpr<'a> {}
 impl<'a> LoopExpr<'a> {}
 
 // MatchArm
-#[derive(Debug, Clone, Copy)]
+#[derive(Debug, Clone, Copy,)]
 pub struct MatchArmNode<R: TreeRoot<RaTypes> = OwnedRoot> {
-    syntax: SyntaxNode<R>,
+    pub(crate) syntax: SyntaxNode<R>,
 }
 pub type MatchArm<'a> = MatchArmNode<RefRoot<'a>>;
 
+impl<R1: TreeRoot<RaTypes>, R2: TreeRoot<RaTypes>> PartialEq<MatchArmNode<R1>> for MatchArmNode<R2> {
+    fn eq(&self, other: &MatchArmNode<R1>) -> bool { self.syntax == other.syntax }
+}
+impl<R: TreeRoot<RaTypes>> Eq for MatchArmNode<R> {}
+impl<R: TreeRoot<RaTypes>> Hash for MatchArmNode<R> {
+    fn hash<H: Hasher>(&self, state: &mut H) { self.syntax.hash(state) }
+}
+
 impl<'a> AstNode<'a> for MatchArm<'a> {
     fn cast(syntax: SyntaxNodeRef<'a>) -> Option<Self> {
         match syntax.kind() {
@@ -1371,12 +1761,20 @@ pub fn expr(self) -> Option<Expr<'a>> {
 }
 
 // MatchArmList
-#[derive(Debug, Clone, Copy)]
+#[derive(Debug, Clone, Copy,)]
 pub struct MatchArmListNode<R: TreeRoot<RaTypes> = OwnedRoot> {
-    syntax: SyntaxNode<R>,
+    pub(crate) syntax: SyntaxNode<R>,
 }
 pub type MatchArmList<'a> = MatchArmListNode<RefRoot<'a>>;
 
+impl<R1: TreeRoot<RaTypes>, R2: TreeRoot<RaTypes>> PartialEq<MatchArmListNode<R1>> for MatchArmListNode<R2> {
+    fn eq(&self, other: &MatchArmListNode<R1>) -> bool { self.syntax == other.syntax }
+}
+impl<R: TreeRoot<RaTypes>> Eq for MatchArmListNode<R> {}
+impl<R: TreeRoot<RaTypes>> Hash for MatchArmListNode<R> {
+    fn hash<H: Hasher>(&self, state: &mut H) { self.syntax.hash(state) }
+}
+
 impl<'a> AstNode<'a> for MatchArmList<'a> {
     fn cast(syntax: SyntaxNodeRef<'a>) -> Option<Self> {
         match syntax.kind() {
@@ -1404,12 +1802,20 @@ pub fn arms(self) -> impl Iterator<Item = MatchArm<'a>> + 'a {
 }
 
 // MatchExpr
-#[derive(Debug, Clone, Copy)]
+#[derive(Debug, Clone, Copy,)]
 pub struct MatchExprNode<R: TreeRoot<RaTypes> = OwnedRoot> {
-    syntax: SyntaxNode<R>,
+    pub(crate) syntax: SyntaxNode<R>,
 }
 pub type MatchExpr<'a> = MatchExprNode<RefRoot<'a>>;
 
+impl<R1: TreeRoot<RaTypes>, R2: TreeRoot<RaTypes>> PartialEq<MatchExprNode<R1>> for MatchExprNode<R2> {
+    fn eq(&self, other: &MatchExprNode<R1>) -> bool { self.syntax == other.syntax }
+}
+impl<R: TreeRoot<RaTypes>> Eq for MatchExprNode<R> {}
+impl<R: TreeRoot<RaTypes>> Hash for MatchExprNode<R> {
+    fn hash<H: Hasher>(&self, state: &mut H) { self.syntax.hash(state) }
+}
+
 impl<'a> AstNode<'a> for MatchExpr<'a> {
     fn cast(syntax: SyntaxNodeRef<'a>) -> Option<Self> {
         match syntax.kind() {
@@ -1441,12 +1847,20 @@ pub fn match_arm_list(self) -> Option<MatchArmList<'a>> {
 }
 
 // MatchGuard
-#[derive(Debug, Clone, Copy)]
+#[derive(Debug, Clone, Copy,)]
 pub struct MatchGuardNode<R: TreeRoot<RaTypes> = OwnedRoot> {
-    syntax: SyntaxNode<R>,
+    pub(crate) syntax: SyntaxNode<R>,
 }
 pub type MatchGuard<'a> = MatchGuardNode<RefRoot<'a>>;
 
+impl<R1: TreeRoot<RaTypes>, R2: TreeRoot<RaTypes>> PartialEq<MatchGuardNode<R1>> for MatchGuardNode<R2> {
+    fn eq(&self, other: &MatchGuardNode<R1>) -> bool { self.syntax == other.syntax }
+}
+impl<R: TreeRoot<RaTypes>> Eq for MatchGuardNode<R> {}
+impl<R: TreeRoot<RaTypes>> Hash for MatchGuardNode<R> {
+    fn hash<H: Hasher>(&self, state: &mut H) { self.syntax.hash(state) }
+}
+
 impl<'a> AstNode<'a> for MatchGuard<'a> {
     fn cast(syntax: SyntaxNodeRef<'a>) -> Option<Self> {
         match syntax.kind() {
@@ -1470,12 +1884,20 @@ pub fn owned(&self) -> MatchGuardNode {
 impl<'a> MatchGuard<'a> {}
 
 // MethodCallExpr
-#[derive(Debug, Clone, Copy)]
+#[derive(Debug, Clone, Copy,)]
 pub struct MethodCallExprNode<R: TreeRoot<RaTypes> = OwnedRoot> {
-    syntax: SyntaxNode<R>,
+    pub(crate) syntax: SyntaxNode<R>,
 }
 pub type MethodCallExpr<'a> = MethodCallExprNode<RefRoot<'a>>;
 
+impl<R1: TreeRoot<RaTypes>, R2: TreeRoot<RaTypes>> PartialEq<MethodCallExprNode<R1>> for MethodCallExprNode<R2> {
+    fn eq(&self, other: &MethodCallExprNode<R1>) -> bool { self.syntax == other.syntax }
+}
+impl<R: TreeRoot<RaTypes>> Eq for MethodCallExprNode<R> {}
+impl<R: TreeRoot<RaTypes>> Hash for MethodCallExprNode<R> {
+    fn hash<H: Hasher>(&self, state: &mut H) { self.syntax.hash(state) }
+}
+
 impl<'a> AstNode<'a> for MethodCallExpr<'a> {
     fn cast(syntax: SyntaxNodeRef<'a>) -> Option<Self> {
         match syntax.kind() {
@@ -1504,12 +1926,20 @@ pub fn expr(self) -> Option<Expr<'a>> {
 }
 
 // Module
-#[derive(Debug, Clone, Copy)]
+#[derive(Debug, Clone, Copy,)]
 pub struct ModuleNode<R: TreeRoot<RaTypes> = OwnedRoot> {
-    syntax: SyntaxNode<R>,
+    pub(crate) syntax: SyntaxNode<R>,
 }
 pub type Module<'a> = ModuleNode<RefRoot<'a>>;
 
+impl<R1: TreeRoot<RaTypes>, R2: TreeRoot<RaTypes>> PartialEq<ModuleNode<R1>> for ModuleNode<R2> {
+    fn eq(&self, other: &ModuleNode<R1>) -> bool { self.syntax == other.syntax }
+}
+impl<R: TreeRoot<RaTypes>> Eq for ModuleNode<R> {}
+impl<R: TreeRoot<RaTypes>> Hash for ModuleNode<R> {
+    fn hash<H: Hasher>(&self, state: &mut H) { self.syntax.hash(state) }
+}
+
 impl<'a> AstNode<'a> for Module<'a> {
     fn cast(syntax: SyntaxNodeRef<'a>) -> Option<Self> {
         match syntax.kind() {
@@ -1532,6 +1962,7 @@ pub fn owned(&self) -> ModuleNode {
 
 impl<'a> ast::NameOwner<'a> for Module<'a> {}
 impl<'a> ast::AttrsOwner<'a> for Module<'a> {}
+impl<'a> ast::DocCommentsOwner<'a> for Module<'a> {}
 impl<'a> Module<'a> {
     pub fn item_list(self) -> Option<ItemList<'a>> {
         super::child_opt(self)
@@ -1539,7 +1970,7 @@ pub fn item_list(self) -> Option<ItemList<'a>> {
 }
 
 // ModuleItem
-#[derive(Debug, Clone, Copy)]
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
 pub enum ModuleItem<'a> {
     StructDef(StructDef<'a>),
     EnumDef(EnumDef<'a>),
@@ -1591,12 +2022,20 @@ fn syntax(self) -> SyntaxNodeRef<'a> {
 impl<'a> ModuleItem<'a> {}
 
 // Name
-#[derive(Debug, Clone, Copy)]
+#[derive(Debug, Clone, Copy,)]
 pub struct NameNode<R: TreeRoot<RaTypes> = OwnedRoot> {
-    syntax: SyntaxNode<R>,
+    pub(crate) syntax: SyntaxNode<R>,
 }
 pub type Name<'a> = NameNode<RefRoot<'a>>;
 
+impl<R1: TreeRoot<RaTypes>, R2: TreeRoot<RaTypes>> PartialEq<NameNode<R1>> for NameNode<R2> {
+    fn eq(&self, other: &NameNode<R1>) -> bool { self.syntax == other.syntax }
+}
+impl<R: TreeRoot<RaTypes>> Eq for NameNode<R> {}
+impl<R: TreeRoot<RaTypes>> Hash for NameNode<R> {
+    fn hash<H: Hasher>(&self, state: &mut H) { self.syntax.hash(state) }
+}
+
 impl<'a> AstNode<'a> for Name<'a> {
     fn cast(syntax: SyntaxNodeRef<'a>) -> Option<Self> {
         match syntax.kind() {
@@ -1620,12 +2059,20 @@ pub fn owned(&self) -> NameNode {
 impl<'a> Name<'a> {}
 
 // NameRef
-#[derive(Debug, Clone, Copy)]
+#[derive(Debug, Clone, Copy,)]
 pub struct NameRefNode<R: TreeRoot<RaTypes> = OwnedRoot> {
-    syntax: SyntaxNode<R>,
+    pub(crate) syntax: SyntaxNode<R>,
 }
 pub type NameRef<'a> = NameRefNode<RefRoot<'a>>;
 
+impl<R1: TreeRoot<RaTypes>, R2: TreeRoot<RaTypes>> PartialEq<NameRefNode<R1>> for NameRefNode<R2> {
+    fn eq(&self, other: &NameRefNode<R1>) -> bool { self.syntax == other.syntax }
+}
+impl<R: TreeRoot<RaTypes>> Eq for NameRefNode<R> {}
+impl<R: TreeRoot<RaTypes>> Hash for NameRefNode<R> {
+    fn hash<H: Hasher>(&self, state: &mut H) { self.syntax.hash(state) }
+}
+
 impl<'a> AstNode<'a> for NameRef<'a> {
     fn cast(syntax: SyntaxNodeRef<'a>) -> Option<Self> {
         match syntax.kind() {
@@ -1649,12 +2096,20 @@ pub fn owned(&self) -> NameRefNode {
 impl<'a> NameRef<'a> {}
 
 // NamedField
-#[derive(Debug, Clone, Copy)]
+#[derive(Debug, Clone, Copy,)]
 pub struct NamedFieldNode<R: TreeRoot<RaTypes> = OwnedRoot> {
-    syntax: SyntaxNode<R>,
+    pub(crate) syntax: SyntaxNode<R>,
 }
 pub type NamedField<'a> = NamedFieldNode<RefRoot<'a>>;
 
+impl<R1: TreeRoot<RaTypes>, R2: TreeRoot<RaTypes>> PartialEq<NamedFieldNode<R1>> for NamedFieldNode<R2> {
+    fn eq(&self, other: &NamedFieldNode<R1>) -> bool { self.syntax == other.syntax }
+}
+impl<R: TreeRoot<RaTypes>> Eq for NamedFieldNode<R> {}
+impl<R: TreeRoot<RaTypes>> Hash for NamedFieldNode<R> {
+    fn hash<H: Hasher>(&self, state: &mut H) { self.syntax.hash(state) }
+}
+
 impl<'a> AstNode<'a> for NamedField<'a> {
     fn cast(syntax: SyntaxNodeRef<'a>) -> Option<Self> {
         match syntax.kind() {
@@ -1678,12 +2133,20 @@ pub fn owned(&self) -> NamedFieldNode {
 impl<'a> NamedField<'a> {}
 
 // NamedFieldDef
-#[derive(Debug, Clone, Copy)]
+#[derive(Debug, Clone, Copy,)]
 pub struct NamedFieldDefNode<R: TreeRoot<RaTypes> = OwnedRoot> {
-    syntax: SyntaxNode<R>,
+    pub(crate) syntax: SyntaxNode<R>,
 }
 pub type NamedFieldDef<'a> = NamedFieldDefNode<RefRoot<'a>>;
 
+impl<R1: TreeRoot<RaTypes>, R2: TreeRoot<RaTypes>> PartialEq<NamedFieldDefNode<R1>> for NamedFieldDefNode<R2> {
+    fn eq(&self, other: &NamedFieldDefNode<R1>) -> bool { self.syntax == other.syntax }
+}
+impl<R: TreeRoot<RaTypes>> Eq for NamedFieldDefNode<R> {}
+impl<R: TreeRoot<RaTypes>> Hash for NamedFieldDefNode<R> {
+    fn hash<H: Hasher>(&self, state: &mut H) { self.syntax.hash(state) }
+}
+
 impl<'a> AstNode<'a> for NamedFieldDef<'a> {
     fn cast(syntax: SyntaxNodeRef<'a>) -> Option<Self> {
         match syntax.kind() {
@@ -1709,12 +2172,20 @@ impl<'a> ast::AttrsOwner<'a> for NamedFieldDef<'a> {}
 impl<'a> NamedFieldDef<'a> {}
 
 // NamedFieldList
-#[derive(Debug, Clone, Copy)]
+#[derive(Debug, Clone, Copy,)]
 pub struct NamedFieldListNode<R: TreeRoot<RaTypes> = OwnedRoot> {
-    syntax: SyntaxNode<R>,
+    pub(crate) syntax: SyntaxNode<R>,
 }
 pub type NamedFieldList<'a> = NamedFieldListNode<RefRoot<'a>>;
 
+impl<R1: TreeRoot<RaTypes>, R2: TreeRoot<RaTypes>> PartialEq<NamedFieldListNode<R1>> for NamedFieldListNode<R2> {
+    fn eq(&self, other: &NamedFieldListNode<R1>) -> bool { self.syntax == other.syntax }
+}
+impl<R: TreeRoot<RaTypes>> Eq for NamedFieldListNode<R> {}
+impl<R: TreeRoot<RaTypes>> Hash for NamedFieldListNode<R> {
+    fn hash<H: Hasher>(&self, state: &mut H) { self.syntax.hash(state) }
+}
+
 impl<'a> AstNode<'a> for NamedFieldList<'a> {
     fn cast(syntax: SyntaxNodeRef<'a>) -> Option<Self> {
         match syntax.kind() {
@@ -1738,12 +2209,20 @@ pub fn owned(&self) -> NamedFieldListNode {
 impl<'a> NamedFieldList<'a> {}
 
 // NeverType
-#[derive(Debug, Clone, Copy)]
+#[derive(Debug, Clone, Copy,)]
 pub struct NeverTypeNode<R: TreeRoot<RaTypes> = OwnedRoot> {
-    syntax: SyntaxNode<R>,
+    pub(crate) syntax: SyntaxNode<R>,
 }
 pub type NeverType<'a> = NeverTypeNode<RefRoot<'a>>;
 
+impl<R1: TreeRoot<RaTypes>, R2: TreeRoot<RaTypes>> PartialEq<NeverTypeNode<R1>> for NeverTypeNode<R2> {
+    fn eq(&self, other: &NeverTypeNode<R1>) -> bool { self.syntax == other.syntax }
+}
+impl<R: TreeRoot<RaTypes>> Eq for NeverTypeNode<R> {}
+impl<R: TreeRoot<RaTypes>> Hash for NeverTypeNode<R> {
+    fn hash<H: Hasher>(&self, state: &mut H) { self.syntax.hash(state) }
+}
+
 impl<'a> AstNode<'a> for NeverType<'a> {
     fn cast(syntax: SyntaxNodeRef<'a>) -> Option<Self> {
         match syntax.kind() {
@@ -1767,7 +2246,7 @@ pub fn owned(&self) -> NeverTypeNode {
 impl<'a> NeverType<'a> {}
 
 // NominalDef
-#[derive(Debug, Clone, Copy)]
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
 pub enum NominalDef<'a> {
     StructDef(StructDef<'a>),
     EnumDef(EnumDef<'a>),
@@ -1795,12 +2274,20 @@ impl<'a> ast::AttrsOwner<'a> for NominalDef<'a> {}
 impl<'a> NominalDef<'a> {}
 
 // Param
-#[derive(Debug, Clone, Copy)]
+#[derive(Debug, Clone, Copy,)]
 pub struct ParamNode<R: TreeRoot<RaTypes> = OwnedRoot> {
-    syntax: SyntaxNode<R>,
+    pub(crate) syntax: SyntaxNode<R>,
 }
 pub type Param<'a> = ParamNode<RefRoot<'a>>;
 
+impl<R1: TreeRoot<RaTypes>, R2: TreeRoot<RaTypes>> PartialEq<ParamNode<R1>> for ParamNode<R2> {
+    fn eq(&self, other: &ParamNode<R1>) -> bool { self.syntax == other.syntax }
+}
+impl<R: TreeRoot<RaTypes>> Eq for ParamNode<R> {}
+impl<R: TreeRoot<RaTypes>> Hash for ParamNode<R> {
+    fn hash<H: Hasher>(&self, state: &mut H) { self.syntax.hash(state) }
+}
+
 impl<'a> AstNode<'a> for Param<'a> {
     fn cast(syntax: SyntaxNodeRef<'a>) -> Option<Self> {
         match syntax.kind() {
@@ -1828,12 +2315,20 @@ pub fn pat(self) -> Option<Pat<'a>> {
 }
 
 // ParamList
-#[derive(Debug, Clone, Copy)]
+#[derive(Debug, Clone, Copy,)]
 pub struct ParamListNode<R: TreeRoot<RaTypes> = OwnedRoot> {
-    syntax: SyntaxNode<R>,
+    pub(crate) syntax: SyntaxNode<R>,
 }
 pub type ParamList<'a> = ParamListNode<RefRoot<'a>>;
 
+impl<R1: TreeRoot<RaTypes>, R2: TreeRoot<RaTypes>> PartialEq<ParamListNode<R1>> for ParamListNode<R2> {
+    fn eq(&self, other: &ParamListNode<R1>) -> bool { self.syntax == other.syntax }
+}
+impl<R: TreeRoot<RaTypes>> Eq for ParamListNode<R> {}
+impl<R: TreeRoot<RaTypes>> Hash for ParamListNode<R> {
+    fn hash<H: Hasher>(&self, state: &mut H) { self.syntax.hash(state) }
+}
+
 impl<'a> AstNode<'a> for ParamList<'a> {
     fn cast(syntax: SyntaxNodeRef<'a>) -> Option<Self> {
         match syntax.kind() {
@@ -1865,12 +2360,20 @@ pub fn self_param(self) -> Option<SelfParam<'a>> {
 }
 
 // ParenExpr
-#[derive(Debug, Clone, Copy)]
+#[derive(Debug, Clone, Copy,)]
 pub struct ParenExprNode<R: TreeRoot<RaTypes> = OwnedRoot> {
-    syntax: SyntaxNode<R>,
+    pub(crate) syntax: SyntaxNode<R>,
 }
 pub type ParenExpr<'a> = ParenExprNode<RefRoot<'a>>;
 
+impl<R1: TreeRoot<RaTypes>, R2: TreeRoot<RaTypes>> PartialEq<ParenExprNode<R1>> for ParenExprNode<R2> {
+    fn eq(&self, other: &ParenExprNode<R1>) -> bool { self.syntax == other.syntax }
+}
+impl<R: TreeRoot<RaTypes>> Eq for ParenExprNode<R> {}
+impl<R: TreeRoot<RaTypes>> Hash for ParenExprNode<R> {
+    fn hash<H: Hasher>(&self, state: &mut H) { self.syntax.hash(state) }
+}
+
 impl<'a> AstNode<'a> for ParenExpr<'a> {
     fn cast(syntax: SyntaxNodeRef<'a>) -> Option<Self> {
         match syntax.kind() {
@@ -1894,12 +2397,20 @@ pub fn owned(&self) -> ParenExprNode {
 impl<'a> ParenExpr<'a> {}
 
 // ParenType
-#[derive(Debug, Clone, Copy)]
+#[derive(Debug, Clone, Copy,)]
 pub struct ParenTypeNode<R: TreeRoot<RaTypes> = OwnedRoot> {
-    syntax: SyntaxNode<R>,
+    pub(crate) syntax: SyntaxNode<R>,
 }
 pub type ParenType<'a> = ParenTypeNode<RefRoot<'a>>;
 
+impl<R1: TreeRoot<RaTypes>, R2: TreeRoot<RaTypes>> PartialEq<ParenTypeNode<R1>> for ParenTypeNode<R2> {
+    fn eq(&self, other: &ParenTypeNode<R1>) -> bool { self.syntax == other.syntax }
+}
+impl<R: TreeRoot<RaTypes>> Eq for ParenTypeNode<R> {}
+impl<R: TreeRoot<RaTypes>> Hash for ParenTypeNode<R> {
+    fn hash<H: Hasher>(&self, state: &mut H) { self.syntax.hash(state) }
+}
+
 impl<'a> AstNode<'a> for ParenType<'a> {
     fn cast(syntax: SyntaxNodeRef<'a>) -> Option<Self> {
         match syntax.kind() {
@@ -1923,7 +2434,7 @@ pub fn owned(&self) -> ParenTypeNode {
 impl<'a> ParenType<'a> {}
 
 // Pat
-#[derive(Debug, Clone, Copy)]
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
 pub enum Pat<'a> {
     RefPat(RefPat<'a>),
     BindPat(BindPat<'a>),
@@ -1972,12 +2483,20 @@ fn syntax(self) -> SyntaxNodeRef<'a> {
 impl<'a> Pat<'a> {}
 
 // Path
-#[derive(Debug, Clone, Copy)]
+#[derive(Debug, Clone, Copy,)]
 pub struct PathNode<R: TreeRoot<RaTypes> = OwnedRoot> {
-    syntax: SyntaxNode<R>,
+    pub(crate) syntax: SyntaxNode<R>,
 }
 pub type Path<'a> = PathNode<RefRoot<'a>>;
 
+impl<R1: TreeRoot<RaTypes>, R2: TreeRoot<RaTypes>> PartialEq<PathNode<R1>> for PathNode<R2> {
+    fn eq(&self, other: &PathNode<R1>) -> bool { self.syntax == other.syntax }
+}
+impl<R: TreeRoot<RaTypes>> Eq for PathNode<R> {}
+impl<R: TreeRoot<RaTypes>> Hash for PathNode<R> {
+    fn hash<H: Hasher>(&self, state: &mut H) { self.syntax.hash(state) }
+}
+
 impl<'a> AstNode<'a> for Path<'a> {
     fn cast(syntax: SyntaxNodeRef<'a>) -> Option<Self> {
         match syntax.kind() {
@@ -2009,12 +2528,20 @@ pub fn qualifier(self) -> Option<Path<'a>> {
 }
 
 // PathExpr
-#[derive(Debug, Clone, Copy)]
+#[derive(Debug, Clone, Copy,)]
 pub struct PathExprNode<R: TreeRoot<RaTypes> = OwnedRoot> {
-    syntax: SyntaxNode<R>,
+    pub(crate) syntax: SyntaxNode<R>,
 }
 pub type PathExpr<'a> = PathExprNode<RefRoot<'a>>;
 
+impl<R1: TreeRoot<RaTypes>, R2: TreeRoot<RaTypes>> PartialEq<PathExprNode<R1>> for PathExprNode<R2> {
+    fn eq(&self, other: &PathExprNode<R1>) -> bool { self.syntax == other.syntax }
+}
+impl<R: TreeRoot<RaTypes>> Eq for PathExprNode<R> {}
+impl<R: TreeRoot<RaTypes>> Hash for PathExprNode<R> {
+    fn hash<H: Hasher>(&self, state: &mut H) { self.syntax.hash(state) }
+}
+
 impl<'a> AstNode<'a> for PathExpr<'a> {
     fn cast(syntax: SyntaxNodeRef<'a>) -> Option<Self> {
         match syntax.kind() {
@@ -2042,12 +2569,20 @@ pub fn path(self) -> Option<Path<'a>> {
 }
 
 // PathPat
-#[derive(Debug, Clone, Copy)]
+#[derive(Debug, Clone, Copy,)]
 pub struct PathPatNode<R: TreeRoot<RaTypes> = OwnedRoot> {
-    syntax: SyntaxNode<R>,
+    pub(crate) syntax: SyntaxNode<R>,
 }
 pub type PathPat<'a> = PathPatNode<RefRoot<'a>>;
 
+impl<R1: TreeRoot<RaTypes>, R2: TreeRoot<RaTypes>> PartialEq<PathPatNode<R1>> for PathPatNode<R2> {
+    fn eq(&self, other: &PathPatNode<R1>) -> bool { self.syntax == other.syntax }
+}
+impl<R: TreeRoot<RaTypes>> Eq for PathPatNode<R> {}
+impl<R: TreeRoot<RaTypes>> Hash for PathPatNode<R> {
+    fn hash<H: Hasher>(&self, state: &mut H) { self.syntax.hash(state) }
+}
+
 impl<'a> AstNode<'a> for PathPat<'a> {
     fn cast(syntax: SyntaxNodeRef<'a>) -> Option<Self> {
         match syntax.kind() {
@@ -2071,12 +2606,20 @@ pub fn owned(&self) -> PathPatNode {
 impl<'a> PathPat<'a> {}
 
 // PathSegment
-#[derive(Debug, Clone, Copy)]
+#[derive(Debug, Clone, Copy,)]
 pub struct PathSegmentNode<R: TreeRoot<RaTypes> = OwnedRoot> {
-    syntax: SyntaxNode<R>,
+    pub(crate) syntax: SyntaxNode<R>,
 }
 pub type PathSegment<'a> = PathSegmentNode<RefRoot<'a>>;
 
+impl<R1: TreeRoot<RaTypes>, R2: TreeRoot<RaTypes>> PartialEq<PathSegmentNode<R1>> for PathSegmentNode<R2> {
+    fn eq(&self, other: &PathSegmentNode<R1>) -> bool { self.syntax == other.syntax }
+}
+impl<R: TreeRoot<RaTypes>> Eq for PathSegmentNode<R> {}
+impl<R: TreeRoot<RaTypes>> Hash for PathSegmentNode<R> {
+    fn hash<H: Hasher>(&self, state: &mut H) { self.syntax.hash(state) }
+}
+
 impl<'a> AstNode<'a> for PathSegment<'a> {
     fn cast(syntax: SyntaxNodeRef<'a>) -> Option<Self> {
         match syntax.kind() {
@@ -2104,12 +2647,20 @@ pub fn name_ref(self) -> Option<NameRef<'a>> {
 }
 
 // PathType
-#[derive(Debug, Clone, Copy)]
+#[derive(Debug, Clone, Copy,)]
 pub struct PathTypeNode<R: TreeRoot<RaTypes> = OwnedRoot> {
-    syntax: SyntaxNode<R>,
+    pub(crate) syntax: SyntaxNode<R>,
 }
 pub type PathType<'a> = PathTypeNode<RefRoot<'a>>;
 
+impl<R1: TreeRoot<RaTypes>, R2: TreeRoot<RaTypes>> PartialEq<PathTypeNode<R1>> for PathTypeNode<R2> {
+    fn eq(&self, other: &PathTypeNode<R1>) -> bool { self.syntax == other.syntax }
+}
+impl<R: TreeRoot<RaTypes>> Eq for PathTypeNode<R> {}
+impl<R: TreeRoot<RaTypes>> Hash for PathTypeNode<R> {
+    fn hash<H: Hasher>(&self, state: &mut H) { self.syntax.hash(state) }
+}
+
 impl<'a> AstNode<'a> for PathType<'a> {
     fn cast(syntax: SyntaxNodeRef<'a>) -> Option<Self> {
         match syntax.kind() {
@@ -2133,12 +2684,20 @@ pub fn owned(&self) -> PathTypeNode {
 impl<'a> PathType<'a> {}
 
 // PlaceholderPat
-#[derive(Debug, Clone, Copy)]
+#[derive(Debug, Clone, Copy,)]
 pub struct PlaceholderPatNode<R: TreeRoot<RaTypes> = OwnedRoot> {
-    syntax: SyntaxNode<R>,
+    pub(crate) syntax: SyntaxNode<R>,
 }
 pub type PlaceholderPat<'a> = PlaceholderPatNode<RefRoot<'a>>;
 
+impl<R1: TreeRoot<RaTypes>, R2: TreeRoot<RaTypes>> PartialEq<PlaceholderPatNode<R1>> for PlaceholderPatNode<R2> {
+    fn eq(&self, other: &PlaceholderPatNode<R1>) -> bool { self.syntax == other.syntax }
+}
+impl<R: TreeRoot<RaTypes>> Eq for PlaceholderPatNode<R> {}
+impl<R: TreeRoot<RaTypes>> Hash for PlaceholderPatNode<R> {
+    fn hash<H: Hasher>(&self, state: &mut H) { self.syntax.hash(state) }
+}
+
 impl<'a> AstNode<'a> for PlaceholderPat<'a> {
     fn cast(syntax: SyntaxNodeRef<'a>) -> Option<Self> {
         match syntax.kind() {
@@ -2162,12 +2721,20 @@ pub fn owned(&self) -> PlaceholderPatNode {
 impl<'a> PlaceholderPat<'a> {}
 
 // PlaceholderType
-#[derive(Debug, Clone, Copy)]
+#[derive(Debug, Clone, Copy,)]
 pub struct PlaceholderTypeNode<R: TreeRoot<RaTypes> = OwnedRoot> {
-    syntax: SyntaxNode<R>,
+    pub(crate) syntax: SyntaxNode<R>,
 }
 pub type PlaceholderType<'a> = PlaceholderTypeNode<RefRoot<'a>>;
 
+impl<R1: TreeRoot<RaTypes>, R2: TreeRoot<RaTypes>> PartialEq<PlaceholderTypeNode<R1>> for PlaceholderTypeNode<R2> {
+    fn eq(&self, other: &PlaceholderTypeNode<R1>) -> bool { self.syntax == other.syntax }
+}
+impl<R: TreeRoot<RaTypes>> Eq for PlaceholderTypeNode<R> {}
+impl<R: TreeRoot<RaTypes>> Hash for PlaceholderTypeNode<R> {
+    fn hash<H: Hasher>(&self, state: &mut H) { self.syntax.hash(state) }
+}
+
 impl<'a> AstNode<'a> for PlaceholderType<'a> {
     fn cast(syntax: SyntaxNodeRef<'a>) -> Option<Self> {
         match syntax.kind() {
@@ -2191,12 +2758,20 @@ pub fn owned(&self) -> PlaceholderTypeNode {
 impl<'a> PlaceholderType<'a> {}
 
 // PointerType
-#[derive(Debug, Clone, Copy)]
+#[derive(Debug, Clone, Copy,)]
 pub struct PointerTypeNode<R: TreeRoot<RaTypes> = OwnedRoot> {
-    syntax: SyntaxNode<R>,
+    pub(crate) syntax: SyntaxNode<R>,
 }
 pub type PointerType<'a> = PointerTypeNode<RefRoot<'a>>;
 
+impl<R1: TreeRoot<RaTypes>, R2: TreeRoot<RaTypes>> PartialEq<PointerTypeNode<R1>> for PointerTypeNode<R2> {
+    fn eq(&self, other: &PointerTypeNode<R1>) -> bool { self.syntax == other.syntax }
+}
+impl<R: TreeRoot<RaTypes>> Eq for PointerTypeNode<R> {}
+impl<R: TreeRoot<RaTypes>> Hash for PointerTypeNode<R> {
+    fn hash<H: Hasher>(&self, state: &mut H) { self.syntax.hash(state) }
+}
+
 impl<'a> AstNode<'a> for PointerType<'a> {
     fn cast(syntax: SyntaxNodeRef<'a>) -> Option<Self> {
         match syntax.kind() {
@@ -2220,12 +2795,20 @@ pub fn owned(&self) -> PointerTypeNode {
 impl<'a> PointerType<'a> {}
 
 // PrefixExpr
-#[derive(Debug, Clone, Copy)]
+#[derive(Debug, Clone, Copy,)]
 pub struct PrefixExprNode<R: TreeRoot<RaTypes> = OwnedRoot> {
-    syntax: SyntaxNode<R>,
+    pub(crate) syntax: SyntaxNode<R>,
 }
 pub type PrefixExpr<'a> = PrefixExprNode<RefRoot<'a>>;
 
+impl<R1: TreeRoot<RaTypes>, R2: TreeRoot<RaTypes>> PartialEq<PrefixExprNode<R1>> for PrefixExprNode<R2> {
+    fn eq(&self, other: &PrefixExprNode<R1>) -> bool { self.syntax == other.syntax }
+}
+impl<R: TreeRoot<RaTypes>> Eq for PrefixExprNode<R> {}
+impl<R: TreeRoot<RaTypes>> Hash for PrefixExprNode<R> {
+    fn hash<H: Hasher>(&self, state: &mut H) { self.syntax.hash(state) }
+}
+
 impl<'a> AstNode<'a> for PrefixExpr<'a> {
     fn cast(syntax: SyntaxNodeRef<'a>) -> Option<Self> {
         match syntax.kind() {
@@ -2249,12 +2832,20 @@ pub fn owned(&self) -> PrefixExprNode {
 impl<'a> PrefixExpr<'a> {}
 
 // RangeExpr
-#[derive(Debug, Clone, Copy)]
+#[derive(Debug, Clone, Copy,)]
 pub struct RangeExprNode<R: TreeRoot<RaTypes> = OwnedRoot> {
-    syntax: SyntaxNode<R>,
+    pub(crate) syntax: SyntaxNode<R>,
 }
 pub type RangeExpr<'a> = RangeExprNode<RefRoot<'a>>;
 
+impl<R1: TreeRoot<RaTypes>, R2: TreeRoot<RaTypes>> PartialEq<RangeExprNode<R1>> for RangeExprNode<R2> {
+    fn eq(&self, other: &RangeExprNode<R1>) -> bool { self.syntax == other.syntax }
+}
+impl<R: TreeRoot<RaTypes>> Eq for RangeExprNode<R> {}
+impl<R: TreeRoot<RaTypes>> Hash for RangeExprNode<R> {
+    fn hash<H: Hasher>(&self, state: &mut H) { self.syntax.hash(state) }
+}
+
 impl<'a> AstNode<'a> for RangeExpr<'a> {
     fn cast(syntax: SyntaxNodeRef<'a>) -> Option<Self> {
         match syntax.kind() {
@@ -2278,12 +2869,20 @@ pub fn owned(&self) -> RangeExprNode {
 impl<'a> RangeExpr<'a> {}
 
 // RangePat
-#[derive(Debug, Clone, Copy)]
+#[derive(Debug, Clone, Copy,)]
 pub struct RangePatNode<R: TreeRoot<RaTypes> = OwnedRoot> {
-    syntax: SyntaxNode<R>,
+    pub(crate) syntax: SyntaxNode<R>,
 }
 pub type RangePat<'a> = RangePatNode<RefRoot<'a>>;
 
+impl<R1: TreeRoot<RaTypes>, R2: TreeRoot<RaTypes>> PartialEq<RangePatNode<R1>> for RangePatNode<R2> {
+    fn eq(&self, other: &RangePatNode<R1>) -> bool { self.syntax == other.syntax }
+}
+impl<R: TreeRoot<RaTypes>> Eq for RangePatNode<R> {}
+impl<R: TreeRoot<RaTypes>> Hash for RangePatNode<R> {
+    fn hash<H: Hasher>(&self, state: &mut H) { self.syntax.hash(state) }
+}
+
 impl<'a> AstNode<'a> for RangePat<'a> {
     fn cast(syntax: SyntaxNodeRef<'a>) -> Option<Self> {
         match syntax.kind() {
@@ -2307,12 +2906,20 @@ pub fn owned(&self) -> RangePatNode {
 impl<'a> RangePat<'a> {}
 
 // RefExpr
-#[derive(Debug, Clone, Copy)]
+#[derive(Debug, Clone, Copy,)]
 pub struct RefExprNode<R: TreeRoot<RaTypes> = OwnedRoot> {
-    syntax: SyntaxNode<R>,
+    pub(crate) syntax: SyntaxNode<R>,
 }
 pub type RefExpr<'a> = RefExprNode<RefRoot<'a>>;
 
+impl<R1: TreeRoot<RaTypes>, R2: TreeRoot<RaTypes>> PartialEq<RefExprNode<R1>> for RefExprNode<R2> {
+    fn eq(&self, other: &RefExprNode<R1>) -> bool { self.syntax == other.syntax }
+}
+impl<R: TreeRoot<RaTypes>> Eq for RefExprNode<R> {}
+impl<R: TreeRoot<RaTypes>> Hash for RefExprNode<R> {
+    fn hash<H: Hasher>(&self, state: &mut H) { self.syntax.hash(state) }
+}
+
 impl<'a> AstNode<'a> for RefExpr<'a> {
     fn cast(syntax: SyntaxNodeRef<'a>) -> Option<Self> {
         match syntax.kind() {
@@ -2336,12 +2943,20 @@ pub fn owned(&self) -> RefExprNode {
 impl<'a> RefExpr<'a> {}
 
 // RefPat
-#[derive(Debug, Clone, Copy)]
+#[derive(Debug, Clone, Copy,)]
 pub struct RefPatNode<R: TreeRoot<RaTypes> = OwnedRoot> {
-    syntax: SyntaxNode<R>,
+    pub(crate) syntax: SyntaxNode<R>,
 }
 pub type RefPat<'a> = RefPatNode<RefRoot<'a>>;
 
+impl<R1: TreeRoot<RaTypes>, R2: TreeRoot<RaTypes>> PartialEq<RefPatNode<R1>> for RefPatNode<R2> {
+    fn eq(&self, other: &RefPatNode<R1>) -> bool { self.syntax == other.syntax }
+}
+impl<R: TreeRoot<RaTypes>> Eq for RefPatNode<R> {}
+impl<R: TreeRoot<RaTypes>> Hash for RefPatNode<R> {
+    fn hash<H: Hasher>(&self, state: &mut H) { self.syntax.hash(state) }
+}
+
 impl<'a> AstNode<'a> for RefPat<'a> {
     fn cast(syntax: SyntaxNodeRef<'a>) -> Option<Self> {
         match syntax.kind() {
@@ -2365,12 +2980,20 @@ pub fn owned(&self) -> RefPatNode {
 impl<'a> RefPat<'a> {}
 
 // ReferenceType
-#[derive(Debug, Clone, Copy)]
+#[derive(Debug, Clone, Copy,)]
 pub struct ReferenceTypeNode<R: TreeRoot<RaTypes> = OwnedRoot> {
-    syntax: SyntaxNode<R>,
+    pub(crate) syntax: SyntaxNode<R>,
 }
 pub type ReferenceType<'a> = ReferenceTypeNode<RefRoot<'a>>;
 
+impl<R1: TreeRoot<RaTypes>, R2: TreeRoot<RaTypes>> PartialEq<ReferenceTypeNode<R1>> for ReferenceTypeNode<R2> {
+    fn eq(&self, other: &ReferenceTypeNode<R1>) -> bool { self.syntax == other.syntax }
+}
+impl<R: TreeRoot<RaTypes>> Eq for ReferenceTypeNode<R> {}
+impl<R: TreeRoot<RaTypes>> Hash for ReferenceTypeNode<R> {
+    fn hash<H: Hasher>(&self, state: &mut H) { self.syntax.hash(state) }
+}
+
 impl<'a> AstNode<'a> for ReferenceType<'a> {
     fn cast(syntax: SyntaxNodeRef<'a>) -> Option<Self> {
         match syntax.kind() {
@@ -2394,12 +3017,20 @@ pub fn owned(&self) -> ReferenceTypeNode {
 impl<'a> ReferenceType<'a> {}
 
 // RetType
-#[derive(Debug, Clone, Copy)]
+#[derive(Debug, Clone, Copy,)]
 pub struct RetTypeNode<R: TreeRoot<RaTypes> = OwnedRoot> {
-    syntax: SyntaxNode<R>,
+    pub(crate) syntax: SyntaxNode<R>,
 }
 pub type RetType<'a> = RetTypeNode<RefRoot<'a>>;
 
+impl<R1: TreeRoot<RaTypes>, R2: TreeRoot<RaTypes>> PartialEq<RetTypeNode<R1>> for RetTypeNode<R2> {
+    fn eq(&self, other: &RetTypeNode<R1>) -> bool { self.syntax == other.syntax }
+}
+impl<R: TreeRoot<RaTypes>> Eq for RetTypeNode<R> {}
+impl<R: TreeRoot<RaTypes>> Hash for RetTypeNode<R> {
+    fn hash<H: Hasher>(&self, state: &mut H) { self.syntax.hash(state) }
+}
+
 impl<'a> AstNode<'a> for RetType<'a> {
     fn cast(syntax: SyntaxNodeRef<'a>) -> Option<Self> {
         match syntax.kind() {
@@ -2423,12 +3054,20 @@ pub fn owned(&self) -> RetTypeNode {
 impl<'a> RetType<'a> {}
 
 // ReturnExpr
-#[derive(Debug, Clone, Copy)]
+#[derive(Debug, Clone, Copy,)]
 pub struct ReturnExprNode<R: TreeRoot<RaTypes> = OwnedRoot> {
-    syntax: SyntaxNode<R>,
+    pub(crate) syntax: SyntaxNode<R>,
 }
 pub type ReturnExpr<'a> = ReturnExprNode<RefRoot<'a>>;
 
+impl<R1: TreeRoot<RaTypes>, R2: TreeRoot<RaTypes>> PartialEq<ReturnExprNode<R1>> for ReturnExprNode<R2> {
+    fn eq(&self, other: &ReturnExprNode<R1>) -> bool { self.syntax == other.syntax }
+}
+impl<R: TreeRoot<RaTypes>> Eq for ReturnExprNode<R> {}
+impl<R: TreeRoot<RaTypes>> Hash for ReturnExprNode<R> {
+    fn hash<H: Hasher>(&self, state: &mut H) { self.syntax.hash(state) }
+}
+
 impl<'a> AstNode<'a> for ReturnExpr<'a> {
     fn cast(syntax: SyntaxNodeRef<'a>) -> Option<Self> {
         match syntax.kind() {
@@ -2451,48 +3090,21 @@ pub fn owned(&self) -> ReturnExprNode {
 
 impl<'a> ReturnExpr<'a> {}
 
-// Root
-#[derive(Debug, Clone, Copy)]
-pub struct RootNode<R: TreeRoot<RaTypes> = OwnedRoot> {
-    syntax: SyntaxNode<R>,
-}
-pub type Root<'a> = RootNode<RefRoot<'a>>;
-
-impl<'a> AstNode<'a> for Root<'a> {
-    fn cast(syntax: SyntaxNodeRef<'a>) -> Option<Self> {
-        match syntax.kind() {
-            ROOT => Some(Root { syntax }),
-            _ => None,
-        }
-    }
-    fn syntax(self) -> SyntaxNodeRef<'a> { self.syntax }
-}
-
-impl<R: TreeRoot<RaTypes>> RootNode<R> {
-    pub fn borrowed(&self) -> Root {
-        RootNode { syntax: self.syntax.borrowed() }
-    }
-    pub fn owned(&self) -> RootNode {
-        RootNode { syntax: self.syntax.owned() }
-    }
-}
-
-
-impl<'a> ast::ModuleItemOwner<'a> for Root<'a> {}
-impl<'a> ast::FnDefOwner<'a> for Root<'a> {}
-impl<'a> Root<'a> {
-    pub fn modules(self) -> impl Iterator<Item = Module<'a>> + 'a {
-        super::children(self)
-    }
-}
-
 // SelfParam
-#[derive(Debug, Clone, Copy)]
+#[derive(Debug, Clone, Copy,)]
 pub struct SelfParamNode<R: TreeRoot<RaTypes> = OwnedRoot> {
-    syntax: SyntaxNode<R>,
+    pub(crate) syntax: SyntaxNode<R>,
 }
 pub type SelfParam<'a> = SelfParamNode<RefRoot<'a>>;
 
+impl<R1: TreeRoot<RaTypes>, R2: TreeRoot<RaTypes>> PartialEq<SelfParamNode<R1>> for SelfParamNode<R2> {
+    fn eq(&self, other: &SelfParamNode<R1>) -> bool { self.syntax == other.syntax }
+}
+impl<R: TreeRoot<RaTypes>> Eq for SelfParamNode<R> {}
+impl<R: TreeRoot<RaTypes>> Hash for SelfParamNode<R> {
+    fn hash<H: Hasher>(&self, state: &mut H) { self.syntax.hash(state) }
+}
+
 impl<'a> AstNode<'a> for SelfParam<'a> {
     fn cast(syntax: SyntaxNodeRef<'a>) -> Option<Self> {
         match syntax.kind() {
@@ -2516,12 +3128,20 @@ pub fn owned(&self) -> SelfParamNode {
 impl<'a> SelfParam<'a> {}
 
 // SlicePat
-#[derive(Debug, Clone, Copy)]
+#[derive(Debug, Clone, Copy,)]
 pub struct SlicePatNode<R: TreeRoot<RaTypes> = OwnedRoot> {
-    syntax: SyntaxNode<R>,
+    pub(crate) syntax: SyntaxNode<R>,
 }
 pub type SlicePat<'a> = SlicePatNode<RefRoot<'a>>;
 
+impl<R1: TreeRoot<RaTypes>, R2: TreeRoot<RaTypes>> PartialEq<SlicePatNode<R1>> for SlicePatNode<R2> {
+    fn eq(&self, other: &SlicePatNode<R1>) -> bool { self.syntax == other.syntax }
+}
+impl<R: TreeRoot<RaTypes>> Eq for SlicePatNode<R> {}
+impl<R: TreeRoot<RaTypes>> Hash for SlicePatNode<R> {
+    fn hash<H: Hasher>(&self, state: &mut H) { self.syntax.hash(state) }
+}
+
 impl<'a> AstNode<'a> for SlicePat<'a> {
     fn cast(syntax: SyntaxNodeRef<'a>) -> Option<Self> {
         match syntax.kind() {
@@ -2545,12 +3165,20 @@ pub fn owned(&self) -> SlicePatNode {
 impl<'a> SlicePat<'a> {}
 
 // SliceType
-#[derive(Debug, Clone, Copy)]
+#[derive(Debug, Clone, Copy,)]
 pub struct SliceTypeNode<R: TreeRoot<RaTypes> = OwnedRoot> {
-    syntax: SyntaxNode<R>,
+    pub(crate) syntax: SyntaxNode<R>,
 }
 pub type SliceType<'a> = SliceTypeNode<RefRoot<'a>>;
 
+impl<R1: TreeRoot<RaTypes>, R2: TreeRoot<RaTypes>> PartialEq<SliceTypeNode<R1>> for SliceTypeNode<R2> {
+    fn eq(&self, other: &SliceTypeNode<R1>) -> bool { self.syntax == other.syntax }
+}
+impl<R: TreeRoot<RaTypes>> Eq for SliceTypeNode<R> {}
+impl<R: TreeRoot<RaTypes>> Hash for SliceTypeNode<R> {
+    fn hash<H: Hasher>(&self, state: &mut H) { self.syntax.hash(state) }
+}
+
 impl<'a> AstNode<'a> for SliceType<'a> {
     fn cast(syntax: SyntaxNodeRef<'a>) -> Option<Self> {
         match syntax.kind() {
@@ -2573,13 +3201,64 @@ pub fn owned(&self) -> SliceTypeNode {
 
 impl<'a> SliceType<'a> {}
 
+// SourceFile
+#[derive(Debug, Clone, Copy,)]
+pub struct SourceFileNode<R: TreeRoot<RaTypes> = OwnedRoot> {
+    pub(crate) syntax: SyntaxNode<R>,
+}
+pub type SourceFile<'a> = SourceFileNode<RefRoot<'a>>;
+
+impl<R1: TreeRoot<RaTypes>, R2: TreeRoot<RaTypes>> PartialEq<SourceFileNode<R1>> for SourceFileNode<R2> {
+    fn eq(&self, other: &SourceFileNode<R1>) -> bool { self.syntax == other.syntax }
+}
+impl<R: TreeRoot<RaTypes>> Eq for SourceFileNode<R> {}
+impl<R: TreeRoot<RaTypes>> Hash for SourceFileNode<R> {
+    fn hash<H: Hasher>(&self, state: &mut H) { self.syntax.hash(state) }
+}
+
+impl<'a> AstNode<'a> for SourceFile<'a> {
+    fn cast(syntax: SyntaxNodeRef<'a>) -> Option<Self> {
+        match syntax.kind() {
+            SOURCE_FILE => Some(SourceFile { syntax }),
+            _ => None,
+        }
+    }
+    fn syntax(self) -> SyntaxNodeRef<'a> { self.syntax }
+}
+
+impl<R: TreeRoot<RaTypes>> SourceFileNode<R> {
+    pub fn borrowed(&self) -> SourceFile {
+        SourceFileNode { syntax: self.syntax.borrowed() }
+    }
+    pub fn owned(&self) -> SourceFileNode {
+        SourceFileNode { syntax: self.syntax.owned() }
+    }
+}
+
+
+impl<'a> ast::ModuleItemOwner<'a> for SourceFile<'a> {}
+impl<'a> ast::FnDefOwner<'a> for SourceFile<'a> {}
+impl<'a> SourceFile<'a> {
+    pub fn modules(self) -> impl Iterator<Item = Module<'a>> + 'a {
+        super::children(self)
+    }
+}
+
 // StaticDef
-#[derive(Debug, Clone, Copy)]
+#[derive(Debug, Clone, Copy,)]
 pub struct StaticDefNode<R: TreeRoot<RaTypes> = OwnedRoot> {
-    syntax: SyntaxNode<R>,
+    pub(crate) syntax: SyntaxNode<R>,
 }
 pub type StaticDef<'a> = StaticDefNode<RefRoot<'a>>;
 
+impl<R1: TreeRoot<RaTypes>, R2: TreeRoot<RaTypes>> PartialEq<StaticDefNode<R1>> for StaticDefNode<R2> {
+    fn eq(&self, other: &StaticDefNode<R1>) -> bool { self.syntax == other.syntax }
+}
+impl<R: TreeRoot<RaTypes>> Eq for StaticDefNode<R> {}
+impl<R: TreeRoot<RaTypes>> Hash for StaticDefNode<R> {
+    fn hash<H: Hasher>(&self, state: &mut H) { self.syntax.hash(state) }
+}
+
 impl<'a> AstNode<'a> for StaticDef<'a> {
     fn cast(syntax: SyntaxNodeRef<'a>) -> Option<Self> {
         match syntax.kind() {
@@ -2603,10 +3282,11 @@ pub fn owned(&self) -> StaticDefNode {
 impl<'a> ast::NameOwner<'a> for StaticDef<'a> {}
 impl<'a> ast::TypeParamsOwner<'a> for StaticDef<'a> {}
 impl<'a> ast::AttrsOwner<'a> for StaticDef<'a> {}
+impl<'a> ast::DocCommentsOwner<'a> for StaticDef<'a> {}
 impl<'a> StaticDef<'a> {}
 
 // Stmt
-#[derive(Debug, Clone, Copy)]
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
 pub enum Stmt<'a> {
     ExprStmt(ExprStmt<'a>),
     LetStmt(LetStmt<'a>),
@@ -2630,13 +3310,58 @@ fn syntax(self) -> SyntaxNodeRef<'a> {
 
 impl<'a> Stmt<'a> {}
 
+// String
+#[derive(Debug, Clone, Copy,)]
+pub struct StringNode<R: TreeRoot<RaTypes> = OwnedRoot> {
+    pub(crate) syntax: SyntaxNode<R>,
+}
+pub type String<'a> = StringNode<RefRoot<'a>>;
+
+impl<R1: TreeRoot<RaTypes>, R2: TreeRoot<RaTypes>> PartialEq<StringNode<R1>> for StringNode<R2> {
+    fn eq(&self, other: &StringNode<R1>) -> bool { self.syntax == other.syntax }
+}
+impl<R: TreeRoot<RaTypes>> Eq for StringNode<R> {}
+impl<R: TreeRoot<RaTypes>> Hash for StringNode<R> {
+    fn hash<H: Hasher>(&self, state: &mut H) { self.syntax.hash(state) }
+}
+
+impl<'a> AstNode<'a> for String<'a> {
+    fn cast(syntax: SyntaxNodeRef<'a>) -> Option<Self> {
+        match syntax.kind() {
+            STRING => Some(String { syntax }),
+            _ => None,
+        }
+    }
+    fn syntax(self) -> SyntaxNodeRef<'a> { self.syntax }
+}
+
+impl<R: TreeRoot<RaTypes>> StringNode<R> {
+    pub fn borrowed(&self) -> String {
+        StringNode { syntax: self.syntax.borrowed() }
+    }
+    pub fn owned(&self) -> StringNode {
+        StringNode { syntax: self.syntax.owned() }
+    }
+}
+
+
+impl<'a> String<'a> {}
+
 // StructDef
-#[derive(Debug, Clone, Copy)]
+#[derive(Debug, Clone, Copy,)]
 pub struct StructDefNode<R: TreeRoot<RaTypes> = OwnedRoot> {
-    syntax: SyntaxNode<R>,
+    pub(crate) syntax: SyntaxNode<R>,
 }
 pub type StructDef<'a> = StructDefNode<RefRoot<'a>>;
 
+impl<R1: TreeRoot<RaTypes>, R2: TreeRoot<RaTypes>> PartialEq<StructDefNode<R1>> for StructDefNode<R2> {
+    fn eq(&self, other: &StructDefNode<R1>) -> bool { self.syntax == other.syntax }
+}
+impl<R: TreeRoot<RaTypes>> Eq for StructDefNode<R> {}
+impl<R: TreeRoot<RaTypes>> Hash for StructDefNode<R> {
+    fn hash<H: Hasher>(&self, state: &mut H) { self.syntax.hash(state) }
+}
+
 impl<'a> AstNode<'a> for StructDef<'a> {
     fn cast(syntax: SyntaxNodeRef<'a>) -> Option<Self> {
         match syntax.kind() {
@@ -2660,6 +3385,7 @@ pub fn owned(&self) -> StructDefNode {
 impl<'a> ast::NameOwner<'a> for StructDef<'a> {}
 impl<'a> ast::TypeParamsOwner<'a> for StructDef<'a> {}
 impl<'a> ast::AttrsOwner<'a> for StructDef<'a> {}
+impl<'a> ast::DocCommentsOwner<'a> for StructDef<'a> {}
 impl<'a> StructDef<'a> {
     pub fn fields(self) -> impl Iterator<Item = NamedFieldDef<'a>> + 'a {
         super::children(self)
@@ -2667,12 +3393,20 @@ pub fn fields(self) -> impl Iterator<Item = NamedFieldDef<'a>> + 'a {
 }
 
 // StructLit
-#[derive(Debug, Clone, Copy)]
+#[derive(Debug, Clone, Copy,)]
 pub struct StructLitNode<R: TreeRoot<RaTypes> = OwnedRoot> {
-    syntax: SyntaxNode<R>,
+    pub(crate) syntax: SyntaxNode<R>,
 }
 pub type StructLit<'a> = StructLitNode<RefRoot<'a>>;
 
+impl<R1: TreeRoot<RaTypes>, R2: TreeRoot<RaTypes>> PartialEq<StructLitNode<R1>> for StructLitNode<R2> {
+    fn eq(&self, other: &StructLitNode<R1>) -> bool { self.syntax == other.syntax }
+}
+impl<R: TreeRoot<RaTypes>> Eq for StructLitNode<R> {}
+impl<R: TreeRoot<RaTypes>> Hash for StructLitNode<R> {
+    fn hash<H: Hasher>(&self, state: &mut H) { self.syntax.hash(state) }
+}
+
 impl<'a> AstNode<'a> for StructLit<'a> {
     fn cast(syntax: SyntaxNodeRef<'a>) -> Option<Self> {
         match syntax.kind() {
@@ -2696,12 +3430,20 @@ pub fn owned(&self) -> StructLitNode {
 impl<'a> StructLit<'a> {}
 
 // StructPat
-#[derive(Debug, Clone, Copy)]
+#[derive(Debug, Clone, Copy,)]
 pub struct StructPatNode<R: TreeRoot<RaTypes> = OwnedRoot> {
-    syntax: SyntaxNode<R>,
+    pub(crate) syntax: SyntaxNode<R>,
 }
 pub type StructPat<'a> = StructPatNode<RefRoot<'a>>;
 
+impl<R1: TreeRoot<RaTypes>, R2: TreeRoot<RaTypes>> PartialEq<StructPatNode<R1>> for StructPatNode<R2> {
+    fn eq(&self, other: &StructPatNode<R1>) -> bool { self.syntax == other.syntax }
+}
+impl<R: TreeRoot<RaTypes>> Eq for StructPatNode<R> {}
+impl<R: TreeRoot<RaTypes>> Hash for StructPatNode<R> {
+    fn hash<H: Hasher>(&self, state: &mut H) { self.syntax.hash(state) }
+}
+
 impl<'a> AstNode<'a> for StructPat<'a> {
     fn cast(syntax: SyntaxNodeRef<'a>) -> Option<Self> {
         match syntax.kind() {
@@ -2725,12 +3467,20 @@ pub fn owned(&self) -> StructPatNode {
 impl<'a> StructPat<'a> {}
 
 // TokenTree
-#[derive(Debug, Clone, Copy)]
+#[derive(Debug, Clone, Copy,)]
 pub struct TokenTreeNode<R: TreeRoot<RaTypes> = OwnedRoot> {
-    syntax: SyntaxNode<R>,
+    pub(crate) syntax: SyntaxNode<R>,
 }
 pub type TokenTree<'a> = TokenTreeNode<RefRoot<'a>>;
 
+impl<R1: TreeRoot<RaTypes>, R2: TreeRoot<RaTypes>> PartialEq<TokenTreeNode<R1>> for TokenTreeNode<R2> {
+    fn eq(&self, other: &TokenTreeNode<R1>) -> bool { self.syntax == other.syntax }
+}
+impl<R: TreeRoot<RaTypes>> Eq for TokenTreeNode<R> {}
+impl<R: TreeRoot<RaTypes>> Hash for TokenTreeNode<R> {
+    fn hash<H: Hasher>(&self, state: &mut H) { self.syntax.hash(state) }
+}
+
 impl<'a> AstNode<'a> for TokenTree<'a> {
     fn cast(syntax: SyntaxNodeRef<'a>) -> Option<Self> {
         match syntax.kind() {
@@ -2754,12 +3504,20 @@ pub fn owned(&self) -> TokenTreeNode {
 impl<'a> TokenTree<'a> {}
 
 // TraitDef
-#[derive(Debug, Clone, Copy)]
+#[derive(Debug, Clone, Copy,)]
 pub struct TraitDefNode<R: TreeRoot<RaTypes> = OwnedRoot> {
-    syntax: SyntaxNode<R>,
+    pub(crate) syntax: SyntaxNode<R>,
 }
 pub type TraitDef<'a> = TraitDefNode<RefRoot<'a>>;
 
+impl<R1: TreeRoot<RaTypes>, R2: TreeRoot<RaTypes>> PartialEq<TraitDefNode<R1>> for TraitDefNode<R2> {
+    fn eq(&self, other: &TraitDefNode<R1>) -> bool { self.syntax == other.syntax }
+}
+impl<R: TreeRoot<RaTypes>> Eq for TraitDefNode<R> {}
+impl<R: TreeRoot<RaTypes>> Hash for TraitDefNode<R> {
+    fn hash<H: Hasher>(&self, state: &mut H) { self.syntax.hash(state) }
+}
+
 impl<'a> AstNode<'a> for TraitDef<'a> {
     fn cast(syntax: SyntaxNodeRef<'a>) -> Option<Self> {
         match syntax.kind() {
@@ -2782,15 +3540,24 @@ pub fn owned(&self) -> TraitDefNode {
 
 impl<'a> ast::NameOwner<'a> for TraitDef<'a> {}
 impl<'a> ast::AttrsOwner<'a> for TraitDef<'a> {}
+impl<'a> ast::DocCommentsOwner<'a> for TraitDef<'a> {}
 impl<'a> TraitDef<'a> {}
 
 // TryExpr
-#[derive(Debug, Clone, Copy)]
+#[derive(Debug, Clone, Copy,)]
 pub struct TryExprNode<R: TreeRoot<RaTypes> = OwnedRoot> {
-    syntax: SyntaxNode<R>,
+    pub(crate) syntax: SyntaxNode<R>,
 }
 pub type TryExpr<'a> = TryExprNode<RefRoot<'a>>;
 
+impl<R1: TreeRoot<RaTypes>, R2: TreeRoot<RaTypes>> PartialEq<TryExprNode<R1>> for TryExprNode<R2> {
+    fn eq(&self, other: &TryExprNode<R1>) -> bool { self.syntax == other.syntax }
+}
+impl<R: TreeRoot<RaTypes>> Eq for TryExprNode<R> {}
+impl<R: TreeRoot<RaTypes>> Hash for TryExprNode<R> {
+    fn hash<H: Hasher>(&self, state: &mut H) { self.syntax.hash(state) }
+}
+
 impl<'a> AstNode<'a> for TryExpr<'a> {
     fn cast(syntax: SyntaxNodeRef<'a>) -> Option<Self> {
         match syntax.kind() {
@@ -2814,12 +3581,20 @@ pub fn owned(&self) -> TryExprNode {
 impl<'a> TryExpr<'a> {}
 
 // TupleExpr
-#[derive(Debug, Clone, Copy)]
+#[derive(Debug, Clone, Copy,)]
 pub struct TupleExprNode<R: TreeRoot<RaTypes> = OwnedRoot> {
-    syntax: SyntaxNode<R>,
+    pub(crate) syntax: SyntaxNode<R>,
 }
 pub type TupleExpr<'a> = TupleExprNode<RefRoot<'a>>;
 
+impl<R1: TreeRoot<RaTypes>, R2: TreeRoot<RaTypes>> PartialEq<TupleExprNode<R1>> for TupleExprNode<R2> {
+    fn eq(&self, other: &TupleExprNode<R1>) -> bool { self.syntax == other.syntax }
+}
+impl<R: TreeRoot<RaTypes>> Eq for TupleExprNode<R> {}
+impl<R: TreeRoot<RaTypes>> Hash for TupleExprNode<R> {
+    fn hash<H: Hasher>(&self, state: &mut H) { self.syntax.hash(state) }
+}
+
 impl<'a> AstNode<'a> for TupleExpr<'a> {
     fn cast(syntax: SyntaxNodeRef<'a>) -> Option<Self> {
         match syntax.kind() {
@@ -2843,12 +3618,20 @@ pub fn owned(&self) -> TupleExprNode {
 impl<'a> TupleExpr<'a> {}
 
 // TuplePat
-#[derive(Debug, Clone, Copy)]
+#[derive(Debug, Clone, Copy,)]
 pub struct TuplePatNode<R: TreeRoot<RaTypes> = OwnedRoot> {
-    syntax: SyntaxNode<R>,
+    pub(crate) syntax: SyntaxNode<R>,
 }
 pub type TuplePat<'a> = TuplePatNode<RefRoot<'a>>;
 
+impl<R1: TreeRoot<RaTypes>, R2: TreeRoot<RaTypes>> PartialEq<TuplePatNode<R1>> for TuplePatNode<R2> {
+    fn eq(&self, other: &TuplePatNode<R1>) -> bool { self.syntax == other.syntax }
+}
+impl<R: TreeRoot<RaTypes>> Eq for TuplePatNode<R> {}
+impl<R: TreeRoot<RaTypes>> Hash for TuplePatNode<R> {
+    fn hash<H: Hasher>(&self, state: &mut H) { self.syntax.hash(state) }
+}
+
 impl<'a> AstNode<'a> for TuplePat<'a> {
     fn cast(syntax: SyntaxNodeRef<'a>) -> Option<Self> {
         match syntax.kind() {
@@ -2872,12 +3655,20 @@ pub fn owned(&self) -> TuplePatNode {
 impl<'a> TuplePat<'a> {}
 
 // TupleStructPat
-#[derive(Debug, Clone, Copy)]
+#[derive(Debug, Clone, Copy,)]
 pub struct TupleStructPatNode<R: TreeRoot<RaTypes> = OwnedRoot> {
-    syntax: SyntaxNode<R>,
+    pub(crate) syntax: SyntaxNode<R>,
 }
 pub type TupleStructPat<'a> = TupleStructPatNode<RefRoot<'a>>;
 
+impl<R1: TreeRoot<RaTypes>, R2: TreeRoot<RaTypes>> PartialEq<TupleStructPatNode<R1>> for TupleStructPatNode<R2> {
+    fn eq(&self, other: &TupleStructPatNode<R1>) -> bool { self.syntax == other.syntax }
+}
+impl<R: TreeRoot<RaTypes>> Eq for TupleStructPatNode<R> {}
+impl<R: TreeRoot<RaTypes>> Hash for TupleStructPatNode<R> {
+    fn hash<H: Hasher>(&self, state: &mut H) { self.syntax.hash(state) }
+}
+
 impl<'a> AstNode<'a> for TupleStructPat<'a> {
     fn cast(syntax: SyntaxNodeRef<'a>) -> Option<Self> {
         match syntax.kind() {
@@ -2901,12 +3692,20 @@ pub fn owned(&self) -> TupleStructPatNode {
 impl<'a> TupleStructPat<'a> {}
 
 // TupleType
-#[derive(Debug, Clone, Copy)]
+#[derive(Debug, Clone, Copy,)]
 pub struct TupleTypeNode<R: TreeRoot<RaTypes> = OwnedRoot> {
-    syntax: SyntaxNode<R>,
+    pub(crate) syntax: SyntaxNode<R>,
 }
 pub type TupleType<'a> = TupleTypeNode<RefRoot<'a>>;
 
+impl<R1: TreeRoot<RaTypes>, R2: TreeRoot<RaTypes>> PartialEq<TupleTypeNode<R1>> for TupleTypeNode<R2> {
+    fn eq(&self, other: &TupleTypeNode<R1>) -> bool { self.syntax == other.syntax }
+}
+impl<R: TreeRoot<RaTypes>> Eq for TupleTypeNode<R> {}
+impl<R: TreeRoot<RaTypes>> Hash for TupleTypeNode<R> {
+    fn hash<H: Hasher>(&self, state: &mut H) { self.syntax.hash(state) }
+}
+
 impl<'a> AstNode<'a> for TupleType<'a> {
     fn cast(syntax: SyntaxNodeRef<'a>) -> Option<Self> {
         match syntax.kind() {
@@ -2930,12 +3729,20 @@ pub fn owned(&self) -> TupleTypeNode {
 impl<'a> TupleType<'a> {}
 
 // TypeDef
-#[derive(Debug, Clone, Copy)]
+#[derive(Debug, Clone, Copy,)]
 pub struct TypeDefNode<R: TreeRoot<RaTypes> = OwnedRoot> {
-    syntax: SyntaxNode<R>,
+    pub(crate) syntax: SyntaxNode<R>,
 }
 pub type TypeDef<'a> = TypeDefNode<RefRoot<'a>>;
 
+impl<R1: TreeRoot<RaTypes>, R2: TreeRoot<RaTypes>> PartialEq<TypeDefNode<R1>> for TypeDefNode<R2> {
+    fn eq(&self, other: &TypeDefNode<R1>) -> bool { self.syntax == other.syntax }
+}
+impl<R: TreeRoot<RaTypes>> Eq for TypeDefNode<R> {}
+impl<R: TreeRoot<RaTypes>> Hash for TypeDefNode<R> {
+    fn hash<H: Hasher>(&self, state: &mut H) { self.syntax.hash(state) }
+}
+
 impl<'a> AstNode<'a> for TypeDef<'a> {
     fn cast(syntax: SyntaxNodeRef<'a>) -> Option<Self> {
         match syntax.kind() {
@@ -2959,15 +3766,24 @@ pub fn owned(&self) -> TypeDefNode {
 impl<'a> ast::NameOwner<'a> for TypeDef<'a> {}
 impl<'a> ast::TypeParamsOwner<'a> for TypeDef<'a> {}
 impl<'a> ast::AttrsOwner<'a> for TypeDef<'a> {}
+impl<'a> ast::DocCommentsOwner<'a> for TypeDef<'a> {}
 impl<'a> TypeDef<'a> {}
 
 // TypeParam
-#[derive(Debug, Clone, Copy)]
+#[derive(Debug, Clone, Copy,)]
 pub struct TypeParamNode<R: TreeRoot<RaTypes> = OwnedRoot> {
-    syntax: SyntaxNode<R>,
+    pub(crate) syntax: SyntaxNode<R>,
 }
 pub type TypeParam<'a> = TypeParamNode<RefRoot<'a>>;
 
+impl<R1: TreeRoot<RaTypes>, R2: TreeRoot<RaTypes>> PartialEq<TypeParamNode<R1>> for TypeParamNode<R2> {
+    fn eq(&self, other: &TypeParamNode<R1>) -> bool { self.syntax == other.syntax }
+}
+impl<R: TreeRoot<RaTypes>> Eq for TypeParamNode<R> {}
+impl<R: TreeRoot<RaTypes>> Hash for TypeParamNode<R> {
+    fn hash<H: Hasher>(&self, state: &mut H) { self.syntax.hash(state) }
+}
+
 impl<'a> AstNode<'a> for TypeParam<'a> {
     fn cast(syntax: SyntaxNodeRef<'a>) -> Option<Self> {
         match syntax.kind() {
@@ -2992,12 +3808,20 @@ impl<'a> ast::NameOwner<'a> for TypeParam<'a> {}
 impl<'a> TypeParam<'a> {}
 
 // TypeParamList
-#[derive(Debug, Clone, Copy)]
+#[derive(Debug, Clone, Copy,)]
 pub struct TypeParamListNode<R: TreeRoot<RaTypes> = OwnedRoot> {
-    syntax: SyntaxNode<R>,
+    pub(crate) syntax: SyntaxNode<R>,
 }
 pub type TypeParamList<'a> = TypeParamListNode<RefRoot<'a>>;
 
+impl<R1: TreeRoot<RaTypes>, R2: TreeRoot<RaTypes>> PartialEq<TypeParamListNode<R1>> for TypeParamListNode<R2> {
+    fn eq(&self, other: &TypeParamListNode<R1>) -> bool { self.syntax == other.syntax }
+}
+impl<R: TreeRoot<RaTypes>> Eq for TypeParamListNode<R> {}
+impl<R: TreeRoot<RaTypes>> Hash for TypeParamListNode<R> {
+    fn hash<H: Hasher>(&self, state: &mut H) { self.syntax.hash(state) }
+}
+
 impl<'a> AstNode<'a> for TypeParamList<'a> {
     fn cast(syntax: SyntaxNodeRef<'a>) -> Option<Self> {
         match syntax.kind() {
@@ -3029,7 +3853,7 @@ pub fn lifetime_params(self) -> impl Iterator<Item = LifetimeParam<'a>> + 'a {
 }
 
 // TypeRef
-#[derive(Debug, Clone, Copy)]
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
 pub enum TypeRef<'a> {
     ParenType(ParenType<'a>),
     TupleType(TupleType<'a>),
@@ -3087,12 +3911,20 @@ fn syntax(self) -> SyntaxNodeRef<'a> {
 impl<'a> TypeRef<'a> {}
 
 // UseItem
-#[derive(Debug, Clone, Copy)]
+#[derive(Debug, Clone, Copy,)]
 pub struct UseItemNode<R: TreeRoot<RaTypes> = OwnedRoot> {
-    syntax: SyntaxNode<R>,
+    pub(crate) syntax: SyntaxNode<R>,
 }
 pub type UseItem<'a> = UseItemNode<RefRoot<'a>>;
 
+impl<R1: TreeRoot<RaTypes>, R2: TreeRoot<RaTypes>> PartialEq<UseItemNode<R1>> for UseItemNode<R2> {
+    fn eq(&self, other: &UseItemNode<R1>) -> bool { self.syntax == other.syntax }
+}
+impl<R: TreeRoot<RaTypes>> Eq for UseItemNode<R> {}
+impl<R: TreeRoot<RaTypes>> Hash for UseItemNode<R> {
+    fn hash<H: Hasher>(&self, state: &mut H) { self.syntax.hash(state) }
+}
+
 impl<'a> AstNode<'a> for UseItem<'a> {
     fn cast(syntax: SyntaxNodeRef<'a>) -> Option<Self> {
         match syntax.kind() {
@@ -3120,12 +3952,20 @@ pub fn use_tree(self) -> Option<UseTree<'a>> {
 }
 
 // UseTree
-#[derive(Debug, Clone, Copy)]
+#[derive(Debug, Clone, Copy,)]
 pub struct UseTreeNode<R: TreeRoot<RaTypes> = OwnedRoot> {
-    syntax: SyntaxNode<R>,
+    pub(crate) syntax: SyntaxNode<R>,
 }
 pub type UseTree<'a> = UseTreeNode<RefRoot<'a>>;
 
+impl<R1: TreeRoot<RaTypes>, R2: TreeRoot<RaTypes>> PartialEq<UseTreeNode<R1>> for UseTreeNode<R2> {
+    fn eq(&self, other: &UseTreeNode<R1>) -> bool { self.syntax == other.syntax }
+}
+impl<R: TreeRoot<RaTypes>> Eq for UseTreeNode<R> {}
+impl<R: TreeRoot<RaTypes>> Hash for UseTreeNode<R> {
+    fn hash<H: Hasher>(&self, state: &mut H) { self.syntax.hash(state) }
+}
+
 impl<'a> AstNode<'a> for UseTree<'a> {
     fn cast(syntax: SyntaxNodeRef<'a>) -> Option<Self> {
         match syntax.kind() {
@@ -3157,12 +3997,20 @@ pub fn use_tree_list(self) -> Option<UseTreeList<'a>> {
 }
 
 // UseTreeList
-#[derive(Debug, Clone, Copy)]
+#[derive(Debug, Clone, Copy,)]
 pub struct UseTreeListNode<R: TreeRoot<RaTypes> = OwnedRoot> {
-    syntax: SyntaxNode<R>,
+    pub(crate) syntax: SyntaxNode<R>,
 }
 pub type UseTreeList<'a> = UseTreeListNode<RefRoot<'a>>;
 
+impl<R1: TreeRoot<RaTypes>, R2: TreeRoot<RaTypes>> PartialEq<UseTreeListNode<R1>> for UseTreeListNode<R2> {
+    fn eq(&self, other: &UseTreeListNode<R1>) -> bool { self.syntax == other.syntax }
+}
+impl<R: TreeRoot<RaTypes>> Eq for UseTreeListNode<R> {}
+impl<R: TreeRoot<RaTypes>> Hash for UseTreeListNode<R> {
+    fn hash<H: Hasher>(&self, state: &mut H) { self.syntax.hash(state) }
+}
+
 impl<'a> AstNode<'a> for UseTreeList<'a> {
     fn cast(syntax: SyntaxNodeRef<'a>) -> Option<Self> {
         match syntax.kind() {
@@ -3190,12 +4038,20 @@ pub fn use_trees(self) -> impl Iterator<Item = UseTree<'a>> + 'a {
 }
 
 // WhereClause
-#[derive(Debug, Clone, Copy)]
+#[derive(Debug, Clone, Copy,)]
 pub struct WhereClauseNode<R: TreeRoot<RaTypes> = OwnedRoot> {
-    syntax: SyntaxNode<R>,
+    pub(crate) syntax: SyntaxNode<R>,
 }
 pub type WhereClause<'a> = WhereClauseNode<RefRoot<'a>>;
 
+impl<R1: TreeRoot<RaTypes>, R2: TreeRoot<RaTypes>> PartialEq<WhereClauseNode<R1>> for WhereClauseNode<R2> {
+    fn eq(&self, other: &WhereClauseNode<R1>) -> bool { self.syntax == other.syntax }
+}
+impl<R: TreeRoot<RaTypes>> Eq for WhereClauseNode<R> {}
+impl<R: TreeRoot<RaTypes>> Hash for WhereClauseNode<R> {
+    fn hash<H: Hasher>(&self, state: &mut H) { self.syntax.hash(state) }
+}
+
 impl<'a> AstNode<'a> for WhereClause<'a> {
     fn cast(syntax: SyntaxNodeRef<'a>) -> Option<Self> {
         match syntax.kind() {
@@ -3219,12 +4075,20 @@ pub fn owned(&self) -> WhereClauseNode {
 impl<'a> WhereClause<'a> {}
 
 // WhileExpr
-#[derive(Debug, Clone, Copy)]
+#[derive(Debug, Clone, Copy,)]
 pub struct WhileExprNode<R: TreeRoot<RaTypes> = OwnedRoot> {
-    syntax: SyntaxNode<R>,
+    pub(crate) syntax: SyntaxNode<R>,
 }
 pub type WhileExpr<'a> = WhileExprNode<RefRoot<'a>>;
 
+impl<R1: TreeRoot<RaTypes>, R2: TreeRoot<RaTypes>> PartialEq<WhileExprNode<R1>> for WhileExprNode<R2> {
+    fn eq(&self, other: &WhileExprNode<R1>) -> bool { self.syntax == other.syntax }
+}
+impl<R: TreeRoot<RaTypes>> Eq for WhileExprNode<R> {}
+impl<R: TreeRoot<RaTypes>> Hash for WhileExprNode<R> {
+    fn hash<H: Hasher>(&self, state: &mut H) { self.syntax.hash(state) }
+}
+
 impl<'a> AstNode<'a> for WhileExpr<'a> {
     fn cast(syntax: SyntaxNodeRef<'a>) -> Option<Self> {
         match syntax.kind() {
@@ -3253,12 +4117,20 @@ pub fn condition(self) -> Option<Condition<'a>> {
 }
 
 // Whitespace
-#[derive(Debug, Clone, Copy)]
+#[derive(Debug, Clone, Copy,)]
 pub struct WhitespaceNode<R: TreeRoot<RaTypes> = OwnedRoot> {
-    syntax: SyntaxNode<R>,
+    pub(crate) syntax: SyntaxNode<R>,
 }
 pub type Whitespace<'a> = WhitespaceNode<RefRoot<'a>>;
 
+impl<R1: TreeRoot<RaTypes>, R2: TreeRoot<RaTypes>> PartialEq<WhitespaceNode<R1>> for WhitespaceNode<R2> {
+    fn eq(&self, other: &WhitespaceNode<R1>) -> bool { self.syntax == other.syntax }
+}
+impl<R: TreeRoot<RaTypes>> Eq for WhitespaceNode<R> {}
+impl<R: TreeRoot<RaTypes>> Hash for WhitespaceNode<R> {
+    fn hash<H: Hasher>(&self, state: &mut H) { self.syntax.hash(state) }
+}
+
 impl<'a> AstNode<'a> for Whitespace<'a> {
     fn cast(syntax: SyntaxNodeRef<'a>) -> Option<Self> {
         match syntax.kind() {
index d4fc6b5bd8515ea4f09c1615ed77776d9231c2c2..131ee09ecb5646baf280833ed0e02ca880ac08a3 100644 (file)
@@ -11,6 +11,8 @@ the below applies to the result of this template
 
 #![cfg_attr(rustfmt, rustfmt_skip)]
 
+use std::hash::{Hash, Hasher};
+
 use crate::{
     ast,
     SyntaxNode, SyntaxNodeRef, AstNode,
@@ -21,7 +23,7 @@ use crate::{
 // {{ node }}
 
 {%- if methods.enum %}
-#[derive(Debug, Clone, Copy)]
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
 pub enum {{ node }}<'a> {
 {%- for kind in methods.enum %}
     {{ kind }}({{ kind }}<'a>),
@@ -46,12 +48,20 @@ impl<'a> AstNode<'a> for {{ node }}<'a> {
     }
 }
 {% else %}
-#[derive(Debug, Clone, Copy)]
+#[derive(Debug, Clone, Copy,)]
 pub struct {{ node }}Node<R: TreeRoot<RaTypes> = OwnedRoot> {
-    syntax: SyntaxNode<R>,
+    pub(crate) syntax: SyntaxNode<R>,
 }
 pub type {{ node }}<'a> = {{ node }}Node<RefRoot<'a>>;
 
+impl<R1: TreeRoot<RaTypes>, R2: TreeRoot<RaTypes>> PartialEq<{{node}}Node<R1>> for {{node}}Node<R2> {
+    fn eq(&self, other: &{{node}}Node<R1>) -> bool { self.syntax == other.syntax }
+}
+impl<R: TreeRoot<RaTypes>> Eq for {{node}}Node<R> {}
+impl<R: TreeRoot<RaTypes>> Hash for {{node}}Node<R> {
+    fn hash<H: Hasher>(&self, state: &mut H) { self.syntax.hash(state) }
+}
+
 impl<'a> AstNode<'a> for {{ node }}<'a> {
     fn cast(syntax: SyntaxNodeRef<'a>) -> Option<Self> {
         match syntax.kind() {
index d93f926729d7b971f12cc5f9631b51c2f6dc83bb..7077e349223006fb9d3f98d1bf30126a24716412 100644 (file)
@@ -1,6 +1,7 @@
 mod generated;
 
 use std::marker::PhantomData;
+use std::string::String as RustString;
 
 use itertools::Itertools;
 
@@ -76,7 +77,7 @@ fn doc_comments(self) -> AstChildren<'a, Comment<'a>> {
 
     /// Returns the textual content of a doc comment block as a single string.
     /// That is, strips leading `///` and joins lines
-    fn doc_comment_text(self) -> String {
+    fn doc_comment_text(self) -> RustString {
         self.doc_comments()
             .map(|comment| {
                 let prefix = comment.prefix();
@@ -133,6 +134,24 @@ pub fn text(&self) -> &SmolStr {
     }
 }
 
+impl<'a> Byte<'a> {
+    pub fn text(&self) -> &SmolStr {
+        &self.syntax().leaf_text().unwrap()
+    }
+}
+
+impl<'a> ByteString<'a> {
+    pub fn text(&self) -> &SmolStr {
+        &self.syntax().leaf_text().unwrap()
+    }
+}
+
+impl<'a> String<'a> {
+    pub fn text(&self) -> &SmolStr {
+        &self.syntax().leaf_text().unwrap()
+    }
+}
+
 impl<'a> Comment<'a> {
     pub fn text(&self) -> &SmolStr {
         self.syntax().leaf_text().unwrap()
@@ -296,6 +315,15 @@ pub fn kind(self) -> Option<PathSegmentKind<'a>> {
     }
 }
 
+impl<'a> UseTreeList<'a> {
+    pub fn parent_use_tree(self) -> UseTree<'a> {
+        self.syntax()
+            .parent()
+            .and_then(UseTree::cast)
+            .expect("UseTreeLists are always nested in UseTrees")
+    }
+}
+
 fn child_opt<'a, P: AstNode<'a>, C: AstNode<'a>>(parent: P) -> Option<C> {
     children(parent).next()
 }
index 2ed165c5253edc0e8c8e9aa09a565296e140e863..53cd2118f3d4478ff9fecee9da5241745165230c 100644 (file)
@@ -116,7 +116,7 @@ Grammar(
         "SHEBANG",
     ],
     nodes: [
-        "ROOT",
+        "SOURCE_FILE",
 
         "STRUCT_DEF",
         "ENUM_DEF",
@@ -239,7 +239,7 @@ Grammar(
         "ARG_LIST",
     ],
     ast: {
-        "Root": (
+        "SourceFile": (
             traits: [ "ModuleItemOwner", "FnDefOwner" ],
             collections: [
                 ["modules", "Module"],
@@ -260,6 +260,7 @@ Grammar(
                 "NameOwner",
                 "TypeParamsOwner",
                 "AttrsOwner",
+                "DocCommentsOwner"
             ],
             collections: [
                 ["fields", "NamedFieldDef"]
@@ -270,10 +271,11 @@ Grammar(
             "NameOwner",
             "TypeParamsOwner",
             "AttrsOwner",
+            "DocCommentsOwner"
         ] ),
-        "TraitDef": ( traits: ["NameOwner", "AttrsOwner"] ),
+        "TraitDef": ( traits: ["NameOwner", "AttrsOwner", "DocCommentsOwner"] ),
         "Module": (
-            traits: ["NameOwner", "AttrsOwner" ],
+            traits: ["NameOwner", "AttrsOwner", "DocCommentsOwner" ],
             options: [ "ItemList" ]
         ),
         "ItemList": (
@@ -283,16 +285,19 @@ Grammar(
             "NameOwner",
             "TypeParamsOwner",
             "AttrsOwner",
+            "DocCommentsOwner"
         ] ),
         "StaticDef": ( traits: [
             "NameOwner",
             "TypeParamsOwner",
             "AttrsOwner",
+            "DocCommentsOwner"
         ] ),
         "TypeDef": ( traits: [
             "NameOwner",
             "TypeParamsOwner",
             "AttrsOwner",
+            "DocCommentsOwner"
         ] ),
         "ImplItem": (),
 
@@ -406,6 +411,9 @@ Grammar(
         "PrefixExpr": (),
         "RangeExpr": (),
         "BinExpr": (),
+        "String": (),
+        "Byte": (),
+        "ByteString": (),
         "Char": (),
         "Literal": (),
 
index 95c437983330cc11a568a1bbd9b920b30c0d7c08..06a37d648ed577d5e914fec88ec46021a6107461 100644 (file)
@@ -53,7 +53,7 @@ pub(crate) fn root(p: &mut Parser) {
     let m = p.start();
     p.eat(SHEBANG);
     items::mod_contents(p, false);
-    m.complete(p, ROOT);
+    m.complete(p, SOURCE_FILE);
 }
 
 #[derive(Clone, Copy, PartialEq, Eq)]
index 4c291b9c4dfe6fa4b59295d425bf395398ee0a86..7e4df51aa892de900e00f31f35cd6836fcbe52d2 100644 (file)
@@ -30,8 +30,7 @@ pub fn current(&self) -> Option<char> {
     /// Gets the nth character from the current.
     /// For example, 0 will return the current token, 1 will return the next, etc.
     pub fn nth(&self, n: u32) -> Option<char> {
-        let mut chars = self.chars().peekable();
-        chars.by_ref().nth(n as usize)
+        self.chars().nth(n as usize)
     }
 
     /// Checks whether the current character is `c`.
index 1230028257b184545eba5ea5840e845db2a041f3..330f680533fec910c1ccd07b0a51b41532a46226 100644 (file)
@@ -20,6 +20,7 @@
 #![allow(missing_docs)]
 //#![warn(unreachable_pub)] // rust-lang/rust#47816
 
+extern crate arrayvec;
 extern crate drop_bomb;
 extern crate itertools;
 extern crate parking_lot;
 
 use crate::yellow::GreenNode;
 
-/// File represents a parse tree for a single Rust file.
-#[derive(Clone, Debug, Hash, PartialEq, Eq)]
-pub struct File {
-    root: SyntaxNode,
-}
+/// `SourceFileNode` represents a parse tree for a single Rust file.
+pub use crate::ast::SourceFileNode;
 
-impl File {
-    fn new(green: GreenNode, errors: Vec<SyntaxError>) -> File {
+impl SourceFileNode {
+    fn new(green: GreenNode, errors: Vec<SyntaxError>) -> SourceFileNode {
         let root = SyntaxNode::new(green, errors);
         if cfg!(debug_assertions) {
             utils::validate_block_structure(root.borrowed());
         }
-        File { root }
+        assert_eq!(root.kind(), SyntaxKind::SOURCE_FILE);
+        ast::SourceFileNode { syntax: root }
     }
-    pub fn parse(text: &str) -> File {
+    pub fn parse(text: &str) -> SourceFileNode {
         let tokens = tokenize(&text);
         let (green, errors) =
             parser_impl::parse_with(yellow::GreenBuilder::new(), text, &tokens, grammar::root);
-        File::new(green, errors)
+        SourceFileNode::new(green, errors)
     }
-    pub fn reparse(&self, edit: &AtomEdit) -> File {
+    pub fn reparse(&self, edit: &AtomEdit) -> SourceFileNode {
         self.incremental_reparse(edit)
             .unwrap_or_else(|| self.full_reparse(edit))
     }
-    pub fn incremental_reparse(&self, edit: &AtomEdit) -> Option<File> {
+    pub fn incremental_reparse(&self, edit: &AtomEdit) -> Option<SourceFileNode> {
         reparsing::incremental_reparse(self.syntax(), edit, self.errors())
-            .map(|(green_node, errors)| File::new(green_node, errors))
+            .map(|(green_node, errors)| SourceFileNode::new(green_node, errors))
     }
-    fn full_reparse(&self, edit: &AtomEdit) -> File {
+    fn full_reparse(&self, edit: &AtomEdit) -> SourceFileNode {
         let text =
             text_utils::replace_range(self.syntax().text().to_string(), edit.delete, &edit.insert);
-        File::parse(&text)
+        SourceFileNode::parse(&text)
     }
     /// Typed AST representation of the parse tree.
-    pub fn ast(&self) -> ast::Root {
-        ast::Root::cast(self.syntax()).unwrap()
+    pub fn ast(&self) -> ast::SourceFile {
+        self.borrowed()
     }
     /// Untyped homogeneous representation of the parse tree.
     pub fn syntax(&self) -> SyntaxNodeRef {
-        self.root.borrowed()
+        self.syntax.borrowed()
     }
     pub fn errors(&self) -> Vec<SyntaxError> {
-        let mut errors = self.root.root_data().clone();
+        let mut errors = self.syntax.root_data().clone();
         errors.extend(validation::validate(self));
         errors
     }
index bf9c1cef09554526e2ac58079500ddeceeb29b95..3d8b062d5950aeb6c2f068a9e77510b3714439b1 100644 (file)
@@ -172,7 +172,7 @@ fn tombstone() -> Event {
     }
 
     fn start(&mut self, kind: SyntaxKind) {
-        if kind == ROOT {
+        if kind == SOURCE_FILE {
             self.sink.start_internal(kind);
             return;
         }
index 3c4ea5c22b7600618e803826305c2314ebfead16..d48133166c7b5cb9a87d2a1b759101b4571ece9d 100644 (file)
@@ -180,7 +180,7 @@ fn merge_errors(
 #[cfg(test)]
 mod tests {
     use super::{
-        super::{test_utils::extract_range, text_utils::replace_range, utils::dump_tree, File},
+        super::{test_utils::extract_range, text_utils::replace_range, utils::dump_tree, SourceFileNode},
         reparse_block, reparse_leaf, AtomEdit, GreenNode, SyntaxError, SyntaxNodeRef,
 };
 
@@ -192,9 +192,9 @@ fn do_check<F>(before: &str, replace_with: &str, reparser: F)
         let (range, before) = extract_range(before);
         let after = replace_range(before.clone(), range, replace_with);
 
-        let fully_reparsed = File::parse(&after);
+        let fully_reparsed = SourceFileNode::parse(&after);
         let incrementally_reparsed = {
-            let f = File::parse(&before);
+            let f = SourceFileNode::parse(&before);
             let edit = AtomEdit {
                 delete: range,
                 insert: replace_with.to_string(),
@@ -203,7 +203,7 @@ fn do_check<F>(before: &str, replace_with: &str, reparser: F)
                 reparser(f.syntax(), &edit).expect("cannot incrementally reparse");
             let green_root = node.replace_with(green);
             let errors = super::merge_errors(f.errors(), new_errors, node, &edit);
-            File::new(green_root, errors)
+            SourceFileNode::new(green_root, errors)
         };
 
         assert_eq_text!(
diff --git a/crates/ra_syntax/src/string_lexing/byte.rs b/crates/ra_syntax/src/string_lexing/byte.rs
new file mode 100644 (file)
index 0000000..2442434
--- /dev/null
@@ -0,0 +1,51 @@
+use super::parser::Parser;
+use super::CharComponent;
+
+pub fn parse_byte_literal(src: &str) -> ByteComponentIterator {
+    ByteComponentIterator {
+        parser: Parser::new(src),
+        has_closing_quote: false,
+    }
+}
+
+pub struct ByteComponentIterator<'a> {
+    parser: Parser<'a>,
+    pub has_closing_quote: bool,
+}
+
+impl<'a> Iterator for ByteComponentIterator<'a> {
+    type Item = CharComponent;
+    fn next(&mut self) -> Option<CharComponent> {
+        if self.parser.pos == 0 {
+            assert!(
+                self.parser.advance() == 'b',
+                "Byte literal should start with a `b`"
+            );
+
+            assert!(
+                self.parser.advance() == '\'',
+                "Byte literal should start with a `b`, followed by a quote"
+            );
+        }
+
+        if let Some(component) = self.parser.parse_char_component() {
+            return Some(component);
+        }
+
+        // We get here when there are no char components left to parse
+        if self.parser.peek() == Some('\'') {
+            self.parser.advance();
+            self.has_closing_quote = true;
+        }
+
+        assert!(
+            self.parser.peek() == None,
+            "byte literal should leave no unparsed input: src = {}, pos = {}, length = {}",
+            self.parser.src,
+            self.parser.pos,
+            self.parser.src.len()
+        );
+
+        None
+    }
+}
diff --git a/crates/ra_syntax/src/string_lexing/byte_string.rs b/crates/ra_syntax/src/string_lexing/byte_string.rs
new file mode 100644 (file)
index 0000000..5b6dda7
--- /dev/null
@@ -0,0 +1,51 @@
+use super::parser::Parser;
+use super::StringComponent;
+
+pub fn parse_byte_string_literal(src: &str) -> ByteStringComponentIterator {
+    ByteStringComponentIterator {
+        parser: Parser::new(src),
+        has_closing_quote: false,
+    }
+}
+
+pub struct ByteStringComponentIterator<'a> {
+    parser: Parser<'a>,
+    pub has_closing_quote: bool,
+}
+
+impl<'a> Iterator for ByteStringComponentIterator<'a> {
+    type Item = StringComponent;
+    fn next(&mut self) -> Option<StringComponent> {
+        if self.parser.pos == 0 {
+            assert!(
+                self.parser.advance() == 'b',
+                "byte string literal should start with a `b`"
+            );
+
+            assert!(
+                self.parser.advance() == '"',
+                "byte string literal should start with a `b`, followed by double quotes"
+            );
+        }
+
+        if let Some(component) = self.parser.parse_string_component() {
+            return Some(component);
+        }
+
+        // We get here when there are no char components left to parse
+        if self.parser.peek() == Some('"') {
+            self.parser.advance();
+            self.has_closing_quote = true;
+        }
+
+        assert!(
+            self.parser.peek() == None,
+            "byte string literal should leave no unparsed input: src = {}, pos = {}, length = {}",
+            self.parser.src,
+            self.parser.pos,
+            self.parser.src.len()
+        );
+
+        None
+    }
+}
diff --git a/crates/ra_syntax/src/string_lexing/char.rs b/crates/ra_syntax/src/string_lexing/char.rs
new file mode 100644 (file)
index 0000000..885c03b
--- /dev/null
@@ -0,0 +1,176 @@
+use super::parser::Parser;
+use super::CharComponent;
+
+pub fn parse_char_literal(src: &str) -> CharComponentIterator {
+    CharComponentIterator {
+        parser: Parser::new(src),
+        has_closing_quote: false,
+    }
+}
+
+pub struct CharComponentIterator<'a> {
+    parser: Parser<'a>,
+    pub has_closing_quote: bool,
+}
+
+impl<'a> Iterator for CharComponentIterator<'a> {
+    type Item = CharComponent;
+    fn next(&mut self) -> Option<CharComponent> {
+        if self.parser.pos == 0 {
+            assert!(
+                self.parser.advance() == '\'',
+                "char literal should start with a quote"
+            );
+        }
+
+        if let Some(component) = self.parser.parse_char_component() {
+            return Some(component);
+        }
+
+        // We get here when there are no char components left to parse
+        if self.parser.peek() == Some('\'') {
+            self.parser.advance();
+            self.has_closing_quote = true;
+        }
+
+        assert!(
+            self.parser.peek() == None,
+            "char literal should leave no unparsed input: src = {}, pos = {}, length = {}",
+            self.parser.src,
+            self.parser.pos,
+            self.parser.src.len()
+        );
+
+        None
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use rowan::TextRange;
+    use crate::string_lexing::{
+        CharComponent,
+        CharComponentKind::*,
+};
+
+    fn parse(src: &str) -> (bool, Vec<CharComponent>) {
+        let component_iterator = &mut super::parse_char_literal(src);
+        let components: Vec<_> = component_iterator.collect();
+        (component_iterator.has_closing_quote, components)
+    }
+
+    fn unclosed_char_component(src: &str) -> CharComponent {
+        let (has_closing_quote, components) = parse(src);
+        assert!(!has_closing_quote, "char should not have closing quote");
+        assert!(components.len() == 1);
+        components[0].clone()
+    }
+
+    fn closed_char_component(src: &str) -> CharComponent {
+        let (has_closing_quote, components) = parse(src);
+        assert!(has_closing_quote, "char should have closing quote");
+        assert!(
+            components.len() == 1,
+            "Literal: {}\nComponents: {:#?}",
+            src,
+            components
+        );
+        components[0].clone()
+    }
+
+    fn closed_char_components(src: &str) -> Vec<CharComponent> {
+        let (has_closing_quote, components) = parse(src);
+        assert!(has_closing_quote, "char should have closing quote");
+        components
+    }
+
+    fn range_closed(src: &str) -> TextRange {
+        TextRange::from_to(1.into(), (src.len() as u32 - 1).into())
+    }
+
+    fn range_unclosed(src: &str) -> TextRange {
+        TextRange::from_to(1.into(), (src.len() as u32).into())
+    }
+
+    #[test]
+    fn test_unicode_escapes() {
+        let unicode_escapes = &[r"{DEAD}", "{BEEF}", "{FF}", "{}", ""];
+        for escape in unicode_escapes {
+            let escape_sequence = format!(r"'\u{}'", escape);
+            let component = closed_char_component(&escape_sequence);
+            let expected_range = range_closed(&escape_sequence);
+            assert_eq!(component.kind, UnicodeEscape);
+            assert_eq!(component.range, expected_range);
+        }
+    }
+
+    #[test]
+    fn test_unicode_escapes_unclosed() {
+        let unicode_escapes = &["{DEAD", "{BEEF", "{FF"];
+        for escape in unicode_escapes {
+            let escape_sequence = format!(r"'\u{}'", escape);
+            let component = unclosed_char_component(&escape_sequence);
+            let expected_range = range_unclosed(&escape_sequence);
+            assert_eq!(component.kind, UnicodeEscape);
+            assert_eq!(component.range, expected_range);
+        }
+    }
+
+    #[test]
+    fn test_empty_char() {
+        let (has_closing_quote, components) = parse("''");
+        assert!(has_closing_quote, "char should have closing quote");
+        assert!(components.len() == 0);
+    }
+
+    #[test]
+    fn test_unclosed_char() {
+        let component = unclosed_char_component("'a");
+        assert!(component.kind == CodePoint);
+        assert!(component.range == TextRange::from_to(1.into(), 2.into()));
+    }
+
+    #[test]
+    fn test_digit_escapes() {
+        let literals = &[r"", r"5", r"55"];
+
+        for literal in literals {
+            let lit_text = format!(r"'\x{}'", literal);
+            let component = closed_char_component(&lit_text);
+            assert!(component.kind == AsciiCodeEscape);
+            assert!(component.range == range_closed(&lit_text));
+        }
+
+        // More than 2 digits starts a new codepoint
+        let components = closed_char_components(r"'\x555'");
+        assert!(components.len() == 2);
+        assert!(components[1].kind == CodePoint);
+    }
+
+    #[test]
+    fn test_ascii_escapes() {
+        let literals = &[
+            r"\'", "\\\"", // equivalent to \"
+            r"\n", r"\r", r"\t", r"\\", r"\0",
+        ];
+
+        for literal in literals {
+            let lit_text = format!("'{}'", literal);
+            let component = closed_char_component(&lit_text);
+            assert!(component.kind == AsciiEscape);
+            assert!(component.range == range_closed(&lit_text));
+        }
+    }
+
+    #[test]
+    fn test_no_escapes() {
+        let literals = &['"', 'n', 'r', 't', '0', 'x', 'u'];
+
+        for &literal in literals {
+            let lit_text = format!("'{}'", literal);
+            let component = closed_char_component(&lit_text);
+            assert!(component.kind == CodePoint);
+            assert!(component.range == range_closed(&lit_text));
+        }
+    }
+}
index f0812ff289d6d205c4b760bfc5946041dd9ff784..94853331f2e89417b4767bbfddaec0b104cd01cf 100644 (file)
-use self::CharComponentKind::*;
-use rowan::{TextRange, TextUnit};
-
-pub fn parse_char_literal(src: &str) -> CharComponentIterator {
-    CharComponentIterator {
-        parser: Parser::new(src),
-        has_closing_quote: false,
-    }
-}
-
-#[derive(Debug, Eq, PartialEq, Clone)]
-pub struct CharComponent {
-    pub range: TextRange,
-    pub kind: CharComponentKind,
-}
-
-impl CharComponent {
-    fn new(range: TextRange, kind: CharComponentKind) -> CharComponent {
-        CharComponent { range, kind }
-    }
-}
-
-#[derive(Debug, Eq, PartialEq, Clone)]
-pub enum CharComponentKind {
-    CodePoint,
-    AsciiEscape,
-    AsciiCodeEscape,
-    UnicodeEscape,
-}
-
-pub struct CharComponentIterator<'a> {
-    parser: Parser<'a>,
-    pub has_closing_quote: bool,
-}
-
-impl<'a> Iterator for CharComponentIterator<'a> {
-    type Item = CharComponent;
-    fn next(&mut self) -> Option<CharComponent> {
-        if self.parser.pos == 0 {
-            assert!(
-                self.parser.advance() == '\'',
-                "char literal should start with a quote"
-            );
-        }
-
-        if let Some(component) = self.parser.parse_char_component() {
-            return Some(component);
-        }
-
-        // We get here when there are no char components left to parse
-        if self.parser.peek() == Some('\'') {
-            self.parser.advance();
-            self.has_closing_quote = true;
-        }
-
-        assert!(
-            self.parser.peek() == None,
-            "char literal should leave no unparsed input: src = {}, pos = {}, length = {}",
-            self.parser.src,
-            self.parser.pos,
-            self.parser.src.len()
-        );
-
-        None
-    }
-}
-
-pub struct Parser<'a> {
-    src: &'a str,
-    pos: usize,
-}
-
-impl<'a> Parser<'a> {
-    pub fn new(src: &'a str) -> Parser<'a> {
-        Parser { src, pos: 0 }
-    }
-
-    // Utility methods
-
-    pub fn peek(&self) -> Option<char> {
-        if self.pos == self.src.len() {
-            return None;
-        }
-
-        self.src[self.pos..].chars().next()
-    }
-
-    pub fn advance(&mut self) -> char {
-        let next = self
-            .peek()
-            .expect("cannot advance if end of input is reached");
-        self.pos += next.len_utf8();
-        next
-    }
-
-    pub fn get_pos(&self) -> TextUnit {
-        (self.pos as u32).into()
-    }
-
-    // Char parsing methods
-
-    fn parse_unicode_escape(&mut self, start: TextUnit) -> CharComponent {
-        match self.peek() {
-            Some('{') => {
-                self.advance();
-
-                // Parse anything until we reach `}`
-                while let Some(next) = self.peek() {
-                    self.advance();
-                    if next == '}' {
-                        break;
-                    }
-                }
-
-                let end = self.get_pos();
-                CharComponent::new(TextRange::from_to(start, end), UnicodeEscape)
-            }
-            Some(_) | None => {
-                let end = self.get_pos();
-                CharComponent::new(TextRange::from_to(start, end), UnicodeEscape)
-            }
-        }
-    }
-
-    fn parse_ascii_code_escape(&mut self, start: TextUnit) -> CharComponent {
-        let code_start = self.get_pos();
-        while let Some(next) = self.peek() {
-            if next == '\'' || (self.get_pos() - code_start == 2.into()) {
-                break;
-            }
-
-            self.advance();
-        }
-
-        let end = self.get_pos();
-        CharComponent::new(TextRange::from_to(start, end), AsciiCodeEscape)
-    }
-
-    fn parse_escape(&mut self, start: TextUnit) -> CharComponent {
-        if self.peek().is_none() {
-            return CharComponent::new(TextRange::from_to(start, start), AsciiEscape);
-        }
-
-        let next = self.advance();
-        let end = self.get_pos();
-        let range = TextRange::from_to(start, end);
-        match next {
-            'x' => self.parse_ascii_code_escape(start),
-            'u' => self.parse_unicode_escape(start),
-            _ => CharComponent::new(range, AsciiEscape),
-        }
-    }
-
-    pub fn parse_char_component(&mut self) -> Option<CharComponent> {
-        let next = self.peek()?;
-
-        // Ignore character close
-        if next == '\'' {
-            return None;
-        }
-
-        let start = self.get_pos();
-        self.advance();
-
-        if next == '\\' {
-            Some(self.parse_escape(start))
-        } else {
-            let end = self.get_pos();
-            Some(CharComponent::new(
-                TextRange::from_to(start, end),
-                CodePoint,
-            ))
-        }
-    }
-}
-
-#[cfg(test)]
-mod tests {
-    use super::*;
-
-    fn parse(src: &str) -> (bool, Vec<CharComponent>) {
-        let component_iterator = &mut super::parse_char_literal(src);
-        let components: Vec<_> = component_iterator.collect();
-        (component_iterator.has_closing_quote, components)
-    }
-
-    fn unclosed_char_component(src: &str) -> CharComponent {
-        let (has_closing_quote, components) = parse(src);
-        assert!(!has_closing_quote, "char should not have closing quote");
-        assert!(components.len() == 1);
-        components[0].clone()
-    }
-
-    fn closed_char_component(src: &str) -> CharComponent {
-        let (has_closing_quote, components) = parse(src);
-        assert!(has_closing_quote, "char should have closing quote");
-        assert!(
-            components.len() == 1,
-            "Literal: {}\nComponents: {:#?}",
-            src,
-            components
-        );
-        components[0].clone()
-    }
-
-    fn closed_char_components(src: &str) -> Vec<CharComponent> {
-        let (has_closing_quote, components) = parse(src);
-        assert!(has_closing_quote, "char should have closing quote");
-        components
-    }
-
-    fn range_closed(src: &str) -> TextRange {
-        TextRange::from_to(1.into(), (src.len() as u32 - 1).into())
-    }
-
-    fn range_unclosed(src: &str) -> TextRange {
-        TextRange::from_to(1.into(), (src.len() as u32).into())
-    }
-
-    #[test]
-    fn test_unicode_escapes() {
-        let unicode_escapes = &[r"{DEAD}", "{BEEF}", "{FF}", ""];
-        for escape in unicode_escapes {
-            let escape_sequence = format!(r"'\u{}'", escape);
-            let component = closed_char_component(&escape_sequence);
-            let expected_range = range_closed(&escape_sequence);
-            assert_eq!(component.kind, CharComponentKind::UnicodeEscape);
-            assert_eq!(component.range, expected_range);
-        }
-    }
-
-    #[test]
-    fn test_unicode_escapes_unclosed() {
-        let unicode_escapes = &["{DEAD", "{BEEF", "{FF"];
-        for escape in unicode_escapes {
-            let escape_sequence = format!(r"'\u{}'", escape);
-            let component = unclosed_char_component(&escape_sequence);
-            let expected_range = range_unclosed(&escape_sequence);
-            assert_eq!(component.kind, CharComponentKind::UnicodeEscape);
-            assert_eq!(component.range, expected_range);
-        }
-    }
-
-    #[test]
-    fn test_empty_char() {
-        let (has_closing_quote, components) = parse("''");
-        assert!(has_closing_quote, "char should have closing quote");
-        assert!(components.len() == 0);
-    }
-
-    #[test]
-    fn test_unclosed_char() {
-        let component = unclosed_char_component("'a");
-        assert!(component.kind == CodePoint);
-        assert!(component.range == TextRange::from_to(1.into(), 2.into()));
-    }
-
-    #[test]
-    fn test_digit_escapes() {
-        let literals = &[r"", r"5", r"55"];
-
-        for literal in literals {
-            let lit_text = format!(r"'\x{}'", literal);
-            let component = closed_char_component(&lit_text);
-            assert!(component.kind == CharComponentKind::AsciiCodeEscape);
-            assert!(component.range == range_closed(&lit_text));
-        }
-
-        // More than 2 digits starts a new codepoint
-        let components = closed_char_components(r"'\x555'");
-        assert!(components.len() == 2);
-        assert!(components[1].kind == CharComponentKind::CodePoint);
-    }
-
-    #[test]
-    fn test_ascii_escapes() {
-        let literals = &[
-            r"\'", "\\\"", // equivalent to \"
-            r"\n", r"\r", r"\t", r"\\", r"\0",
-        ];
-
-        for literal in literals {
-            let lit_text = format!("'{}'", literal);
-            let component = closed_char_component(&lit_text);
-            assert!(component.kind == CharComponentKind::AsciiEscape);
-            assert!(component.range == range_closed(&lit_text));
-        }
-    }
-
-    #[test]
-    fn test_no_escapes() {
-        let literals = &['"', 'n', 'r', 't', '0', 'x', 'u'];
-
-        for &literal in literals {
-            let lit_text = format!("'{}'", literal);
-            let component = closed_char_component(&lit_text);
-            assert!(component.kind == CharComponentKind::CodePoint);
-            assert!(component.range == range_closed(&lit_text));
-        }
-    }
-}
+mod parser;
+mod byte;
+mod byte_string;
+mod char;
+mod string;
+
+pub use self::{
+    byte::parse_byte_literal,
+    byte_string::parse_byte_string_literal,
+    char::parse_char_literal,
+    parser::{CharComponent, CharComponentKind, StringComponent, StringComponentKind},
+    string::parse_string_literal,
+};
diff --git a/crates/ra_syntax/src/string_lexing/parser.rs b/crates/ra_syntax/src/string_lexing/parser.rs
new file mode 100644 (file)
index 0000000..4a6d5bc
--- /dev/null
@@ -0,0 +1,201 @@
+use rowan::{TextRange, TextUnit};
+
+use self::CharComponentKind::*;
+
+pub struct Parser<'a> {
+    pub(super) src: &'a str,
+    pub(super) pos: usize,
+}
+
+impl<'a> Parser<'a> {
+    pub fn new(src: &'a str) -> Parser<'a> {
+        Parser { src, pos: 0 }
+    }
+
+    // Utility methods
+
+    pub fn peek(&self) -> Option<char> {
+        if self.pos == self.src.len() {
+            return None;
+        }
+
+        self.src[self.pos..].chars().next()
+    }
+
+    pub fn advance(&mut self) -> char {
+        let next = self
+            .peek()
+            .expect("cannot advance if end of input is reached");
+        self.pos += next.len_utf8();
+        next
+    }
+
+    pub fn skip_whitespace(&mut self) {
+        while self.peek().map(|c| c.is_whitespace()) == Some(true) {
+            self.advance();
+        }
+    }
+
+    pub fn get_pos(&self) -> TextUnit {
+        (self.pos as u32).into()
+    }
+
+    // Char parsing methods
+
+    fn parse_unicode_escape(&mut self, start: TextUnit) -> CharComponent {
+        match self.peek() {
+            Some('{') => {
+                self.advance();
+
+                // Parse anything until we reach `}`
+                while let Some(next) = self.peek() {
+                    self.advance();
+                    if next == '}' {
+                        break;
+                    }
+                }
+
+                let end = self.get_pos();
+                CharComponent::new(TextRange::from_to(start, end), UnicodeEscape)
+            }
+            Some(_) | None => {
+                let end = self.get_pos();
+                CharComponent::new(TextRange::from_to(start, end), UnicodeEscape)
+            }
+        }
+    }
+
+    fn parse_ascii_code_escape(&mut self, start: TextUnit) -> CharComponent {
+        let code_start = self.get_pos();
+        while let Some(next) = self.peek() {
+            if next == '\'' || (self.get_pos() - code_start == 2.into()) {
+                break;
+            }
+
+            self.advance();
+        }
+
+        let end = self.get_pos();
+        CharComponent::new(TextRange::from_to(start, end), AsciiCodeEscape)
+    }
+
+    fn parse_escape(&mut self, start: TextUnit) -> CharComponent {
+        if self.peek().is_none() {
+            return CharComponent::new(TextRange::from_to(start, start), AsciiEscape);
+        }
+
+        let next = self.advance();
+        let end = self.get_pos();
+        let range = TextRange::from_to(start, end);
+        match next {
+            'x' => self.parse_ascii_code_escape(start),
+            'u' => self.parse_unicode_escape(start),
+            _ => CharComponent::new(range, AsciiEscape),
+        }
+    }
+
+    pub fn parse_char_component(&mut self) -> Option<CharComponent> {
+        let next = self.peek()?;
+
+        // Ignore character close
+        if next == '\'' {
+            return None;
+        }
+
+        let start = self.get_pos();
+        self.advance();
+
+        if next == '\\' {
+            Some(self.parse_escape(start))
+        } else {
+            let end = self.get_pos();
+            Some(CharComponent::new(
+                TextRange::from_to(start, end),
+                CodePoint,
+            ))
+        }
+    }
+
+    pub fn parse_ignore_newline(&mut self, start: TextUnit) -> Option<StringComponent> {
+        // In string literals, when a `\` occurs immediately before the newline, the `\`,
+        // the newline, and all whitespace at the beginning of the next line are ignored
+        match self.peek() {
+            Some('\n') | Some('\r') => {
+                self.skip_whitespace();
+                Some(StringComponent::new(
+                    TextRange::from_to(start, self.get_pos()),
+                    StringComponentKind::IgnoreNewline,
+                ))
+            }
+            _ => None,
+        }
+    }
+
+    pub fn parse_string_component(&mut self) -> Option<StringComponent> {
+        let next = self.peek()?;
+
+        // Ignore string close
+        if next == '"' {
+            return None;
+        }
+
+        let start = self.get_pos();
+        self.advance();
+
+        if next == '\\' {
+            // Strings can use `\` to ignore newlines, so we first try to parse one of those
+            // before falling back to parsing char escapes
+            self.parse_ignore_newline(start).or_else(|| {
+                let char_component = self.parse_escape(start);
+                Some(StringComponent::new(
+                    char_component.range,
+                    StringComponentKind::Char(char_component.kind),
+                ))
+            })
+        } else {
+            let end = self.get_pos();
+            Some(StringComponent::new(
+                TextRange::from_to(start, end),
+                StringComponentKind::Char(CodePoint),
+            ))
+        }
+    }
+}
+
+#[derive(Debug, Eq, PartialEq, Clone)]
+pub struct StringComponent {
+    pub range: TextRange,
+    pub kind: StringComponentKind,
+}
+
+impl StringComponent {
+    fn new(range: TextRange, kind: StringComponentKind) -> StringComponent {
+        StringComponent { range, kind }
+    }
+}
+
+#[derive(Debug, Eq, PartialEq, Clone)]
+pub enum StringComponentKind {
+    IgnoreNewline,
+    Char(CharComponentKind),
+}
+
+#[derive(Debug, Eq, PartialEq, Clone)]
+pub struct CharComponent {
+    pub range: TextRange,
+    pub kind: CharComponentKind,
+}
+
+impl CharComponent {
+    fn new(range: TextRange, kind: CharComponentKind) -> CharComponent {
+        CharComponent { range, kind }
+    }
+}
+
+#[derive(Debug, Eq, PartialEq, Clone)]
+pub enum CharComponentKind {
+    CodePoint,
+    AsciiEscape,
+    AsciiCodeEscape,
+    UnicodeEscape,
+}
diff --git a/crates/ra_syntax/src/string_lexing/string.rs b/crates/ra_syntax/src/string_lexing/string.rs
new file mode 100644 (file)
index 0000000..1b23029
--- /dev/null
@@ -0,0 +1,46 @@
+use super::parser::Parser;
+use super::StringComponent;
+
+pub fn parse_string_literal(src: &str) -> StringComponentIterator {
+    StringComponentIterator {
+        parser: Parser::new(src),
+        has_closing_quote: false,
+    }
+}
+
+pub struct StringComponentIterator<'a> {
+    parser: Parser<'a>,
+    pub has_closing_quote: bool,
+}
+
+impl<'a> Iterator for StringComponentIterator<'a> {
+    type Item = StringComponent;
+    fn next(&mut self) -> Option<StringComponent> {
+        if self.parser.pos == 0 {
+            assert!(
+                self.parser.advance() == '"',
+                "string literal should start with double quotes"
+            );
+        }
+
+        if let Some(component) = self.parser.parse_string_component() {
+            return Some(component);
+        }
+
+        // We get here when there are no char components left to parse
+        if self.parser.peek() == Some('"') {
+            self.parser.advance();
+            self.has_closing_quote = true;
+        }
+
+        assert!(
+            self.parser.peek() == None,
+            "string literal should leave no unparsed input: src = {}, pos = {}, length = {}",
+            self.parser.src,
+            self.parser.pos,
+            self.parser.src.len()
+        );
+
+        None
+    }
+}
index 6568f1a3729747903e0ad8125b7e80f4e49d3ac3..c43a8bf653878a36deb33658bebba43e00e13615 100644 (file)
@@ -117,7 +117,7 @@ pub enum SyntaxKind {
     RAW_BYTE_STRING,
     COMMENT,
     SHEBANG,
-    ROOT,
+    SOURCE_FILE,
     STRUCT_DEF,
     ENUM_DEF,
     FN_DEF,
@@ -378,7 +378,7 @@ pub(crate) fn info(self) -> &'static SyntaxInfo {
             RAW_BYTE_STRING => &SyntaxInfo { name: "RAW_BYTE_STRING" },
             COMMENT => &SyntaxInfo { name: "COMMENT" },
             SHEBANG => &SyntaxInfo { name: "SHEBANG" },
-            ROOT => &SyntaxInfo { name: "ROOT" },
+            SOURCE_FILE => &SyntaxInfo { name: "SOURCE_FILE" },
             STRUCT_DEF => &SyntaxInfo { name: "STRUCT_DEF" },
             ENUM_DEF => &SyntaxInfo { name: "ENUM_DEF" },
             FN_DEF => &SyntaxInfo { name: "FN_DEF" },
index 288d7edd4e8eb684219f1f55317177e9680cb6af..5bbdf80bb75f19a55f9c6cd7560ae9eda28aa482 100644 (file)
@@ -1,5 +1,6 @@
-use crate::{File, SyntaxKind, SyntaxNodeRef, WalkEvent};
+use crate::{SourceFileNode, SyntaxKind, SyntaxNodeRef, WalkEvent};
 use std::fmt::Write;
+use std::str;
 
 /// Parse a file and create a string representation of the resulting parse tree.
 pub fn dump_tree(syntax: SyntaxNodeRef) -> String {
@@ -44,7 +45,7 @@ macro_rules! indent {
 }
 
 pub fn check_fuzz_invariants(text: &str) {
-    let file = File::parse(text);
+    let file = SourceFileNode::parse(text);
     let root = file.syntax();
     validate_block_structure(root);
     let _ = file.ast();
diff --git a/crates/ra_syntax/src/validation.rs b/crates/ra_syntax/src/validation.rs
deleted file mode 100644 (file)
index 009f505..0000000
+++ /dev/null
@@ -1,78 +0,0 @@
-use crate::{
-    algo::visit::{visitor_ctx, VisitorCtx},
-    ast::{self, AstNode},
-    File,
-    string_lexing::{self, CharComponentKind},
-    yellow::{
-        SyntaxError,
-        SyntaxErrorKind::*,
-    },
-};
-
-pub(crate) fn validate(file: &File) -> Vec<SyntaxError> {
-    let mut errors = Vec::new();
-    for node in file.root.borrowed().descendants() {
-        let _ = visitor_ctx(&mut errors)
-            .visit::<ast::Char, _>(validate_char)
-            .accept(node);
-    }
-    errors
-}
-
-fn validate_char(node: ast::Char, errors: &mut Vec<SyntaxError>) {
-    let mut components = string_lexing::parse_char_literal(node.text());
-    let mut len = 0;
-    for component in &mut components {
-        len += 1;
-
-        // Validate escapes
-        let text = &node.text()[component.range];
-        let range = component.range + node.syntax().range().start();
-        use self::CharComponentKind::*;
-        match component.kind {
-            AsciiEscape => {
-                if text.len() == 1 {
-                    // Escape sequence consists only of leading `\`
-                    errors.push(SyntaxError::new(EmptyAsciiEscape, range));
-                } else {
-                    let escape_code = text.chars().skip(1).next().unwrap();
-                    if !is_ascii_escape(escape_code) {
-                        errors.push(SyntaxError::new(InvalidAsciiEscape, range));
-                    }
-                }
-            }
-            AsciiCodeEscape => {
-                // TODO:
-                // * First digit is octal
-                // * Second digit is hex
-            }
-            UnicodeEscape => {
-                // TODO:
-                // * Only hex digits or underscores allowed
-                // * Max 6 chars
-                // * Within allowed range (must be at most 10FFFF)
-            }
-            // Code points are always valid
-            CodePoint => (),
-        }
-    }
-
-    if !components.has_closing_quote {
-        errors.push(SyntaxError::new(UnclosedChar, node.syntax().range()));
-    }
-
-    if len == 0 {
-        errors.push(SyntaxError::new(EmptyChar, node.syntax().range()));
-    }
-
-    if len > 1 {
-        errors.push(SyntaxError::new(LongChar, node.syntax().range()));
-    }
-}
-
-fn is_ascii_escape(code: char) -> bool {
-    match code {
-        '\'' | '"' | 'n' | 'r' | 't' | '0' => true,
-        _ => false,
-    }
-}
diff --git a/crates/ra_syntax/src/validation/byte.rs b/crates/ra_syntax/src/validation/byte.rs
new file mode 100644 (file)
index 0000000..43c0d7e
--- /dev/null
@@ -0,0 +1,211 @@
+//! Validation of byte literals
+
+use crate::{
+    ast::{self, AstNode},
+    string_lexing::{self, CharComponentKind},
+    TextRange,
+    validation::char,
+    yellow::{
+        SyntaxError,
+        SyntaxErrorKind::*,
+    },
+};
+
+pub(super) fn validate_byte_node(node: ast::Byte, errors: &mut Vec<SyntaxError>) {
+    let literal_text = node.text();
+    let literal_range = node.syntax().range();
+    let mut components = string_lexing::parse_byte_literal(literal_text);
+    let mut len = 0;
+    for component in &mut components {
+        len += 1;
+        let text = &literal_text[component.range];
+        let range = component.range + literal_range.start();
+        validate_byte_component(text, component.kind, range, errors);
+    }
+
+    if !components.has_closing_quote {
+        errors.push(SyntaxError::new(UnclosedByte, literal_range));
+    }
+
+    if len == 0 {
+        errors.push(SyntaxError::new(EmptyByte, literal_range));
+    }
+
+    if len > 1 {
+        errors.push(SyntaxError::new(OverlongByte, literal_range));
+    }
+}
+
+pub(super) fn validate_byte_component(
+    text: &str,
+    kind: CharComponentKind,
+    range: TextRange,
+    errors: &mut Vec<SyntaxError>,
+) {
+    use self::CharComponentKind::*;
+    match kind {
+        AsciiEscape => validate_byte_escape(text, range, errors),
+        AsciiCodeEscape => validate_byte_code_escape(text, range, errors),
+        UnicodeEscape => errors.push(SyntaxError::new(UnicodeEscapeForbidden, range)),
+        CodePoint => {
+            let c = text
+                .chars()
+                .next()
+                .expect("Code points should be one character long");
+
+            // These bytes must always be escaped
+            if c == '\t' || c == '\r' || c == '\n' {
+                errors.push(SyntaxError::new(UnescapedByte, range));
+            }
+
+            // Only ASCII bytes are allowed
+            if c > 0x7F as char {
+                errors.push(SyntaxError::new(ByteOutOfRange, range));
+            }
+        }
+    }
+}
+
+fn validate_byte_escape(text: &str, range: TextRange, errors: &mut Vec<SyntaxError>) {
+    if text.len() == 1 {
+        // Escape sequence consists only of leading `\`
+        errors.push(SyntaxError::new(EmptyByteEscape, range));
+    } else {
+        let escape_code = text.chars().skip(1).next().unwrap();
+        if !char::is_ascii_escape(escape_code) {
+            errors.push(SyntaxError::new(InvalidByteEscape, range));
+        }
+    }
+}
+
+fn validate_byte_code_escape(text: &str, range: TextRange, errors: &mut Vec<SyntaxError>) {
+    // A ByteCodeEscape has 4 chars, example: `\xDD`
+    if text.len() < 4 {
+        errors.push(SyntaxError::new(TooShortByteCodeEscape, range));
+    } else {
+        assert!(
+            text.chars().count() == 4,
+            "ByteCodeEscape cannot be longer than 4 chars"
+        );
+
+        if u8::from_str_radix(&text[2..], 16).is_err() {
+            errors.push(SyntaxError::new(MalformedByteCodeEscape, range));
+        }
+    }
+}
+
+#[cfg(test)]
+mod test {
+    use crate::SourceFileNode;
+
+    fn build_file(literal: &str) -> SourceFileNode {
+        let src = format!("const C: u8 = b'{}';", literal);
+        SourceFileNode::parse(&src)
+    }
+
+    fn assert_valid_byte(literal: &str) {
+        let file = build_file(literal);
+        assert!(
+            file.errors().len() == 0,
+            "Errors for literal '{}': {:?}",
+            literal,
+            file.errors()
+        );
+    }
+
+    fn assert_invalid_byte(literal: &str) {
+        let file = build_file(literal);
+        assert!(file.errors().len() > 0);
+    }
+
+    #[test]
+    fn test_ansi_codepoints() {
+        for byte in 0..128 {
+            match byte {
+                b'\n' | b'\r' | b'\t' => assert_invalid_byte(&(byte as char).to_string()),
+                b'\'' | b'\\' => { /* Ignore character close and backslash */ }
+                _ => assert_valid_byte(&(byte as char).to_string()),
+            }
+        }
+
+        for byte in 128..=255u8 {
+            assert_invalid_byte(&(byte as char).to_string());
+        }
+    }
+
+    #[test]
+    fn test_unicode_codepoints() {
+        let invalid = ["Ƒ", "バ", "メ", "﷽"];
+        for c in &invalid {
+            assert_invalid_byte(c);
+        }
+    }
+
+    #[test]
+    fn test_unicode_multiple_codepoints() {
+        let invalid = ["नी", "👨‍👨‍"];
+        for c in &invalid {
+            assert_invalid_byte(c);
+        }
+    }
+
+    #[test]
+    fn test_valid_byte_escape() {
+        let valid = [r"\'", "\"", "\\\\", "\\\"", r"\n", r"\r", r"\t", r"\0"];
+        for c in &valid {
+            assert_valid_byte(c);
+        }
+    }
+
+    #[test]
+    fn test_invalid_byte_escape() {
+        let invalid = [r"\a", r"\?", r"\"];
+        for c in &invalid {
+            assert_invalid_byte(c);
+        }
+    }
+
+    #[test]
+    fn test_valid_byte_code_escape() {
+        let valid = [r"\x00", r"\x7F", r"\x55", r"\xF0"];
+        for c in &valid {
+            assert_valid_byte(c);
+        }
+    }
+
+    #[test]
+    fn test_invalid_byte_code_escape() {
+        let invalid = [r"\x", r"\x7"];
+        for c in &invalid {
+            assert_invalid_byte(c);
+        }
+    }
+
+    #[test]
+    fn test_invalid_unicode_escape() {
+        let well_formed = [
+            r"\u{FF}",
+            r"\u{0}",
+            r"\u{F}",
+            r"\u{10FFFF}",
+            r"\u{1_0__FF___FF_____}",
+        ];
+        for c in &well_formed {
+            assert_invalid_byte(c);
+        }
+
+        let invalid = [
+            r"\u",
+            r"\u{}",
+            r"\u{",
+            r"\u{FF",
+            r"\u{FFFFFF}",
+            r"\u{_F}",
+            r"\u{00FFFFF}",
+            r"\u{110000}",
+        ];
+        for c in &invalid {
+            assert_invalid_byte(c);
+        }
+    }
+}
diff --git a/crates/ra_syntax/src/validation/byte_string.rs b/crates/ra_syntax/src/validation/byte_string.rs
new file mode 100644 (file)
index 0000000..7b830e9
--- /dev/null
@@ -0,0 +1,178 @@
+use crate::{
+    ast::{self, AstNode},
+    string_lexing::{self, StringComponentKind},
+    yellow::{
+        SyntaxError,
+        SyntaxErrorKind::*,
+    },
+};
+
+use super::byte;
+
+pub(crate) fn validate_byte_string_node(node: ast::ByteString, errors: &mut Vec<SyntaxError>) {
+    let literal_text = node.text();
+    let literal_range = node.syntax().range();
+    let mut components = string_lexing::parse_byte_string_literal(literal_text);
+    for component in &mut components {
+        let range = component.range + literal_range.start();
+
+        match component.kind {
+            StringComponentKind::Char(kind) => {
+                // Chars must escape \t, \n and \r codepoints, but strings don't
+                let text = &literal_text[component.range];
+                match text {
+                    "\t" | "\n" | "\r" => { /* always valid */ }
+                    _ => byte::validate_byte_component(text, kind, range, errors),
+                }
+            }
+            StringComponentKind::IgnoreNewline => { /* always valid */ }
+        }
+    }
+
+    if !components.has_closing_quote {
+        errors.push(SyntaxError::new(UnclosedString, literal_range));
+    }
+}
+
+#[cfg(test)]
+mod test {
+    use crate::SourceFileNode;
+
+    fn build_file(literal: &str) -> SourceFileNode {
+        let src = format!(r#"const S: &'static [u8] = b"{}";"#, literal);
+        println!("Source: {}", src);
+        SourceFileNode::parse(&src)
+    }
+
+    fn assert_valid_str(literal: &str) {
+        let file = build_file(literal);
+        assert!(
+            file.errors().len() == 0,
+            "Errors for literal '{}': {:?}",
+            literal,
+            file.errors()
+        );
+    }
+
+    fn assert_invalid_str(literal: &str) {
+        let file = build_file(literal);
+        assert!(file.errors().len() > 0);
+    }
+
+    #[test]
+    fn test_ansi_codepoints() {
+        for byte in 0..128 {
+            match byte {
+                b'\"' | b'\\' => { /* Ignore string close and backslash */ }
+                _ => assert_valid_str(&(byte as char).to_string()),
+            }
+        }
+
+        for byte in 128..=255u8 {
+            assert_invalid_str(&(byte as char).to_string());
+        }
+    }
+
+    #[test]
+    fn test_unicode_codepoints() {
+        let invalid = ["Ƒ", "バ", "メ", "﷽"];
+        for c in &invalid {
+            assert_invalid_str(c);
+        }
+    }
+
+    #[test]
+    fn test_unicode_multiple_codepoints() {
+        let invalid = ["नी", "👨‍👨‍"];
+        for c in &invalid {
+            assert_invalid_str(c);
+        }
+    }
+
+    #[test]
+    fn test_valid_ascii_escape() {
+        let valid = [r"\'", r#"\""#, r"\\", r"\n", r"\r", r"\t", r"\0", "a", "b"];
+        for c in &valid {
+            assert_valid_str(c);
+        }
+    }
+
+    #[test]
+    fn test_invalid_ascii_escape() {
+        let invalid = [r"\a", r"\?", r"\"];
+        for c in &invalid {
+            assert_invalid_str(c);
+        }
+    }
+
+    #[test]
+    fn test_valid_ascii_code_escape() {
+        let valid = [r"\x00", r"\x7F", r"\x55", r"\xF0"];
+        for c in &valid {
+            assert_valid_str(c);
+        }
+    }
+
+    #[test]
+    fn test_invalid_ascii_code_escape() {
+        let invalid = [r"\x", r"\x7"];
+        for c in &invalid {
+            assert_invalid_str(c);
+        }
+    }
+
+    #[test]
+    fn test_invalid_unicode_escape() {
+        let well_formed = [
+            r"\u{FF}",
+            r"\u{0}",
+            r"\u{F}",
+            r"\u{10FFFF}",
+            r"\u{1_0__FF___FF_____}",
+        ];
+        for c in &well_formed {
+            assert_invalid_str(c);
+        }
+
+        let invalid = [
+            r"\u",
+            r"\u{}",
+            r"\u{",
+            r"\u{FF",
+            r"\u{FFFFFF}",
+            r"\u{_F}",
+            r"\u{00FFFFF}",
+            r"\u{110000}",
+        ];
+        for c in &invalid {
+            assert_invalid_str(c);
+        }
+    }
+
+    #[test]
+    fn test_mixed_invalid() {
+        assert_invalid_str(
+            r"This is the tale of a string
+with a newline in between, some emoji (👨‍👨‍) here and there,
+unicode escapes like this: \u{1FFBB} and weird stuff like
+this ﷽",
+        );
+    }
+
+    #[test]
+    fn test_mixed_valid() {
+        assert_valid_str(
+            r"This is the tale of a string
+with a newline in between, no emoji at all,
+nor unicode escapes or weird stuff",
+        );
+    }
+
+    #[test]
+    fn test_ignore_newline() {
+        assert_valid_str(
+            "Hello \
+             World",
+        );
+    }
+}
diff --git a/crates/ra_syntax/src/validation/char.rs b/crates/ra_syntax/src/validation/char.rs
new file mode 100644 (file)
index 0000000..4728c85
--- /dev/null
@@ -0,0 +1,276 @@
+//! Validation of char literals
+
+use std::u32;
+
+use arrayvec::ArrayString;
+
+use crate::{
+    ast::{self, AstNode},
+    string_lexing::{self, CharComponentKind},
+    TextRange,
+    yellow::{
+        SyntaxError,
+        SyntaxErrorKind::*,
+    },
+};
+
+pub(super) fn validate_char_node(node: ast::Char, errors: &mut Vec<SyntaxError>) {
+    let literal_text = node.text();
+    let literal_range = node.syntax().range();
+    let mut components = string_lexing::parse_char_literal(literal_text);
+    let mut len = 0;
+    for component in &mut components {
+        len += 1;
+        let text = &literal_text[component.range];
+        let range = component.range + literal_range.start();
+        validate_char_component(text, component.kind, range, errors);
+    }
+
+    if !components.has_closing_quote {
+        errors.push(SyntaxError::new(UnclosedChar, literal_range));
+    }
+
+    if len == 0 {
+        errors.push(SyntaxError::new(EmptyChar, literal_range));
+    }
+
+    if len > 1 {
+        errors.push(SyntaxError::new(OverlongChar, literal_range));
+    }
+}
+
+pub(super) fn validate_char_component(
+    text: &str,
+    kind: CharComponentKind,
+    range: TextRange,
+    errors: &mut Vec<SyntaxError>,
+) {
+    // Validate escapes
+    use self::CharComponentKind::*;
+    match kind {
+        AsciiEscape => validate_ascii_escape(text, range, errors),
+        AsciiCodeEscape => validate_ascii_code_escape(text, range, errors),
+        UnicodeEscape => validate_unicode_escape(text, range, errors),
+        CodePoint => {
+            // These code points must always be escaped
+            if text == "\t" || text == "\r" || text == "\n" {
+                errors.push(SyntaxError::new(UnescapedCodepoint, range));
+            }
+        }
+    }
+}
+
+fn validate_ascii_escape(text: &str, range: TextRange, errors: &mut Vec<SyntaxError>) {
+    if text.len() == 1 {
+        // Escape sequence consists only of leading `\`
+        errors.push(SyntaxError::new(EmptyAsciiEscape, range));
+    } else {
+        let escape_code = text.chars().skip(1).next().unwrap();
+        if !is_ascii_escape(escape_code) {
+            errors.push(SyntaxError::new(InvalidAsciiEscape, range));
+        }
+    }
+}
+
+pub(super) fn is_ascii_escape(code: char) -> bool {
+    match code {
+        '\\' | '\'' | '"' | 'n' | 'r' | 't' | '0' => true,
+        _ => false,
+    }
+}
+
+fn validate_ascii_code_escape(text: &str, range: TextRange, errors: &mut Vec<SyntaxError>) {
+    // An AsciiCodeEscape has 4 chars, example: `\xDD`
+    if text.len() < 4 {
+        errors.push(SyntaxError::new(TooShortAsciiCodeEscape, range));
+    } else {
+        assert!(
+            text.chars().count() == 4,
+            "AsciiCodeEscape cannot be longer than 4 chars"
+        );
+
+        match u8::from_str_radix(&text[2..], 16) {
+            Ok(code) if code < 128 => { /* Escape code is valid */ }
+            Ok(_) => errors.push(SyntaxError::new(AsciiCodeEscapeOutOfRange, range)),
+            Err(_) => errors.push(SyntaxError::new(MalformedAsciiCodeEscape, range)),
+        }
+    }
+}
+
+fn validate_unicode_escape(text: &str, range: TextRange, errors: &mut Vec<SyntaxError>) {
+    assert!(&text[..2] == "\\u", "UnicodeEscape always starts with \\u");
+
+    if text.len() == 2 {
+        // No starting `{`
+        errors.push(SyntaxError::new(MalformedUnicodeEscape, range));
+        return;
+    }
+
+    if text.len() == 3 {
+        // Only starting `{`
+        errors.push(SyntaxError::new(UnclosedUnicodeEscape, range));
+        return;
+    }
+
+    let mut code = ArrayString::<[_; 6]>::new();
+    let mut closed = false;
+    for c in text[3..].chars() {
+        assert!(!closed, "no characters after escape is closed");
+
+        if c.is_digit(16) {
+            if code.len() == 6 {
+                errors.push(SyntaxError::new(OverlongUnicodeEscape, range));
+                return;
+            }
+
+            code.push(c);
+        } else if c == '_' {
+            // Reject leading _
+            if code.len() == 0 {
+                errors.push(SyntaxError::new(MalformedUnicodeEscape, range));
+                return;
+            }
+        } else if c == '}' {
+            closed = true;
+        } else {
+            errors.push(SyntaxError::new(MalformedUnicodeEscape, range));
+            return;
+        }
+    }
+
+    if !closed {
+        errors.push(SyntaxError::new(UnclosedUnicodeEscape, range))
+    }
+
+    if code.len() == 0 {
+        errors.push(SyntaxError::new(EmptyUnicodeEcape, range));
+        return;
+    }
+
+    match u32::from_str_radix(&code, 16) {
+        Ok(code_u32) if code_u32 > 0x10FFFF => {
+            errors.push(SyntaxError::new(UnicodeEscapeOutOfRange, range));
+        }
+        Ok(_) => {
+            // Valid escape code
+        }
+        Err(_) => {
+            errors.push(SyntaxError::new(MalformedUnicodeEscape, range));
+        }
+    }
+}
+
+#[cfg(test)]
+mod test {
+    use crate::SourceFileNode;
+
+    fn build_file(literal: &str) -> SourceFileNode {
+        let src = format!("const C: char = '{}';", literal);
+        SourceFileNode::parse(&src)
+    }
+
+    fn assert_valid_char(literal: &str) {
+        let file = build_file(literal);
+        assert!(
+            file.errors().len() == 0,
+            "Errors for literal '{}': {:?}",
+            literal,
+            file.errors()
+        );
+    }
+
+    fn assert_invalid_char(literal: &str) {
+        let file = build_file(literal);
+        assert!(file.errors().len() > 0);
+    }
+
+    #[test]
+    fn test_ansi_codepoints() {
+        for byte in 0..=255u8 {
+            match byte {
+                b'\n' | b'\r' | b'\t' => assert_invalid_char(&(byte as char).to_string()),
+                b'\'' | b'\\' => { /* Ignore character close and backslash */ }
+                _ => assert_valid_char(&(byte as char).to_string()),
+            }
+        }
+    }
+
+    #[test]
+    fn test_unicode_codepoints() {
+        let valid = ["Ƒ", "バ", "メ", "﷽"];
+        for c in &valid {
+            assert_valid_char(c);
+        }
+    }
+
+    #[test]
+    fn test_unicode_multiple_codepoints() {
+        let invalid = ["नी", "👨‍👨‍"];
+        for c in &invalid {
+            assert_invalid_char(c);
+        }
+    }
+
+    #[test]
+    fn test_valid_ascii_escape() {
+        let valid = [r"\'", "\"", "\\\\", "\\\"", r"\n", r"\r", r"\t", r"\0"];
+        for c in &valid {
+            assert_valid_char(c);
+        }
+    }
+
+    #[test]
+    fn test_invalid_ascii_escape() {
+        let invalid = [r"\a", r"\?", r"\"];
+        for c in &invalid {
+            assert_invalid_char(c);
+        }
+    }
+
+    #[test]
+    fn test_valid_ascii_code_escape() {
+        let valid = [r"\x00", r"\x7F", r"\x55"];
+        for c in &valid {
+            assert_valid_char(c);
+        }
+    }
+
+    #[test]
+    fn test_invalid_ascii_code_escape() {
+        let invalid = [r"\x", r"\x7", r"\xF0"];
+        for c in &invalid {
+            assert_invalid_char(c);
+        }
+    }
+
+    #[test]
+    fn test_valid_unicode_escape() {
+        let valid = [
+            r"\u{FF}",
+            r"\u{0}",
+            r"\u{F}",
+            r"\u{10FFFF}",
+            r"\u{1_0__FF___FF_____}",
+        ];
+        for c in &valid {
+            assert_valid_char(c);
+        }
+    }
+
+    #[test]
+    fn test_invalid_unicode_escape() {
+        let invalid = [
+            r"\u",
+            r"\u{}",
+            r"\u{",
+            r"\u{FF",
+            r"\u{FFFFFF}",
+            r"\u{_F}",
+            r"\u{00FFFFF}",
+            r"\u{110000}",
+        ];
+        for c in &invalid {
+            assert_invalid_char(c);
+        }
+    }
+}
diff --git a/crates/ra_syntax/src/validation/mod.rs b/crates/ra_syntax/src/validation/mod.rs
new file mode 100644 (file)
index 0000000..bdee812
--- /dev/null
@@ -0,0 +1,24 @@
+use crate::{
+    algo::visit::{visitor_ctx, VisitorCtx},
+    ast,
+    SourceFileNode,
+    yellow::SyntaxError,
+};
+
+mod byte;
+mod byte_string;
+mod char;
+mod string;
+
+pub(crate) fn validate(file: &SourceFileNode) -> Vec<SyntaxError> {
+    let mut errors = Vec::new();
+    for node in file.syntax().descendants() {
+        let _ = visitor_ctx(&mut errors)
+            .visit::<ast::Byte, _>(self::byte::validate_byte_node)
+            .visit::<ast::ByteString, _>(self::byte_string::validate_byte_string_node)
+            .visit::<ast::Char, _>(self::char::validate_char_node)
+            .visit::<ast::String, _>(self::string::validate_string_node)
+            .accept(node);
+    }
+    errors
+}
diff --git a/crates/ra_syntax/src/validation/string.rs b/crates/ra_syntax/src/validation/string.rs
new file mode 100644 (file)
index 0000000..089879d
--- /dev/null
@@ -0,0 +1,168 @@
+use crate::{
+    ast::{self, AstNode},
+    string_lexing::{self, StringComponentKind},
+    yellow::{
+        SyntaxError,
+        SyntaxErrorKind::*,
+    },
+};
+
+use super::char;
+
+pub(crate) fn validate_string_node(node: ast::String, errors: &mut Vec<SyntaxError>) {
+    let literal_text = node.text();
+    let literal_range = node.syntax().range();
+    let mut components = string_lexing::parse_string_literal(literal_text);
+    for component in &mut components {
+        let range = component.range + literal_range.start();
+
+        match component.kind {
+            StringComponentKind::Char(kind) => {
+                // Chars must escape \t, \n and \r codepoints, but strings don't
+                let text = &literal_text[component.range];
+                match text {
+                    "\t" | "\n" | "\r" => { /* always valid */ }
+                    _ => char::validate_char_component(text, kind, range, errors),
+                }
+            }
+            StringComponentKind::IgnoreNewline => { /* always valid */ }
+        }
+    }
+
+    if !components.has_closing_quote {
+        errors.push(SyntaxError::new(UnclosedString, literal_range));
+    }
+}
+
+#[cfg(test)]
+mod test {
+    use crate::SourceFileNode;
+
+    fn build_file(literal: &str) -> SourceFileNode {
+        let src = format!(r#"const S: &'static str = "{}";"#, literal);
+        println!("Source: {}", src);
+        SourceFileNode::parse(&src)
+    }
+
+    fn assert_valid_str(literal: &str) {
+        let file = build_file(literal);
+        assert!(
+            file.errors().len() == 0,
+            "Errors for literal '{}': {:?}",
+            literal,
+            file.errors()
+        );
+    }
+
+    fn assert_invalid_str(literal: &str) {
+        let file = build_file(literal);
+        assert!(file.errors().len() > 0);
+    }
+
+    #[test]
+    fn test_ansi_codepoints() {
+        for byte in 0..=255u8 {
+            match byte {
+                b'\"' | b'\\' => { /* Ignore string close and backslash */ }
+                _ => assert_valid_str(&(byte as char).to_string()),
+            }
+        }
+    }
+
+    #[test]
+    fn test_unicode_codepoints() {
+        let valid = ["Ƒ", "バ", "メ", "﷽"];
+        for c in &valid {
+            assert_valid_str(c);
+        }
+    }
+
+    #[test]
+    fn test_unicode_multiple_codepoints() {
+        let valid = ["नी", "👨‍👨‍"];
+        for c in &valid {
+            assert_valid_str(c);
+        }
+    }
+
+    #[test]
+    fn test_valid_ascii_escape() {
+        let valid = [r"\'", r#"\""#, r"\\", r"\n", r"\r", r"\t", r"\0", "a", "b"];
+        for c in &valid {
+            assert_valid_str(c);
+        }
+    }
+
+    #[test]
+    fn test_invalid_ascii_escape() {
+        let invalid = [r"\a", r"\?", r"\"];
+        for c in &invalid {
+            assert_invalid_str(c);
+        }
+    }
+
+    #[test]
+    fn test_valid_ascii_code_escape() {
+        let valid = [r"\x00", r"\x7F", r"\x55"];
+        for c in &valid {
+            assert_valid_str(c);
+        }
+    }
+
+    #[test]
+    fn test_invalid_ascii_code_escape() {
+        let invalid = [r"\x", r"\x7", r"\xF0"];
+        for c in &invalid {
+            assert_invalid_str(c);
+        }
+    }
+
+    #[test]
+    fn test_valid_unicode_escape() {
+        let valid = [
+            r"\u{FF}",
+            r"\u{0}",
+            r"\u{F}",
+            r"\u{10FFFF}",
+            r"\u{1_0__FF___FF_____}",
+        ];
+        for c in &valid {
+            assert_valid_str(c);
+        }
+    }
+
+    #[test]
+    fn test_invalid_unicode_escape() {
+        let invalid = [
+            r"\u",
+            r"\u{}",
+            r"\u{",
+            r"\u{FF",
+            r"\u{FFFFFF}",
+            r"\u{_F}",
+            r"\u{00FFFFF}",
+            r"\u{110000}",
+        ];
+        for c in &invalid {
+            assert_invalid_str(c);
+        }
+    }
+
+    #[test]
+    fn test_mixed() {
+        assert_valid_str(
+            r"This is the tale of a string
+with a newline in between, some emoji (👨‍👨‍) here and there,
+unicode escapes like this: \u{1FFBB} and weird stuff like
+this ﷽",
+        );
+    }
+
+    #[test]
+    fn test_ignore_newline() {
+        assert_valid_str(
+            "Hello \
+             World",
+        );
+    }
+}
index f3df6bc15318b0e745158d19f5d9ee9b73ae17e0..c32ee650dd880ea75bd65694e94ef2ba7130e710 100644 (file)
@@ -34,6 +34,10 @@ pub fn new<L: Into<Location>>(kind: SyntaxErrorKind, loc: L) -> SyntaxError {
         }
     }
 
+    pub fn kind(&self) -> SyntaxErrorKind {
+        self.kind.clone()
+    }
+
     pub fn location(&self) -> Location {
         self.location.clone()
     }
@@ -64,11 +68,31 @@ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
 #[derive(Debug, Clone, PartialEq, Eq, Hash)]
 pub enum SyntaxErrorKind {
     ParseError(ParseError),
+    UnescapedCodepoint,
     EmptyChar,
     UnclosedChar,
-    LongChar,
+    OverlongChar,
+    EmptyByte,
+    UnclosedByte,
+    OverlongByte,
+    ByteOutOfRange,
+    UnescapedByte,
+    EmptyByteEscape,
+    InvalidByteEscape,
+    TooShortByteCodeEscape,
+    MalformedByteCodeEscape,
+    UnicodeEscapeForbidden,
     EmptyAsciiEscape,
     InvalidAsciiEscape,
+    TooShortAsciiCodeEscape,
+    AsciiCodeEscapeOutOfRange,
+    MalformedAsciiCodeEscape,
+    UnclosedUnicodeEscape,
+    MalformedUnicodeEscape,
+    EmptyUnicodeEcape,
+    OverlongUnicodeEscape,
+    UnicodeEscapeOutOfRange,
+    UnclosedString,
 }
 
 #[derive(Debug, Clone, PartialEq, Eq, Hash)]
@@ -78,11 +102,38 @@ impl fmt::Display for SyntaxErrorKind {
     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
         use self::SyntaxErrorKind::*;
         match self {
+            UnescapedCodepoint => write!(f, "This codepoint should always be escaped"),
             EmptyAsciiEscape => write!(f, "Empty escape sequence"),
             InvalidAsciiEscape => write!(f, "Invalid escape sequence"),
             EmptyChar => write!(f, "Empty char literal"),
             UnclosedChar => write!(f, "Unclosed char literal"),
-            LongChar => write!(f, "Char literal should be one character long"),
+            OverlongChar => write!(f, "Char literal should be one character long"),
+            EmptyByte => write!(f, "Empty byte literal"),
+            UnclosedByte => write!(f, "Unclosed byte literal"),
+            OverlongByte => write!(f, "Byte literal should be one character long"),
+            ByteOutOfRange => write!(f, "Byte should be a valid ASCII character"),
+            UnescapedByte => write!(f, "This byte should always be escaped"),
+            EmptyByteEscape => write!(f, "Empty escape sequence"),
+            InvalidByteEscape => write!(f, "Invalid escape sequence"),
+            TooShortByteCodeEscape => write!(f, "Escape sequence should have two digits"),
+            MalformedByteCodeEscape => write!(f, "Escape sequence should be a hexadecimal number"),
+            UnicodeEscapeForbidden => write!(
+                f,
+                "Unicode escapes are not allowed in byte literals or byte strings"
+            ),
+            TooShortAsciiCodeEscape => write!(f, "Escape sequence should have two digits"),
+            AsciiCodeEscapeOutOfRange => {
+                write!(f, "Escape sequence should be between \\x00 and \\x7F")
+            }
+            MalformedAsciiCodeEscape => write!(f, "Escape sequence should be a hexadecimal number"),
+            UnclosedUnicodeEscape => write!(f, "Missing `}}`"),
+            MalformedUnicodeEscape => write!(f, "Malformed unicode escape sequence"),
+            EmptyUnicodeEcape => write!(f, "Empty unicode escape sequence"),
+            OverlongUnicodeEscape => {
+                write!(f, "Unicode escape sequence should have at most 6 digits")
+            }
+            UnicodeEscapeOutOfRange => write!(f, "Unicode escape code should be at most 0x10FFFF"),
+            UnclosedString => write!(f, "Unclosed string literal"),
             ParseError(msg) => write!(f, "{}", msg.0),
         }
     }
index aa375fe8a4ca35939d0efbbe661c94498cbb2c73..665f7599c9b186b4340b7a7cab03596ddb1f4a8a 100644 (file)
@@ -1,4 +1,4 @@
-ROOT@[0; 34)
+SOURCE_FILE@[0; 34)
   STRUCT_DEF@[0; 34)
     STRUCT_KW@[0; 6)
     WHITESPACE@[6; 7)
index 771d6e1ef03db35ae9218fb7ed54dd43be2b499a..74743d7ad591f21f1ed54d7b988fa29dcc7a92f0 100644 (file)
@@ -1,4 +1,4 @@
-ROOT@[0; 21)
+SOURCE_FILE@[0; 21)
   ERROR@[0; 2)
     IF_KW@[0; 2)
     err: `expected an item`
index 57fb48420c1d412cdea202ea4af046ca26a82075..a1f041c33302bc29b2e08dbec0b896f5d4da4d07 100644 (file)
@@ -1,4 +1,4 @@
-ROOT@[0; 42)
+SOURCE_FILE@[0; 42)
   SHEBANG@[0; 20)
   WHITESPACE@[20; 21)
   err: `expected an item`
index b51c0573ad4d37aa82825bd9d91e7c12ba93632c..e9400d03b35cba82a8265e9c568f38a0274fad53 100644 (file)
@@ -1,4 +1,4 @@
-ROOT@[0; 40)
+SOURCE_FILE@[0; 40)
   STRUCT_DEF@[0; 40)
     STRUCT_KW@[0; 6)
     WHITESPACE@[6; 7)
index 646c8453462407a899af997d507b64ddc8080401..5bdb24676c0e5adb03331b439a03b3acb26e374b 100644 (file)
@@ -1,4 +1,4 @@
-ROOT@[0; 12)
+SOURCE_FILE@[0; 12)
   USE_ITEM@[0; 12)
     USE_KW@[0; 3)
     WHITESPACE@[3; 4)
index b3cf9b889dfa43259d4e0222512bcb20fb2594c0..381a7d6ddd224fec65104ee3eb18062ea3effd54 100644 (file)
@@ -1,4 +1,4 @@
-ROOT@[0; 54)
+SOURCE_FILE@[0; 54)
   FN_DEF@[0; 31)
     ATTR@[0; 18)
       POUND@[0; 1)
index 9abda70991de1af6be9811c92445f0a8ac15ca4d..02f832fb7991a9de70c4e6eba0992b758d99fcf3 100644 (file)
@@ -1,4 +1,4 @@
-ROOT@[0; 74)
+SOURCE_FILE@[0; 74)
   STRUCT_DEF@[0; 73)
     STRUCT_KW@[0; 6)
     WHITESPACE@[6; 7)
index 802c69b316c80b90bb1d1934b05c8efa80c31ec1..d46f753ad3500c063ca3b69c36e87328067d8725 100644 (file)
@@ -1,4 +1,4 @@
-ROOT@[0; 31)
+SOURCE_FILE@[0; 31)
   ERROR@[0; 1)
     R_CURLY@[0; 1)
     err: `unmatched `}``
index 95a34333b7f7d6cbac898d025e1130fc1d747100..de5c81f291a4627059067c910567f6bf79fd004a 100644 (file)
@@ -1,4 +1,4 @@
-ROOT@[0; 95)
+SOURCE_FILE@[0; 95)
   FN_DEF@[0; 12)
     FN_KW@[0; 2)
     WHITESPACE@[2; 3)
index fa8adf8816afcbb59f590a4c7586e4e8d5e8aea1..e22c061d27e1702a859552a6717a15d512d33ee4 100644 (file)
@@ -1,4 +1,4 @@
-ROOT@[0; 43)
+SOURCE_FILE@[0; 43)
   STRUCT_DEF@[0; 11)
     STRUCT_KW@[0; 6)
     WHITESPACE@[6; 7)
index 38667e030133fa8ee8f713fbcab626eea93ab8d8..e6794a2a0c1f3fd9910bd61e3aa4fca061ced61e 100644 (file)
@@ -1,4 +1,4 @@
-ROOT@[0; 42)
+SOURCE_FILE@[0; 42)
   FN_DEF@[0; 41)
     FN_KW@[0; 2)
     WHITESPACE@[2; 3)
index 0cb7447c464dbf576c6a75bb287e4f3c58ea9fcc..a65cecde5672928d8d4be883c13393aabdfd09aa 100644 (file)
@@ -1,4 +1,4 @@
-ROOT@[0; 19)
+SOURCE_FILE@[0; 19)
   ERROR@[0; 6)
     ABI@[0; 6)
       EXTERN_KW@[0; 6)
index a03aa05e3904781f62cc33e4eccf807665f25921..12ebc2a3a3bf61540e3574b8aad2e6609f5158b8 100644 (file)
@@ -1,4 +1,4 @@
-ROOT@[0; 389)
+SOURCE_FILE@[0; 389)
   FN_DEF@[0; 389)
     VISIBILITY@[0; 10)
       PUB_KW@[0; 3)
index e1a6b643260dcf88e31b77736403c3008974d2cb..83d716b2f8387bed87d19aa8b90e4ac869020588 100644 (file)
@@ -1,4 +1,4 @@
-ROOT@[0; 86)
+SOURCE_FILE@[0; 86)
   STRUCT_DEF@[0; 72)
     VISIBILITY@[0; 3)
       PUB_KW@[0; 3)
index 37826ce86b0810c8c600fbfce424857f370f535f..a81772764aeb8261647dd94f1006c29a0083ea1a 100644 (file)
@@ -1,4 +1,4 @@
-ROOT@[0; 23)
+SOURCE_FILE@[0; 23)
   FN_DEF@[0; 22)
     FN_KW@[0; 2)
     WHITESPACE@[2; 3)
index 5f736a9784a63af7e449378a2d791ad0d29de9c3..e4d91109f153ae2221f022cb5c95a6a90cf729ed 100644 (file)
@@ -1,4 +1,4 @@
-ROOT@[0; 14)
+SOURCE_FILE@[0; 14)
   FN_DEF@[0; 7)
     FN_KW@[0; 2)
     WHITESPACE@[2; 3)
index a3163adcbfbd04f2bcfa2360c00c586825f61602..5b128caa256021b595b0d8736ec6abb6609c4cf6 100644 (file)
@@ -1,4 +1,4 @@
-ROOT@[0; 56)
+SOURCE_FILE@[0; 56)
   FN_DEF@[0; 55)
     FN_KW@[0; 2)
     WHITESPACE@[2; 3)
index f0be287ada1141bc93025df15634bc9b46a20082..fbfdc2c44823d541f1ba81c633424b2793cc115f 100644 (file)
@@ -1,4 +1,4 @@
-ROOT@[0; 47)
+SOURCE_FILE@[0; 47)
   FN_DEF@[0; 46)
     FN_KW@[0; 2)
     WHITESPACE@[2; 3)
index c4d9f5e7ec02d1377e2cba39d7a9be2d1be40f36..3937be2552128039a21dae26c567107fbc08e081 100644 (file)
@@ -1,4 +1,4 @@
-ROOT@[0; 183)
+SOURCE_FILE@[0; 183)
   IMPL_ITEM@[0; 182)
     IMPL_KW@[0; 4)
     WHITESPACE@[4; 5)
index 6e74771f166123e2f0f6301ac1aef9e0afa0613a..ee75c33aed595d355d8c0a450b70665943b8be50 100644 (file)
@@ -1,4 +1,4 @@
-ROOT@[0; 139)
+SOURCE_FILE@[0; 139)
   FN_DEF@[0; 138)
     FN_KW@[0; 2)
     WHITESPACE@[2; 3)
index b5218b0fac4e9a65a8e2593a88db281e2040244f..375fd3ad2e8ab5dee4a986aa25ccc92e0e25f6c5 100644 (file)
@@ -1,4 +1,4 @@
-ROOT@[0; 16)
+SOURCE_FILE@[0; 16)
   FN_DEF@[0; 2)
     FN_KW@[0; 2)
     err: `expected a name`
index 8dcb58ae24574eed519c9b7fe65c0ca068e06620..1b3dcd945d87bfdc0d86a2270079b17e4fce4de7 100644 (file)
@@ -1,4 +1,4 @@
-ROOT@[0; 22)
+SOURCE_FILE@[0; 22)
   FN_DEF@[0; 21)
     FN_KW@[0; 2)
     WHITESPACE@[2; 3)
index 287e56ac4381ee49d3fbf4231aa64ac80f448847..55999c16095c54bea5d36cdb7566c9061f34e687 100644 (file)
@@ -1,4 +1,4 @@
-ROOT@[0; 112)
+SOURCE_FILE@[0; 112)
   FN_DEF@[0; 33)
     FN_KW@[0; 2)
     WHITESPACE@[2; 3)
index 2df81b12b4401cdb723eec82c26b177f20ebcd15..dcca0756dd230182cdf9270d6b44e14823838dcc 100644 (file)
@@ -1,4 +1,4 @@
-ROOT@[0; 94)
+SOURCE_FILE@[0; 94)
   FN_DEF@[0; 55)
     FN_KW@[0; 2)
     WHITESPACE@[2; 3)
index 195c12e086e0fc26665d127c982632991bf3318c..8720fc6ea639459bbbc654d6085cfb615e9d7faa 100644 (file)
@@ -1,4 +1,4 @@
-ROOT@[0; 240)
+SOURCE_FILE@[0; 240)
   FN_DEF@[0; 53)
     FN_KW@[0; 2)
     WHITESPACE@[2; 3)
index 6879c8d0a3a458d08e5a1bfc7b1b1a1ec559226e..615064f7712ca824f2b3e3421922932bd3d368cc 100644 (file)
@@ -1,4 +1,4 @@
-ROOT@[0; 575)
+SOURCE_FILE@[0; 575)
   FN_DEF@[0; 574)
     FN_KW@[0; 2)
     WHITESPACE@[2; 3)
index 9e26f58a08c6f077dc6160e1893c715a16742c41..82683f6ee9eca523e3ea5c7f30d9514d91cea273 100644 (file)
@@ -1,4 +1,4 @@
-ROOT@[0; 38)
+SOURCE_FILE@[0; 38)
   IMPL_ITEM@[0; 14)
     IMPL_KW@[0; 4)
     TYPE_PARAM_LIST@[4; 14)
index 94b75a6568011c781bb600c8ccdacb8248d7925f..694295a606857f2546d9697a4c82c274a272364c 100644 (file)
@@ -1,4 +1,4 @@
-ROOT@[0; 30)
+SOURCE_FILE@[0; 30)
   FN_DEF@[0; 29)
     FN_KW@[0; 2)
     WHITESPACE@[2; 3)
index 95653298161fa032388841ad7f9c7691a128ab61..814c9f1a0ae75fb4ceb168af2dd58d24ef5029bc 100644 (file)
@@ -1,4 +1,4 @@
-ROOT@[0; 25)
+SOURCE_FILE@[0; 25)
   FN_DEF@[0; 24)
     CONST_KW@[0; 5)
     WHITESPACE@[5; 6)
index bda875fc58da75dbf96de96cfe409c73b1f25e57..8206d973b8740a6835eab9e15e7f14722281c3f2 100644 (file)
@@ -1,4 +1,4 @@
-ROOT@[0; 18)
+SOURCE_FILE@[0; 18)
   FN_DEF@[0; 17)
     CONST_KW@[0; 5)
     WHITESPACE@[5; 6)
index 2c2bbd035c3e62f0d17eb84a25e292f3663aedc2..7e79c228055b482e40b8a2d948760fffbb1b003b 100644 (file)
@@ -1,4 +1,4 @@
-ROOT@[0; 10)
+SOURCE_FILE@[0; 10)
   EXTERN_BLOCK@[0; 9)
     ABI@[0; 6)
       EXTERN_KW@[0; 6)
index f6e7694756e03cfa6628247c2101bd9040bac9cf..d2b26566d012316af8d9a02b4e9da50bd7b2a648 100644 (file)
@@ -1,4 +1,4 @@
-ROOT@[0; 19)
+SOURCE_FILE@[0; 19)
   FN_DEF@[0; 18)
     ABI@[0; 6)
       EXTERN_KW@[0; 6)
index fc025b0f3bbf1365b8ecee95149789d7b51e867f..7ffdc7fbdb93c50bde6c1947dbbe5b042383ba16 100644 (file)
@@ -1,4 +1,4 @@
-ROOT@[0; 18)
+SOURCE_FILE@[0; 18)
   EXTERN_CRATE_ITEM@[0; 17)
     EXTERN_KW@[0; 6)
     WHITESPACE@[6; 7)
index fb97cc0420477a63c59806965cbc2ed4bd4f15a7..3669c93a8be84580f7bd466eb0f084a9c2227aab 100644 (file)
@@ -1,4 +1,4 @@
-ROOT@[0; 18)
+SOURCE_FILE@[0; 18)
   TRAIT_DEF@[0; 17)
     UNSAFE_KW@[0; 6)
     WHITESPACE@[6; 7)
index fa596394f4d8e48c4ebb002c507c50bd27e89b96..f9c96c2429da186dde670a0b42fe71a161c4725b 100644 (file)
@@ -1,4 +1,4 @@
-ROOT@[0; 19)
+SOURCE_FILE@[0; 19)
   IMPL_ITEM@[0; 18)
     UNSAFE_KW@[0; 6)
     WHITESPACE@[6; 7)
index e642bbf808bc9668a503c45e6632bc5c80cae9b4..b541ceff89dbf723b871286a1b78ae75d872a1d0 100644 (file)
@@ -1,4 +1,4 @@
-ROOT@[0; 23)
+SOURCE_FILE@[0; 23)
   TRAIT_DEF@[0; 22)
     UNSAFE_KW@[0; 6)
     WHITESPACE@[6; 7)
index 99d33a902c9d1c1d1cd2a78f4006c5c6b28bf2ea..6003ba645be87bbc0537922dacb6f1dea7cf892c 100644 (file)
@@ -1,4 +1,4 @@
-ROOT@[0; 27)
+SOURCE_FILE@[0; 27)
   IMPL_ITEM@[0; 26)
     UNSAFE_KW@[0; 6)
     WHITESPACE@[6; 7)
index 39ec53b264f749365420e5b12f35fdd58f717b5f..5498dbcbb440c44f7d6bd34c92fdce64ff9beec8 100644 (file)
@@ -1,4 +1,4 @@
-ROOT@[0; 19)
+SOURCE_FILE@[0; 19)
   FN_DEF@[0; 18)
     UNSAFE_KW@[0; 6)
     WHITESPACE@[6; 7)
index 54e296ed675a799c0d190f211b4eaf3a3960c14c..0cde40595847cc42081e70d9ccb0ff03fc85ff64 100644 (file)
@@ -1,4 +1,4 @@
-ROOT@[0; 30)
+SOURCE_FILE@[0; 30)
   FN_DEF@[0; 29)
     UNSAFE_KW@[0; 6)
     WHITESPACE@[6; 7)
index 9825e5ace95322b2f833d67c071609db29df6ff8..d2da2bad70b41c93977254d46be3d64fc8ba771e 100644 (file)
@@ -1,4 +1,4 @@
-ROOT@[0; 33)
+SOURCE_FILE@[0; 33)
   FN_DEF@[0; 10)
     FN_KW@[0; 2)
     WHITESPACE@[2; 3)
index bf65e8a3750e0225c8d194c6a9c4c7f4c01fa5da..89b34d4f134e150e64a320210db65e7b2ea36df1 100644 (file)
@@ -1,4 +1,4 @@
-ROOT@[0; 21)
+SOURCE_FILE@[0; 21)
   TYPE_DEF@[0; 20)
     TYPE_KW@[0; 4)
     WHITESPACE@[4; 5)
index 71048bcc5018ba7c61ee60a6491cd9cab0dfcf09..4b66a05a653dabcba8716f9f8d792babcdafa1d2 100644 (file)
@@ -1,4 +1,4 @@
-ROOT@[0; 16)
+SOURCE_FILE@[0; 16)
   TYPE_DEF@[0; 15)
     TYPE_KW@[0; 4)
     WHITESPACE@[4; 5)
index c3fca013eedec809a74c421c7ae320386736f8c1..f38b2f79ad12b267b091cdf202dfd2723b5912d8 100644 (file)
@@ -1,4 +1,4 @@
-ROOT@[0; 31)
+SOURCE_FILE@[0; 31)
   TYPE_DEF@[0; 30)
     TYPE_KW@[0; 4)
     WHITESPACE@[4; 5)
index 97e73fea121ef20b94136b2aede5925ee53a8d00..bd5feb6b3fe8fb5e0c59ae6a26bfa58b0a5478fe 100644 (file)
@@ -1,4 +1,4 @@
-ROOT@[0; 16)
+SOURCE_FILE@[0; 16)
   TYPE_DEF@[0; 15)
     TYPE_KW@[0; 4)
     WHITESPACE@[4; 5)
index 5d04d42fc9781095ba73e56e378cdc5db4357e20..9fd19ba4ae9bb99c39e7b8653ac048a45ec9da4d 100644 (file)
@@ -1,4 +1,4 @@
-ROOT@[0; 13)
+SOURCE_FILE@[0; 13)
   TYPE_DEF@[0; 12)
     TYPE_KW@[0; 4)
     WHITESPACE@[4; 5)
index 2cd8be9ab74cb6bdd0f59d1417ea158b57240d66..173c325d06d5b5d7cf30b46beb7f7cc8daa6c8cf 100644 (file)
@@ -1,4 +1,4 @@
-ROOT@[0; 17)
+SOURCE_FILE@[0; 17)
   TYPE_DEF@[0; 16)
     TYPE_KW@[0; 4)
     WHITESPACE@[4; 5)
index 6205d9669ab74b04949172d09e35d7b7df461d34..e0cae644d6ac85260e29dd02e454a39f80de68a4 100644 (file)
@@ -1,4 +1,4 @@
-ROOT@[0; 16)
+SOURCE_FILE@[0; 16)
   TYPE_DEF@[0; 15)
     TYPE_KW@[0; 4)
     WHITESPACE@[4; 5)
index 23e68557aa7e9ab1634e4a5476ddeff569a6b789..189aa563e28aef32102871e7743ff900e678dbc8 100644 (file)
@@ -1,4 +1,4 @@
-ROOT@[0; 14)
+SOURCE_FILE@[0; 14)
   TYPE_DEF@[0; 13)
     TYPE_KW@[0; 4)
     WHITESPACE@[4; 5)
index 33f5ad8b49d594bef82b3558fd997bedf524dc5f..149cd571d73eabcfa59f971780500de64083a72b 100644 (file)
@@ -1,4 +1,4 @@
-ROOT@[0; 36)
+SOURCE_FILE@[0; 36)
   TYPE_DEF@[0; 17)
     TYPE_KW@[0; 4)
     WHITESPACE@[4; 5)
index 90bdc5d2e8cce09eaae54e6c0f6956fea26bea48..0363635875f9ab446aaad7c39535aeac9389b267 100644 (file)
@@ -1,4 +1,4 @@
-ROOT@[0; 18)
+SOURCE_FILE@[0; 18)
   TYPE_DEF@[0; 12)
     TYPE_KW@[0; 4)
     WHITESPACE@[4; 5)
index 809c86974a3d2f32dc9cf73509f6320176962e74..2c2b615fce57739150615ee55931a87df45195c4 100644 (file)
@@ -1,4 +1,4 @@
-ROOT@[0; 19)
+SOURCE_FILE@[0; 19)
   TYPE_DEF@[0; 18)
     TYPE_KW@[0; 4)
     WHITESPACE@[4; 5)
index 978530a6aab987d8a807b8ac17320edcc661ebc2..db18c7139e26fe6dff80ecb86a00662e0943ced7 100644 (file)
@@ -1,4 +1,4 @@
-ROOT@[0; 15)
+SOURCE_FILE@[0; 15)
   TYPE_DEF@[0; 14)
     TYPE_KW@[0; 4)
     WHITESPACE@[4; 5)
index ff27ce5ddd2fa31a06a9be874458ea552be48d0d..7f35254d1fcfe773e135ad5f94f8c5c31584f45c 100644 (file)
@@ -1,4 +1,4 @@
-ROOT@[0; 54)
+SOURCE_FILE@[0; 54)
   TYPE_DEF@[0; 13)
     TYPE_KW@[0; 4)
     WHITESPACE@[4; 5)
index 538e4e69ca54f7e0b76ad55b449c11596d7a07fd..43ada95d4c3cd6f518b08a5818d593529345ac4f 100644 (file)
@@ -1,4 +1,4 @@
-ROOT@[0; 22)
+SOURCE_FILE@[0; 22)
   TYPE_DEF@[0; 21)
     TYPE_KW@[0; 4)
     WHITESPACE@[4; 5)
index 4dba83eb572f6a74becac6bb9f6241486fb68f3c..0c508ec2731f4c6805f7cab3b4b5e09e603bc60c 100644 (file)
@@ -1,4 +1,4 @@
-ROOT@[0; 70)
+SOURCE_FILE@[0; 70)
   TYPE_DEF@[0; 14)
     TYPE_KW@[0; 4)
     WHITESPACE@[4; 5)
index fa0771ab13a895ad9dbb065cf67283f79e5275a0..f97db4bffe83ac885c2743552c922c431fa65d13 100644 (file)
@@ -1,4 +1,4 @@
-ROOT@[0; 20)
+SOURCE_FILE@[0; 20)
   TYPE_DEF@[0; 15)
     TYPE_KW@[0; 4)
     WHITESPACE@[4; 5)
index 203839636da1c01f5aea49235697f067fa982f15..f092c6df7f4458ff5a438c4631c1567787b2b28f 100644 (file)
@@ -1,4 +1,4 @@
-ROOT@[0; 21)
+SOURCE_FILE@[0; 21)
   TYPE_DEF@[0; 20)
     TYPE_KW@[0; 4)
     WHITESPACE@[4; 5)
index f6b962b2c9b0f8089414ffd015aefc81a426df46..843cc49730465ac13975240566730b8e4997034e 100644 (file)
@@ -1,4 +1,4 @@
-ROOT@[0; 29)
+SOURCE_FILE@[0; 29)
   TYPE_DEF@[0; 28)
     TYPE_KW@[0; 4)
     WHITESPACE@[4; 5)
index b0a110a2c67770e0cfb5ddf85ca99309c817aada..b72b96e9598923301f321647d8ce98a6965aeb53 100644 (file)
@@ -1,4 +1,4 @@
-ROOT@[0; 71)
+SOURCE_FILE@[0; 71)
   TYPE_DEF@[0; 13)
     TYPE_KW@[0; 4)
     WHITESPACE@[4; 5)
index 9adc89b7c36abe91f9c72c6bf02bdd6d1593f4db..86f8154ea577f262f3346a3c30c3adf3e909763b 100644 (file)
@@ -1,4 +1,4 @@
-ROOT@[0; 146)
+SOURCE_FILE@[0; 146)
   FN_DEF@[0; 145)
     FN_KW@[0; 2)
     WHITESPACE@[2; 3)
index a625637cd5a9cc3280a68217ae63515243ae7702..008f4f159a3c1e6d3e1055f04d83596f7ca81b84 100644 (file)
@@ -1,4 +1,4 @@
-ROOT@[0; 52)
+SOURCE_FILE@[0; 52)
   FN_DEF@[0; 51)
     FN_KW@[0; 2)
     WHITESPACE@[2; 3)
index e89ff4e7c518458068dd654c696972b3a36bf773..08ba391e34b56ca5ec346b4a72258209c5ad566d 100644 (file)
@@ -1,4 +1,4 @@
-ROOT@[0; 26)
+SOURCE_FILE@[0; 26)
   FN_DEF@[0; 25)
     FN_KW@[0; 2)
     WHITESPACE@[2; 3)
index 3b5fbf95d222d64d2d98acd676c8d8dd901325ef..7d137cfe947903665070fa14a3a53126cb4eccf2 100644 (file)
@@ -1,4 +1,4 @@
-ROOT@[0; 81)
+SOURCE_FILE@[0; 81)
   STRUCT_DEF@[0; 20)
     VISIBILITY@[0; 10)
       PUB_KW@[0; 3)
index a3d235ce114724bf347e9089c958198380521a91..1edf9412f1189d82b56aabfe9fc374922e2151c0 100644 (file)
@@ -1,4 +1,4 @@
-ROOT@[0; 30)
+SOURCE_FILE@[0; 30)
   FN_DEF@[0; 11)
     FN_KW@[0; 2)
     WHITESPACE@[2; 3)
index 618afc0a98a4439f893db9877174b2cbe78ab22e..673ea1d3d2810c247a3deb20336ff5312531cd11 100644 (file)
@@ -1,4 +1,4 @@
-ROOT@[0; 91)
+SOURCE_FILE@[0; 91)
   FN_DEF@[0; 90)
     FN_KW@[0; 2)
     WHITESPACE@[2; 3)
index 8105ee25a40ff055e903f24132df538fa34bf37d..1d88f952d68e5609d31aecaa136d8b939e468645 100644 (file)
@@ -1,4 +1,4 @@
-ROOT@[0; 189)
+SOURCE_FILE@[0; 189)
   FN_DEF@[0; 188)
     FN_KW@[0; 2)
     WHITESPACE@[2; 3)
index 549b571c23638337871dc0b7fcc7dfba72947380..745cf8798bf07f7e6f8a7c44b63dbd3d9bed53c6 100644 (file)
@@ -1,4 +1,4 @@
-ROOT@[0; 35)
+SOURCE_FILE@[0; 35)
   STRUCT_DEF@[0; 34)
     STRUCT_KW@[0; 6)
     WHITESPACE@[6; 7)
index a75b7ed0aeacf5a1ace63c02642836c21045ce0c..59b3fbd2e802cc2617c437965055a377adc3f30f 100644 (file)
@@ -1,4 +1,4 @@
-ROOT@[0; 19)
+SOURCE_FILE@[0; 19)
   STRUCT_DEF@[0; 18)
     STRUCT_KW@[0; 6)
     WHITESPACE@[6; 7)
index f8e31045a7bafffaa541be77f3c977ff536fe82c..6364dc73c32d50b8469342a674c51a401b8392bd 100644 (file)
@@ -1,4 +1,4 @@
-ROOT@[0; 57)
+SOURCE_FILE@[0; 57)
   FN_DEF@[0; 56)
     FN_KW@[0; 2)
     WHITESPACE@[2; 3)
index 77f2ca22cd2362e866c4702897fcfbc1886548f5..2cd88876271647ccef1092fb3b5138eea18b19dc 100644 (file)
@@ -1,4 +1,4 @@
-ROOT@[0; 52)
+SOURCE_FILE@[0; 52)
   FN_DEF@[0; 51)
     FN_KW@[0; 2)
     WHITESPACE@[2; 3)
index e6c841f2185b84fe05e2d70f2beed3451534c4a2..c13e22b3a9cc138931270645550133a46ed0d5fe 100644 (file)
@@ -1,4 +1,4 @@
-ROOT@[0; 65)
+SOURCE_FILE@[0; 65)
   FN_DEF@[0; 9)
     FN_KW@[0; 2)
     WHITESPACE@[2; 3)
index 19fe5ca85d2aa334fc95d1fe1edfa4dafc65d9ce..f45b6251fa8e8d0cb0e16ed8eaf95dfbf5ce6057 100644 (file)
@@ -1,4 +1,4 @@
-ROOT@[0; 20)
+SOURCE_FILE@[0; 20)
   IMPL_ITEM@[0; 19)
     DEFAULT_KW@[0; 7)
     WHITESPACE@[7; 8)
index 6419243e25b859b827549f51a72ee9b79dbcc79e..1b9a8aa0e51838634ed9b7b0d26379be3a0a1d21 100644 (file)
@@ -1,4 +1,4 @@
-ROOT@[0; 12)
+SOURCE_FILE@[0; 12)
   IMPL_ITEM@[0; 11)
     IMPL_KW@[0; 4)
     WHITESPACE@[4; 5)
index 6739f9c55568d78271e052d44958a5cec1b173e5..b83db380ec0413d6690ecdafe52c2ee4a5ac7d38 100644 (file)
@@ -1,4 +1,4 @@
-ROOT@[0; 20)
+SOURCE_FILE@[0; 20)
   IMPL_ITEM@[0; 19)
     IMPL_KW@[0; 4)
     WHITESPACE@[4; 5)
index 72d9220e81041cf5d374f34c5dbd5b84753166f0..998ac3da9efbbfb66da22ae721c20df7f2a0211b 100644 (file)
@@ -1,4 +1,4 @@
-ROOT@[0; 83)
+SOURCE_FILE@[0; 83)
   IMPL_ITEM@[0; 82)
     IMPL_KW@[0; 4)
     WHITESPACE@[4; 5)
index 30dc83454454cc41f463ce11e152b8462f294d7c..9c2aad7e006560e7fdfef777ae6ac6bf097dfd62 100644 (file)
@@ -1,4 +1,4 @@
-ROOT@[0; 77)
+SOURCE_FILE@[0; 77)
   FN_DEF@[0; 76)
     FN_KW@[0; 2)
     WHITESPACE@[2; 3)
index 530c80fa8016f6e22e2c4191a180479355260f8f..51ecfdf78cebffd63aaef08bbc37b20f69a63ff9 100644 (file)
@@ -1,4 +1,4 @@
-ROOT@[0; 49)
+SOURCE_FILE@[0; 49)
   FN_DEF@[0; 48)
     FN_KW@[0; 2)
     WHITESPACE@[2; 3)
index 9086219caa2cd8d796b8f5210149e4080f3f0c32..03fbf092b08d86abeb842ba5b8f28d54a6bd982f 100644 (file)
@@ -1,4 +1,4 @@
-ROOT@[0; 37)
+SOURCE_FILE@[0; 37)
   FN_DEF@[0; 36)
     FN_KW@[0; 2)
     WHITESPACE@[2; 3)
index 20122d763d051fc5eb9946f0a037ae18e74f223d..dd1843ee521126e256358ca9e85bc107f560d6be 100644 (file)
@@ -1,4 +1,4 @@
-ROOT@[0; 21)
+SOURCE_FILE@[0; 21)
   FN_DEF@[0; 20)
     FN_KW@[0; 2)
     WHITESPACE@[2; 3)
index 4df4ca35b98516da882f912f1efc9d6037f33a27..13a51a9d97f9375e8df3e809940a0c0c03cc8347 100644 (file)
@@ -1,4 +1,4 @@
-ROOT@[0; 119)
+SOURCE_FILE@[0; 119)
   FN_DEF@[0; 118)
     FN_KW@[0; 2)
     WHITESPACE@[2; 3)
index f0e47c2dcbffc86516f5a3a8af8b42d6f812dfbc..53027c852da858feb1430266f746a7086f43658c 100644 (file)
@@ -1,4 +1,4 @@
-ROOT@[0; 128)
+SOURCE_FILE@[0; 128)
   IMPL_ITEM@[0; 127)
     IMPL_KW@[0; 4)
     WHITESPACE@[4; 5)
index 40c77db4b8fd0533c3c193624b788895cae91d73..333a737eca919b2f8bdf7f7afedbc2c5070079e0 100644 (file)
@@ -1,4 +1,4 @@
-ROOT@[0; 42)
+SOURCE_FILE@[0; 42)
   TRAIT_DEF@[0; 41)
     TRAIT_KW@[0; 5)
     WHITESPACE@[5; 6)
index 0303d76bdf61fac22ef4f0d393e3928b32f20207..8b3ce28d06da8f6e18ac24448e6af0a2e42c35f6 100644 (file)
@@ -1,4 +1,4 @@
-ROOT@[0; 16)
+SOURCE_FILE@[0; 16)
   TRAIT_DEF@[0; 15)
     AUTO_KW@[0; 4)
     WHITESPACE@[4; 5)
index aaf2924ba33f2e311280d95ae06a508816cd6249..086a74bdbfe08c75daae6b100509d3e240725f45 100644 (file)
@@ -1,4 +1,4 @@
-ROOT@[0; 35)
+SOURCE_FILE@[0; 35)
   TYPE_DEF@[0; 34)
     TYPE_KW@[0; 4)
     WHITESPACE@[4; 5)
index 281e79deb1d007713a18ec50951261ce1625ac3a..b58d09e35ee004fff8aed6abd29912a21a10fbef 100644 (file)
@@ -1,4 +1,4 @@
-ROOT@[0; 29)
+SOURCE_FILE@[0; 29)
   FN_DEF@[0; 28)
     FN_KW@[0; 2)
     WHITESPACE@[2; 3)
index b6fea5ab245961e96cd1907e0c13a0f23acd78af..b20f13267cf0679421a7f43af11f20528159488b 100644 (file)
@@ -1,4 +1,4 @@
-ROOT@[0; 28)
+SOURCE_FILE@[0; 28)
   FN_DEF@[0; 27)
     FN_KW@[0; 2)
     WHITESPACE@[2; 3)
index cf6e881b27ad5c15fa0129eadd22afd498dbdf43..aec25403d1a44e76ed374b788d9e6e8a3125a2d0 100644 (file)
@@ -1,4 +1,4 @@
-ROOT@[0; 86)
+SOURCE_FILE@[0; 86)
   FN_DEF@[0; 85)
     FN_KW@[0; 2)
     WHITESPACE@[2; 3)
index 68fce58ee5bffda624e485ce9cc62b1b30eae947..efd4dd42acaa51655168f22330aea5e2da784b56 100644 (file)
@@ -1,4 +1,4 @@
-ROOT@[0; 43)
+SOURCE_FILE@[0; 43)
   TYPE_DEF@[0; 42)
     TYPE_KW@[0; 4)
     WHITESPACE@[4; 5)
index 93f6285acb1409f742e6319db02dade55e0d3b03..8c1340c4ae781fdd2e39df9fdcedc9d48afa4325 100644 (file)
@@ -1,4 +1,4 @@
-ROOT@[0; 74)
+SOURCE_FILE@[0; 74)
   FN_DEF@[0; 74)
     FN_KW@[0; 2)
     NAME@[2; 6)
index de547d69986d53472e3c650e763b7ffc17ef9ff6..503fc97ff19fe17ba27aecc21dcbd5c0c95144a3 100644 (file)
@@ -1,4 +1,4 @@
-ROOT@[0; 67)
+SOURCE_FILE@[0; 67)
   FN_DEF@[0; 9)
     FN_KW@[0; 2)
     WHITESPACE@[2; 3)
index ae1a8101eb7af7a8cf62486e4fb7f2a15ce2646c..637d23d9ef07708fee90300aa8148648e0b9d88c 100644 (file)
@@ -1,4 +1,4 @@
-ROOT@[0; 107)
+SOURCE_FILE@[0; 107)
   FN_DEF@[0; 106)
     FN_KW@[0; 2)
     WHITESPACE@[2; 3)
index 82aaf38973383f6ade944346f818323d9804f51a..98271c233e5209bc972511881c7c35a28d2a295d 100644 (file)
@@ -1,4 +1,4 @@
-ROOT@[0; 79)
+SOURCE_FILE@[0; 79)
   FN_DEF@[0; 78)
     FN_KW@[0; 2)
     WHITESPACE@[2; 3)
index 12a1ff49aa2d9c10bf2f9f6f7f58a3814b6f5eee..eeb184ab25d483a8ef69b0b7dcc38fb6c1b831da 100644 (file)
@@ -1,4 +1,4 @@
-ROOT@[0; 52)
+SOURCE_FILE@[0; 52)
   FN_DEF@[0; 51)
     FN_KW@[0; 2)
     WHITESPACE@[2; 3)
index 5f179c3ca4c4bfa90da42e30886383ae752aef1c..de20eee7a0fb9b2d35d87697f4c6fb392b8308b2 100644 (file)
@@ -1,4 +1,4 @@
-ROOT@[0; 29)
+SOURCE_FILE@[0; 29)
   FN_DEF@[0; 20)
     FN_KW@[0; 2)
     WHITESPACE@[2; 3)
index ead1ada9632b096b2151daecc9651d7f3e9099b2..f8d1db41ce558ec3f911fd82530175edf0c244df 100644 (file)
@@ -1,4 +1,4 @@
-ROOT@[0; 40)
+SOURCE_FILE@[0; 40)
   FN_DEF@[0; 39)
     FN_KW@[0; 2)
     WHITESPACE@[2; 3)
index f862b126836543ef8de3275d55a578b396b97e63..044faec0492af5aa5839e46a69ed48ef3f091502 100644 (file)
@@ -1,4 +1,4 @@
-ROOT@[0; 125)
+SOURCE_FILE@[0; 125)
   FN_DEF@[0; 124)
     FN_KW@[0; 2)
     WHITESPACE@[2; 3)
index d7740e3cca5be10fff64ff5a97293984008ff012..cc22bd3b26903e06609664d0bb1c8768cbee96cf 100644 (file)
@@ -1,4 +1,4 @@
-ROOT@[0; 47)
+SOURCE_FILE@[0; 47)
   FN_DEF@[0; 46)
     FN_KW@[0; 2)
     WHITESPACE@[2; 3)
index 734e0726c5ab9c80b51a83a9bbb200acfa851ae7..737fbec22ab8fa8028d41fedcc745f0108c1ade4 100644 (file)
@@ -1,4 +1,4 @@
-ROOT@[0; 97)
+SOURCE_FILE@[0; 97)
   FN_DEF@[0; 96)
     FN_KW@[0; 2)
     WHITESPACE@[2; 3)
index 1a8d1546d4ce0d7a0d47f97b587c6a408b5b1fdd..0020cfb4dceca60a7293287b2f4f5c264dbc0334 100644 (file)
@@ -1,4 +1,4 @@
-ROOT@[0; 103)
+SOURCE_FILE@[0; 103)
   FN_DEF@[0; 102)
     FN_KW@[0; 2)
     WHITESPACE@[2; 3)
index 1f524f34ababa95dffe7658065ef0563be53e227..b15f93cd2a889efd4fdb5911243c276a0278b045 100644 (file)
@@ -1,4 +1,4 @@
-ROOT@[0; 89)
+SOURCE_FILE@[0; 89)
   IMPL_ITEM@[0; 88)
     IMPL_KW@[0; 4)
     WHITESPACE@[4; 5)
index 4d77c8c11879069cb287e7d2fb0d749e600f463d..72d7d8bfbe8f22ba9b0cfbd43e323caa4827df07 100644 (file)
@@ -1,4 +1,4 @@
-ROOT@[0; 44)
+SOURCE_FILE@[0; 44)
   FN_DEF@[0; 43)
     FN_KW@[0; 2)
     WHITESPACE@[2; 3)
index b802351469ae40e76236dbf6c515be8f120b80a3..f47e6da55b5bec31265a81940c6662569b4f1011 100644 (file)
@@ -1,4 +1,4 @@
-ROOT@[0; 21)
+SOURCE_FILE@[0; 21)
   FN_DEF@[0; 20)
     FN_KW@[0; 2)
     WHITESPACE@[2; 3)
index 1808f10fbe53eae3a5a58c159f6a382f2b88107c..641a09fff5ff54feb237b9478d2b0725e2d1449d 100644 (file)
@@ -1,4 +1,4 @@
-ROOT@[0; 38)
+SOURCE_FILE@[0; 38)
   FN_DEF@[0; 37)
     FN_KW@[0; 2)
     WHITESPACE@[2; 3)
index c73157c043721d9eeb159642bcfe0041e8336fbd..f9b074e8256de09f6c3d333b0a8f42f0b0de16e6 100644 (file)
@@ -1,4 +1,4 @@
-ROOT@[0; 70)
+SOURCE_FILE@[0; 70)
   FN_DEF@[0; 69)
     FN_KW@[0; 2)
     WHITESPACE@[2; 3)
index 57b2b93729640a7c92565e3f9c13c73de46ccf3c..62528ca47c90b67bc7d47cae3e34f4308d73b059 100644 (file)
@@ -1,4 +1,4 @@
-ROOT@[0; 70)
+SOURCE_FILE@[0; 70)
   FN_DEF@[0; 11)
     FN_KW@[0; 2)
     WHITESPACE@[2; 3)
index 6d57078b34acd6281f16bd4b83c70e06f30cf622..a8043991341297f6aaf7513d63955ee765f34f75 100644 (file)
@@ -1,4 +1,4 @@
-ROOT@[0; 28)
+SOURCE_FILE@[0; 28)
   FN_DEF@[0; 27)
     FN_KW@[0; 2)
     WHITESPACE@[2; 3)
index cf541411fa3e5ed4b28e61b93efa39b7e1edd9c9..4e427c9097d402712bb53a993491392a95a4ebe9 100644 (file)
@@ -1,4 +1,4 @@
-ROOT@[0; 40)
+SOURCE_FILE@[0; 40)
   FN_DEF@[0; 39)
     FN_KW@[0; 2)
     WHITESPACE@[2; 3)
index 4624aab23daed8a12acb96038a27ce665fe25d79..6e10be559dfeab234e68507575493c21ea66dfae 100644 (file)
@@ -1,4 +1,4 @@
-ROOT@[0; 26)
+SOURCE_FILE@[0; 26)
   FN_DEF@[0; 25)
     FN_KW@[0; 2)
     WHITESPACE@[2; 3)
index 2d68a5ff270412aba16d04bdf9c0e0a00fa5ef81..2471f6f8cd9057da4e38d5ecb702767fa77ef81e 100644 (file)
@@ -1,4 +1,4 @@
-ROOT@[0; 39)
+SOURCE_FILE@[0; 39)
   FN_DEF@[0; 38)
     FN_KW@[0; 2)
     WHITESPACE@[2; 3)
index 475251ea72bc7c9b6c8c0d101039283721d70b98..415ff2a20d87c3bf8f84ec61b649f758b2736f95 100644 (file)
@@ -1,4 +1,4 @@
-ROOT@[0; 26)
+SOURCE_FILE@[0; 26)
   FN_DEF@[0; 25)
     FN_KW@[0; 2)
     WHITESPACE@[2; 3)
index 5321fade998a667513c0846aaad8aaafb6323392..c3c64c3222730824130ec98821e19725adfc78cd 100644 (file)
@@ -1,4 +1,4 @@
-ROOT@[0; 26)
+SOURCE_FILE@[0; 26)
   FN_DEF@[0; 25)
     FN_KW@[0; 2)
     WHITESPACE@[2; 3)
index 7ddf8122fb4f6ba47f4485281688186ccb8c1615..998f1211040efa6c074c6805f263007f938fe3a1 100644 (file)
@@ -1,4 +1,4 @@
-ROOT@[0; 33)
+SOURCE_FILE@[0; 33)
   FN_DEF@[0; 32)
     FN_KW@[0; 2)
     WHITESPACE@[2; 3)
index 560931645fca7aaca73d3768f7c01675668dad1c..1034846c8fe933768fc60619e3d740e3475f1d11 100644 (file)
@@ -1,4 +1,4 @@
-ROOT@[0; 83)
+SOURCE_FILE@[0; 83)
   FN_DEF@[0; 82)
     FN_KW@[0; 2)
     WHITESPACE@[2; 3)
index dcdeff6096235bf4e21cb0fef7dad0e3ad8341d0..42df7c450ae8bd35d182ed8abf9068924fb88fb5 100644 (file)
@@ -1,4 +1,4 @@
-ROOT@[0; 55)
+SOURCE_FILE@[0; 55)
   FN_DEF@[0; 54)
     FN_KW@[0; 2)
     WHITESPACE@[2; 3)
index f1b0e27878ac756f26661ab83809e8e95cdd6257..69f0a48ced8326da1d07b237733baa6f6caa2ce5 100644 (file)
@@ -1,4 +1,4 @@
-ROOT@[0; 107)
+SOURCE_FILE@[0; 107)
   FN_DEF@[0; 106)
     FN_KW@[0; 2)
     WHITESPACE@[2; 3)
index 91edf3f84dc7853c0439f7fef55c3a731e80a1f8..48e0026af027141a5ebdf0b48810db52fcac85ac 100644 (file)
@@ -1,4 +1,4 @@
-ROOT@[0; 84)
+SOURCE_FILE@[0; 84)
   FN_DEF@[0; 83)
     FN_KW@[0; 2)
     WHITESPACE@[2; 3)
index ff1298acb33e3bc973ff2f166ee6c164ec0a687e..d8c5995376a25e9785c8a231bbadea5c33bae6d2 100644 (file)
@@ -1,4 +1,4 @@
-ROOT@[0; 46)
+SOURCE_FILE@[0; 46)
   FN_DEF@[0; 45)
     FN_KW@[0; 2)
     WHITESPACE@[2; 3)
index 99e8dd4279405804b6bae9e6532d60b8ce7be8dc..72987308dfa02884250a8d2097996fb37e6d8b43 100644 (file)
@@ -1,4 +1,4 @@
-ROOT@[0; 39)
+SOURCE_FILE@[0; 39)
   FN_DEF@[0; 38)
     FN_KW@[0; 2)
     WHITESPACE@[2; 3)
index c35bc8428d0d4355a0a6a6bffd926f820c9c012a..bb87e1d06ce57b994f0070f2ea6e1e2551461cb8 100644 (file)
@@ -1,4 +1,4 @@
-ROOT@[0; 22)
+SOURCE_FILE@[0; 22)
   TRAIT_DEF@[0; 21)
     TRAIT_KW@[0; 5)
     WHITESPACE@[5; 6)
index 3128bfd7d9b17c54e752b78034abe39d13ed4414..99b289d562e69953b7c6f85d85c959892d646c17 100644 (file)
@@ -1,4 +1,4 @@
-ROOT@[0; 95)
+SOURCE_FILE@[0; 95)
   FN_DEF@[0; 94)
     FN_KW@[0; 2)
     WHITESPACE@[2; 3)
index 26a690d3ad16d418a9bbc973a5bd91539d604878..ba1163c2dee9510bfcb7745b01cde87ef8046ea1 100644 (file)
@@ -1,4 +1,4 @@
-ROOT@[0; 32)
+SOURCE_FILE@[0; 32)
   TYPE_DEF@[0; 31)
     TYPE_KW@[0; 4)
     WHITESPACE@[4; 5)
index 4f14d5431589ec16625bf23555403ec5c9424483..12ccc1314b1a3a2da148d79dafac48715531ad62 100644 (file)
@@ -1,4 +1,4 @@
-ROOT@[0; 47)
+SOURCE_FILE@[0; 47)
   FN_DEF@[0; 46)
     FN_KW@[0; 2)
     WHITESPACE@[2; 3)
index 4b864f74150e95d9643cee104c20f6bc1155b774..911a27ea1b4c766e3e4aa5a4c7ad1f1a6c122fd8 100644 (file)
@@ -1,4 +1,4 @@
-ROOT@[0; 27)
+SOURCE_FILE@[0; 27)
   FN_DEF@[0; 26)
     FN_KW@[0; 2)
     WHITESPACE@[2; 3)
index 3c83520679070ee9a874ad610cb18393e2b8fba3..f2d7e866bfc3d285cc4cd10f9564fced837c2840 100644 (file)
@@ -1,4 +1,4 @@
-ROOT@[0; 54)
+SOURCE_FILE@[0; 54)
   TYPE_DEF@[0; 53)
     TYPE_KW@[0; 4)
     WHITESPACE@[4; 5)
index 39bab8dd21ed21502f586473bf65beb7cd8abc57..2ce7a6714e8742fb62476638de32727158b25c2c 100644 (file)
@@ -1,4 +1,4 @@
-ROOT@[0; 35)
+SOURCE_FILE@[0; 35)
   FN_DEF@[0; 34)
     FN_KW@[0; 2)
     WHITESPACE@[2; 3)
index 9fe8035541651962865c6e85c3a9c9b4b5c82992..54c3d64f1e0807d6d055a8201c9dddd962f356b0 100644 (file)
@@ -1,4 +1,4 @@
-ROOT@[0; 87)
+SOURCE_FILE@[0; 87)
   FN_DEF@[0; 86)
     FN_KW@[0; 2)
     WHITESPACE@[2; 3)
index 96a4e91625c4c59eb290824f2dd55b75b4629f01..ff5add838ffde75c8498b671157d61395dbd0c10 100644 (file)
@@ -1,4 +1,4 @@
-ROOT@[0; 20)
+SOURCE_FILE@[0; 20)
   FN_DEF@[0; 19)
     VISIBILITY@[0; 5)
       CRATE_KW@[0; 5)
index c81ae55450da63750a4ba7333a48f46518e7cdc1..d07fe70b26b7d3de74ea0f9c9a9bfea29276896e 100644 (file)
@@ -1,4 +1,4 @@
-ROOT@[0; 42)
+SOURCE_FILE@[0; 42)
   TYPE_DEF@[0; 41)
     TYPE_KW@[0; 4)
     WHITESPACE@[4; 5)
index 99516bf150566122831447d2501147503516d1ee..394fc7f5b07896257dd10a4d347f82f807d6586c 100644 (file)
@@ -1,4 +1,4 @@
-ROOT@[0; 71)
+SOURCE_FILE@[0; 71)
   TYPE_DEF@[0; 26)
     TYPE_KW@[0; 4)
     WHITESPACE@[4; 5)
index 19f77fb735f60b4272b7d5e309c2b7b31897fdc8..caef145ced4a4b3045acd244ff323d641109beb7 100644 (file)
@@ -1,4 +1,4 @@
-ROOT@[0; 21)
+SOURCE_FILE@[0; 21)
   FN_DEF@[0; 20)
     FN_KW@[0; 2)
     WHITESPACE@[2; 3)
index 420712e898613dec27ae43cd8079c8d4aa11066a..e02ebf40066adccd074bd4dd2a27732a0408c603 100644 (file)
@@ -1,4 +1,4 @@
-ROOT@[0; 64)
+SOURCE_FILE@[0; 64)
   STRUCT_DEF@[0; 63)
     STRUCT_KW@[0; 6)
     WHITESPACE@[6; 7)
index 9a306921e8b5bc4dc6aa270be7ec4b74f2f46e01..b2f04ea7bbb74048ccbca1e8a5f121a99f6fa032 100644 (file)
@@ -1,4 +1,4 @@
-ROOT@[0; 69)
+SOURCE_FILE@[0; 69)
   IMPL_ITEM@[0; 68)
     IMPL_KW@[0; 4)
     WHITESPACE@[4; 5)
index 1c78704faf55924390e810f3812b0e1f1b1f195e..39be5b2c899acde9c8e8408c127cba501bc41f16 100644 (file)
@@ -1,4 +1,4 @@
-ROOT@[0; 69)
+SOURCE_FILE@[0; 69)
   FN_DEF@[0; 68)
     FN_KW@[0; 2)
     WHITESPACE@[2; 3)
index 92b6b8bea32a5e1f0ec6d899cff5dff0a00d83dc..1877311e87b8a0300ab5a6d6714bfcca11684ce5 100644 (file)
@@ -1,4 +1,4 @@
-ROOT@[0; 102)
+SOURCE_FILE@[0; 102)
   FN_DEF@[0; 101)
     FN_KW@[0; 2)
     WHITESPACE@[2; 3)
index 7685155955d1a65e99d486a0cfd357e93a19d0ac..c641c3b161bb0c89dd3feaff2860dab31d89b410 100644 (file)
@@ -1,4 +1,4 @@
-ROOT@[0; 74)
+SOURCE_FILE@[0; 74)
   FN_DEF@[0; 73)
     FN_KW@[0; 2)
     WHITESPACE@[2; 3)
index d00305b9732b50c1df8b3f3c9ae4a67c63f1c002..75533ecc18a903f211638fb92f59230bb89b6749 100644 (file)
@@ -1,4 +1,4 @@
-ROOT@[0; 30)
+SOURCE_FILE@[0; 30)
   FN_DEF@[0; 29)
     FN_KW@[0; 2)
     WHITESPACE@[2; 3)
index f9b34cc5ea2c808c2569b27d9d1947b5dccd61eb..058639375c9e91be643e48b735c6d99d0ee3270a 100644 (file)
@@ -1,4 +1,4 @@
-ROOT@[0; 106)
+SOURCE_FILE@[0; 106)
   STRUCT_DEF@[0; 11)
     STRUCT_KW@[0; 6)
     WHITESPACE@[6; 7)
index e2e755414a7064d7ede264fb00c31627af1c42f4..2d2fb1a0bc4742392bbc302c16a71c30568b583c 100644 (file)
@@ -1,4 +1,4 @@
-ROOT@[0; 51)
+SOURCE_FILE@[0; 51)
   STRUCT_DEF@[0; 12)
     UNION_KW@[0; 5)
     WHITESPACE@[5; 6)
index a2907b060c75435cd21d9bffbadb4de394450d50..7279d5cae4eefdc0de0bb28500b61a2ab995f7a7 100644 (file)
@@ -1,4 +1,4 @@
-ROOT@[0; 87)
+SOURCE_FILE@[0; 87)
   IMPL_ITEM@[0; 12)
     IMPL_KW@[0; 4)
     WHITESPACE@[4; 5)
index 4c871ccdde014ccfe4ce15cb0e84bda45420238e..38228645722b368b616d67842045c605a1f96074 100644 (file)
@@ -1,4 +1,4 @@
-ROOT@[0; 16)
+SOURCE_FILE@[0; 16)
   USE_ITEM@[0; 15)
     USE_KW@[0; 3)
     WHITESPACE@[3; 4)
index 08aacc77ab9fbb2bdaf8d01039e5ded03faf94f1..921bdacf4db8925de71af72bf0e8c0012346b585 100644 (file)
@@ -1,4 +1,4 @@
-ROOT@[0; 49)
+SOURCE_FILE@[0; 49)
   FN_DEF@[0; 48)
     FN_KW@[0; 2)
     WHITESPACE@[2; 3)
index 9f505ee960e78cbe9942c438d413cc017cb07489..08f5a942f762f6729e34119e154749165553bace 100644 (file)
@@ -1 +1 @@
-ROOT@[0; 0)
+SOURCE_FILE@[0; 0)
index aa2f1b7125a0d6a756a8e32d659a09c10a091281..12497e1a9b777a0da9dc8d3e58257d00e6611a1a 100644 (file)
@@ -1,4 +1,4 @@
-ROOT@[0; 32)
+SOURCE_FILE@[0; 32)
   STRUCT_DEF@[0; 31)
     STRUCT_KW@[0; 6)
     WHITESPACE@[6; 7)
index 0b65387ef2adf3eb2db1cc1d39a9947f2d176709..2f588c598bcfe1cf0af499f9539ba62dce9f58ec 100644 (file)
@@ -1,4 +1,4 @@
-ROOT@[0; 25)
+SOURCE_FILE@[0; 25)
   STRUCT_DEF@[0; 25)
     STRUCT_KW@[0; 6)
     WHITESPACE@[6; 7)
index bb8874eada33c93ac981ab0ecc506275e5671094..31395ffc26c4464c8c4b8d91d965910e998da53a 100644 (file)
@@ -1,2 +1,2 @@
-ROOT@[0; 20)
+SOURCE_FILE@[0; 20)
   SHEBANG@[0; 20)
index 8ed92f2a4335815b9c9bb4101d601d3368ad58b4..c5eb46888cd70b075813e7a2faab396893832f3d 100644 (file)
@@ -1,4 +1,4 @@
-ROOT@[0; 13)
+SOURCE_FILE@[0; 13)
   FN_DEF@[0; 12)
     FN_KW@[0; 2)
     WHITESPACE@[2; 3)
index 2b64c497fd895b1f2d04b13d91435312d613f256..05b114b54a4cda0f7c895545443be4ea049846d9 100644 (file)
@@ -1,4 +1,4 @@
-ROOT@[0; 236)
+SOURCE_FILE@[0; 236)
   ATTR@[0; 8)
     POUND@[0; 1)
     EXCL@[1; 2)
index 12193397e02b336cf5b652df623c4a0c80edd8c3..5adf3f2769c68f69cd85d272066544bf382c3782 100644 (file)
@@ -1,4 +1,4 @@
-ROOT@[0; 43)
+SOURCE_FILE@[0; 43)
   EXTERN_CRATE_ITEM@[0; 17)
     EXTERN_KW@[0; 6)
     WHITESPACE@[6; 7)
index c790f84312d0af7618d32e324c603381581c9fd4..139349fedbbf7d63fc00847f64a2e85425d772aa 100644 (file)
@@ -1,4 +1,4 @@
-ROOT@[0; 118)
+SOURCE_FILE@[0; 118)
   MODULE@[0; 6)
     MOD_KW@[0; 3)
     WHITESPACE@[3; 4)
index 4bcd15357be2c05838aebf5063757d26e680978d..9e67d176a4814d4c2f32e3916650a22133d63d1c 100644 (file)
@@ -1,4 +1,4 @@
-ROOT@[0; 19)
+SOURCE_FILE@[0; 19)
   USE_ITEM@[0; 8)
     USE_KW@[0; 3)
     WHITESPACE@[3; 4)
index f76ea1a352199011f210701957af488eee64ff14..d0d2c17ed7f38172e89f456bf5a1bd96d0d2305b 100644 (file)
@@ -1,4 +1,4 @@
-ROOT@[0; 40)
+SOURCE_FILE@[0; 40)
   USE_ITEM@[0; 20)
     USE_KW@[0; 3)
     WHITESPACE@[3; 4)
index 203a0719392fe5334cc02a194aff198b5ccd771f..0f5f37474d078df0e7426e71627aed1b2b5f316e 100644 (file)
@@ -1,4 +1,4 @@
-ROOT@[0; 35)
+SOURCE_FILE@[0; 35)
   FN_DEF@[0; 34)
     ATTR@[0; 12)
       POUND@[0; 1)
index 66901af5419189e2f6db3dae6c7fc97adfe5f945..15f547d29d67dbbc944f9f69910236fb77fd812c 100644 (file)
@@ -1,4 +1,4 @@
-ROOT@[0; 98)
+SOURCE_FILE@[0; 98)
   FN_DEF@[0; 9)
     FN_KW@[0; 2)
     WHITESPACE@[2; 3)
index 95be4f75d271161769051ed3c4a7a2bb2b8ef3a2..1ed498f822243a59db235279caef60317e77c9b2 100644 (file)
@@ -1,4 +1,4 @@
-ROOT@[0; 65)
+SOURCE_FILE@[0; 65)
   USE_ITEM@[0; 14)
     USE_KW@[0; 3)
     WHITESPACE@[3; 4)
index 4aa5391595aa8c49afd6222bfd6420e0be4899da..55eee5acc4b3a5c8004c100dc6f2815a5cffb06f 100644 (file)
@@ -1,4 +1,4 @@
-ROOT@[0; 81)
+SOURCE_FILE@[0; 81)
   USE_ITEM@[0; 6)
     USE_KW@[0; 3)
     WHITESPACE@[3; 4)
index aa4ef37159411ad55532a0bb253747c2e91deca7..8a41176736ae2a82cca463d5f401b08e92fb43e8 100644 (file)
@@ -1,4 +1,4 @@
-ROOT@[0; 55)
+SOURCE_FILE@[0; 55)
   USE_ITEM@[0; 15)
     USE_KW@[0; 3)
     WHITESPACE@[3; 4)
index 34948115712f71f23f95d635773de20ede4fd4e7..1d355823d644138ebde60a0dbdc510f579f1586a 100644 (file)
@@ -1,4 +1,4 @@
-ROOT@[0; 97)
+SOURCE_FILE@[0; 97)
   STRUCT_DEF@[0; 9)
     STRUCT_KW@[0; 6)
     WHITESPACE@[6; 7)
index 8cfd77256d1e63ed951208d9befa58860c61c259..d49341bc530fc9ed30db3bdbcab2b3f3cbce80dc 100644 (file)
@@ -1,4 +1,4 @@
-ROOT@[0; 23)
+SOURCE_FILE@[0; 23)
   FN_DEF@[0; 22)
     ATTR@[0; 10)
       POUND@[0; 1)
index a98e823f6b8b57b1d61f191fe0fb964539fb0233..491d154dfa40de90446ebb84048b79b8bdef3002 100644 (file)
@@ -1,4 +1,4 @@
-ROOT@[0; 290)
+SOURCE_FILE@[0; 290)
   STRUCT_DEF@[0; 13)
     STRUCT_KW@[0; 6)
     WHITESPACE@[6; 7)
index 6333e47c5249d133a2257d31c32c42f1b1018fbe..6925219e8a088c21270a8b241d85df1091ab9c38 100644 (file)
@@ -1,4 +1,4 @@
-ROOT@[0; 182)
+SOURCE_FILE@[0; 182)
   ENUM_DEF@[0; 11)
     ENUM_KW@[0; 4)
     WHITESPACE@[4; 5)
index ac1a58e9abc103dae4f8347125d6268ab55e9cad..b70a1d19a48446a371f9786629666521f8f37a64 100644 (file)
@@ -1,4 +1,4 @@
-ROOT@[0; 200)
+SOURCE_FILE@[0; 200)
   STRUCT_DEF@[0; 12)
     STRUCT_KW@[0; 6)
     WHITESPACE@[6; 7)
index 5ceef46e00cd0ddfb97bd250bee6c4c90d7e62fa..6644d6ca23710e3864f9d31fc6d617d324bb538c 100644 (file)
@@ -1,4 +1,4 @@
-ROOT@[0; 71)
+SOURCE_FILE@[0; 71)
   FN_DEF@[0; 19)
     ABI@[0; 6)
       EXTERN_KW@[0; 6)
index 2099e2433879a85ff845264d9d515c7c0e0a728c..8f044e0b1e8fef5b9c667b4c346f25755eed3d1a 100644 (file)
@@ -1,4 +1,4 @@
-ROOT@[0; 27)
+SOURCE_FILE@[0; 27)
   EXTERN_BLOCK@[0; 10)
     ABI@[0; 6)
       EXTERN_KW@[0; 6)
index 5c1a3a7a5096fd0d712f7a4838c0c64db9019fb2..f7b107d1d3bb6f19f49bc44089d3c6b0b3204eab 100644 (file)
@@ -1,4 +1,4 @@
-ROOT@[0; 47)
+SOURCE_FILE@[0; 47)
   STATIC_DEF@[0; 20)
     STATIC_KW@[0; 6)
     WHITESPACE@[6; 7)
index d0e5842da189852c3c31efe8f5c0519f377b2932..9cebc46731216ec824ab3823f601c4e26ae05591 100644 (file)
@@ -1,4 +1,4 @@
-ROOT@[0; 46)
+SOURCE_FILE@[0; 46)
   CONST_DEF@[0; 20)
     CONST_KW@[0; 5)
     WHITESPACE@[5; 6)
index 363b3576e1a128aa5bee6a165b8c64f3d2443ef8..e34ae9c6544edbef7e4dd204fd50802f601cd154 100644 (file)
@@ -1,4 +1,4 @@
-ROOT@[0; 35)
+SOURCE_FILE@[0; 35)
   FN_DEF@[0; 34)
     FN_KW@[0; 2)
     WHITESPACE@[2; 3)
index d45fc25594d5fc94a0e765f02097cbcb1296ac05..6837c05ff72b24298b160602ba102a9fcc19dc60 100644 (file)
@@ -1,4 +1,4 @@
-ROOT@[0; 34)
+SOURCE_FILE@[0; 34)
   FN_DEF@[0; 33)
     FN_KW@[0; 2)
     WHITESPACE@[2; 3)
index 5a39fa98c522dbeb8dcda71499afcc38715dc6d1..fe1a1ccbf26449336f173bcedf84004f23ae6a03 100644 (file)
@@ -1,4 +1,4 @@
-ROOT@[0; 53)
+SOURCE_FILE@[0; 53)
   FN_DEF@[0; 52)
     FN_KW@[0; 2)
     WHITESPACE@[2; 3)
index 27efd180f3e47d0515eac9d7695acefdc179d516..1427e08fcb9b3fb4664ec99daeb0d7113eaa1fc3 100644 (file)
@@ -1,4 +1,4 @@
-ROOT@[0; 248)
+SOURCE_FILE@[0; 248)
   FN_DEF@[0; 247)
     FN_KW@[0; 2)
     WHITESPACE@[2; 3)
index 6d6d2f4d07b8f13dc87c749542e70c6c7e58c8ce..2e7703c213eb021d7b87488397e499e520b8f340 100644 (file)
@@ -1,4 +1,4 @@
-ROOT@[0; 79)
+SOURCE_FILE@[0; 79)
   FN_DEF@[0; 78)
     FN_KW@[0; 2)
     WHITESPACE@[2; 3)
index ddd66640761df642472abef48fa47cc55b845765..b89f348844f0545bb66ef8186f7de13a7d2a3695 100644 (file)
@@ -1,4 +1,4 @@
-ROOT@[0; 164)
+SOURCE_FILE@[0; 164)
   TRAIT_DEF@[0; 66)
     VISIBILITY@[0; 3)
       PUB_KW@[0; 3)
index f3f1c25a9c5cfedb6a81b59e8fafb7c48bd95e1d..9091c08e2b70b844f3696072784a8f006c113799 100644 (file)
@@ -1,4 +1,4 @@
-ROOT@[0; 1598)
+SOURCE_FILE@[0; 1598)
   EXTERN_BLOCK@[0; 1597)
     ABI@[0; 6)
       EXTERN_KW@[0; 6)
index e3b9386a8031daf1f07057be6fe2948e06fb5bdc..150a9e48363de8ed5208a3c6fe3d03cc6104ae2a 100644 (file)
@@ -1,4 +1,4 @@
-ROOT@[0; 116)
+SOURCE_FILE@[0; 116)
   FN_DEF@[0; 115)
     FN_KW@[0; 2)
     WHITESPACE@[2; 3)
index 201eca644eb5682569ece56348720b556074703f..1c2bf82c920c3e50edd7047b28f4b26739853de9 100644 (file)
@@ -1,4 +1,4 @@
-ROOT@[0; 506)
+SOURCE_FILE@[0; 506)
   FN_DEF@[0; 505)
     COMMENT@[0; 33)
     WHITESPACE@[33; 34)
index 74af9d71b4926738ac510fc8c456e30217cf3845..caff8df76a49935b8e856d63352b132299e9ae4a 100644 (file)
@@ -1,4 +1,4 @@
-ROOT@[0; 350)
+SOURCE_FILE@[0; 350)
   MACRO_CALL@[0; 41)
     PATH@[0; 5)
       PATH_SEGMENT@[0; 5)
index 1a8ca761dd1226bb2c7fbbe498421550d2c54e50..cf9a1a2fac2180315511a9a6a9d57d8fe2071c0d 100644 (file)
@@ -1,4 +1,4 @@
-ROOT@[0; 350)
+SOURCE_FILE@[0; 350)
   MACRO_CALL@[0; 41)
     PATH@[0; 5)
       PATH_SEGMENT@[0; 5)
index 36431518016fe8fc2fbd993a16faaf05bdaee20f..3995f476c2e3142970dbc1234fb138ca5b7b3bb9 100644 (file)
@@ -1,4 +1,4 @@
-ROOT@[0; 62)
+SOURCE_FILE@[0; 62)
   FN_DEF@[0; 61)
     FN_KW@[0; 2)
     WHITESPACE@[2; 3)
index 9d1ded093b898f7c734debb000f31c60a05bee03..67acc9020a4d3930b905b7bc991d4461508e729a 100644 (file)
@@ -11,7 +11,7 @@
 
 use ra_syntax::{
     utils::{check_fuzz_invariants, dump_tree},
-    File,
+    SourceFileNode,
 };
 
 #[test]
@@ -25,7 +25,7 @@ fn lexer_tests() {
 #[test]
 fn parser_tests() {
     dir_tests(&["parser/inline", "parser/ok", "parser/err"], |text| {
-        let file = File::parse(text);
+        let file = SourceFileNode::parse(text);
         dump_tree(file.syntax())
     })
 }
index a7a22fa6fa3c143ef4c7fadbb7d86db76d7baa35..75e273f3739164cd34ab244db0f55b0de762aa14 100644 (file)
@@ -20,7 +20,10 @@ export class Server {
             debug: run
         };
         const clientOptions: lc.LanguageClientOptions = {
-            documentSelector: [{ scheme: 'file', language: 'rust' }]
+            documentSelector: [{ scheme: 'file', language: 'rust' }],
+            initializationOptions: {
+                publishDecorations: true,
+            }
         };
 
         Server.client = new lc.LanguageClient(
index 099f611be7b966d3ad9c1fa3217b0fd6ef1744dc..fb73451c1b316c23be368a85986a5dc635df73ee 100644 (file)
   "Set selections cache for current buffer state and START END."
   (setq ra--selections-cache `(,(buffer-modified-tick) 0 ,(ra--selections start end))))
 
+
+(require 'eglot)
+(require 'ivy)
+(require 'counsel)
+
+
+(defun workspace-symbols ()
+  (interactive)
+  (let ((buf (current-buffer)))
+    (ivy-read "Symbol name: "
+              (lambda (str)
+                (with-current-buffer buf
+                  (let ((backend (eglot-xref-backend)))
+                    (mapcar 
+                     (lambda (xref)
+                       (let ((loc (xref-item-location xref)))
+                         (propertize
+                          (concat
+                           (when (xref-file-location-p loc)
+                             (with-slots (file line column) loc
+                               (format "%s:%s:%s:" 
+                                       (propertize (file-relative-name file)
+                                                   'face 'compilation-info)
+                                       (propertize (format "%s" line)
+                                                   'face 'compilation-line
+                                                   )
+                                       column)))
+                           (xref-item-summary xref))
+                          'xref xref)))
+                     (xref-backend-apropos backend str))
+                    )))
+              :dynamic-collection t
+              :action (lambda (item)
+                        (xref--pop-to-location (get-text-property 0 'xref item))))))
+
+(add-to-list 'eglot-server-programs '(rust-mode . ("ra_lsp_server")))
+
+; (require 'rust-mode)
+; (define-key rust-mode-map (kbd "C-n") 'workspace-symbols)
+
+(define-key)
 (provide 'ra)
 ;;; ra.el ends here