]> git.lizzy.rs Git - rust.git/commitdiff
Merge #10105
authorbors[bot] <26634292+bors[bot]@users.noreply.github.com>
Fri, 24 Sep 2021 15:12:17 +0000 (15:12 +0000)
committerGitHub <noreply@github.com>
Fri, 24 Sep 2021 15:12:17 +0000 (15:12 +0000)
10105: RfC: Use `todo!()` instead of `()` for missing fields r=jonas-schievink a=jo-so

Most commonly a field of a struct can be initialized with its default value than an empty tuple.

Co-authored-by: Jörg Sommer <joerg@jo-so.de>
316 files changed:
.gitattributes
Cargo.lock
crates/base_db/Cargo.toml
crates/base_db/src/fixture.rs
crates/flycheck/src/lib.rs
crates/hir/Cargo.toml
crates/hir/src/display.rs
crates/hir/src/lib.rs
crates/hir/src/semantics.rs
crates/hir/src/semantics/source_to_def.rs
crates/hir/src/source_analyzer.rs
crates/hir_def/src/attr.rs
crates/hir_def/src/nameres/collector.rs
crates/hir_expand/Cargo.toml
crates/hir_expand/src/builtin_macro.rs
crates/hir_expand/src/db.rs
crates/hir_expand/src/eager.rs
crates/hir_expand/src/lib.rs
crates/hir_ty/Cargo.toml
crates/hir_ty/src/autoderef.rs
crates/hir_ty/src/chalk_db.rs
crates/hir_ty/src/diagnostics/expr.rs
crates/hir_ty/src/diagnostics/match_check/deconstruct_pat.rs
crates/hir_ty/src/diagnostics/match_check/usefulness.rs
crates/hir_ty/src/infer/coerce.rs
crates/hir_ty/src/infer/expr.rs
crates/hir_ty/src/infer/unify.rs
crates/hir_ty/src/lower.rs
crates/hir_ty/src/method_resolution.rs
crates/hir_ty/src/tests/regression.rs
crates/ide/src/call_hierarchy.rs
crates/ide/src/display.rs
crates/ide/src/display/navigation_target.rs
crates/ide/src/doc_links.rs
crates/ide/src/expand_macro.rs
crates/ide/src/fixture.rs
crates/ide/src/goto_definition.rs
crates/ide/src/highlight_related.rs
crates/ide/src/hover.rs
crates/ide/src/hover/render.rs [new file with mode: 0644]
crates/ide/src/hover/tests.rs [new file with mode: 0644]
crates/ide/src/inlay_hints.rs
crates/ide/src/references.rs
crates/ide/src/rename.rs
crates/ide/src/runnables.rs
crates/ide/src/syntax_highlighting/highlight.rs
crates/ide/src/syntax_highlighting/html.rs
crates/ide/src/syntax_highlighting/test_data/highlight_assoc_functions.html
crates/ide/src/syntax_highlighting/test_data/highlight_doctest.html
crates/ide/src/syntax_highlighting/test_data/highlight_extern_crate.html
crates/ide/src/syntax_highlighting/test_data/highlight_injection.html
crates/ide/src/syntax_highlighting/test_data/highlight_strings.html
crates/ide/src/syntax_highlighting/test_data/highlight_unsafe.html
crates/ide/src/syntax_highlighting/test_data/highlighting.html
crates/ide/src/syntax_highlighting/test_data/injection.html
crates/ide/src/syntax_highlighting/test_data/rainbow_highlighting.html
crates/ide/src/syntax_highlighting/tests.rs
crates/ide_assists/src/assist_context.rs
crates/ide_assists/src/handlers/add_missing_match_arms.rs [new file with mode: 0644]
crates/ide_assists/src/handlers/add_return_type.rs [new file with mode: 0644]
crates/ide_assists/src/handlers/convert_bool_then.rs
crates/ide_assists/src/handlers/convert_comment_block.rs
crates/ide_assists/src/handlers/convert_iter_for_each_to_for.rs
crates/ide_assists/src/handlers/convert_tuple_struct_to_named_struct.rs
crates/ide_assists/src/handlers/convert_while_to_loop.rs [new file with mode: 0644]
crates/ide_assists/src/handlers/expand_glob_import.rs
crates/ide_assists/src/handlers/extract_function.rs
crates/ide_assists/src/handlers/extract_struct_from_enum_variant.rs
crates/ide_assists/src/handlers/extract_variable.rs
crates/ide_assists/src/handlers/fill_match_arms.rs [deleted file]
crates/ide_assists/src/handlers/generate_function.rs
crates/ide_assists/src/handlers/infer_function_return_type.rs [deleted file]
crates/ide_assists/src/handlers/inline_call.rs
crates/ide_assists/src/handlers/inline_local_variable.rs
crates/ide_assists/src/handlers/introduce_named_generic.rs [new file with mode: 0644]
crates/ide_assists/src/handlers/introduce_named_lifetime.rs
crates/ide_assists/src/handlers/move_guard.rs
crates/ide_assists/src/handlers/raw_string.rs
crates/ide_assists/src/handlers/remove_unused_param.rs
crates/ide_assists/src/handlers/replace_for_loop_with_for_each.rs [deleted file]
crates/ide_assists/src/handlers/replace_if_let_with_match.rs
crates/ide_assists/src/handlers/replace_impl_trait_with_generic.rs [deleted file]
crates/ide_assists/src/handlers/replace_qualified_name_with_use.rs
crates/ide_assists/src/handlers/toggle_ignore.rs
crates/ide_assists/src/lib.rs
crates/ide_assists/src/tests/generated.rs
crates/ide_completion/Cargo.toml
crates/ide_completion/src/completions/attribute.rs
crates/ide_completion/src/completions/flyimport.rs
crates/ide_completion/src/completions/fn_param.rs
crates/ide_completion/src/context.rs
crates/ide_completion/src/render/enum_variant.rs
crates/ide_completion/src/render/macro_.rs
crates/ide_completion/src/tests.rs
crates/ide_completion/src/tests/attribute.rs
crates/ide_completion/src/tests/proc_macros.rs [new file with mode: 0644]
crates/ide_db/Cargo.toml
crates/ide_db/src/call_info.rs
crates/ide_db/src/defs.rs
crates/ide_db/src/search.rs
crates/ide_diagnostics/src/handlers/unlinked_file.rs
crates/ide_ssr/src/lib.rs
crates/ide_ssr/src/matching.rs
crates/ide_ssr/src/nester.rs
crates/ide_ssr/src/parsing.rs
crates/ide_ssr/src/resolving.rs
crates/ide_ssr/src/search.rs
crates/mbe/Cargo.toml
crates/mbe/src/syntax_bridge.rs
crates/mbe/src/tests.rs
crates/parser/src/grammar.rs
crates/parser/src/grammar/attributes.rs
crates/parser/src/grammar/expressions.rs
crates/parser/src/grammar/generic_args.rs [new file with mode: 0644]
crates/parser/src/grammar/generic_params.rs [new file with mode: 0644]
crates/parser/src/grammar/items.rs
crates/parser/src/grammar/items/adt.rs
crates/parser/src/grammar/items/consts.rs
crates/parser/src/grammar/items/traits.rs
crates/parser/src/grammar/items/use_item.rs
crates/parser/src/grammar/paths.rs
crates/parser/src/grammar/patterns.rs
crates/parser/src/grammar/type_args.rs [deleted file]
crates/parser/src/grammar/type_params.rs [deleted file]
crates/parser/src/grammar/types.rs
crates/proc_macro_api/Cargo.toml
crates/proc_macro_api/src/lib.rs
crates/proc_macro_api/src/msg/flat.rs
crates/proc_macro_api/src/process.rs
crates/proc_macro_srv/Cargo.toml
crates/project_model/src/cargo_workspace.rs
crates/project_model/src/tests.rs
crates/project_model/src/workspace.rs
crates/rust-analyzer/Cargo.toml
crates/rust-analyzer/src/bin/main.rs
crates/rust-analyzer/src/config.rs
crates/rust-analyzer/src/global_state.rs
crates/rust-analyzer/src/handlers.rs
crates/rust-analyzer/src/main_loop.rs
crates/rust-analyzer/src/reload.rs
crates/rust-analyzer/tests/slow-tests/support.rs
crates/rust-analyzer/tests/slow-tests/tidy.rs
crates/sourcegen/src/lib.rs
crates/stdx/src/lib.rs
crates/syntax/Cargo.toml
crates/syntax/src/algo.rs
crates/syntax/src/ast.rs
crates/syntax/src/ast/generated/nodes.rs
crates/syntax/src/ast/make.rs
crates/syntax/src/ast/node_ext.rs
crates/syntax/src/lib.rs
crates/syntax/src/syntax_node.rs
crates/syntax/src/tests/sourcegen_ast.rs
crates/syntax/src/token_text.rs
crates/syntax/src/validation.rs
crates/syntax/test_data/parser/err/0049_double_fish.rast [new file with mode: 0644]
crates/syntax/test_data/parser/err/0049_double_fish.rs [new file with mode: 0644]
crates/syntax/test_data/parser/inline/err/0013_anonymous_static.rast [new file with mode: 0644]
crates/syntax/test_data/parser/inline/err/0013_anonymous_static.rs [new file with mode: 0644]
crates/syntax/test_data/parser/inline/err/0013_static_underscore.rast [deleted file]
crates/syntax/test_data/parser/inline/err/0013_static_underscore.rs [deleted file]
crates/syntax/test_data/parser/inline/ok/0002_use_tree_list.rast
crates/syntax/test_data/parser/inline/ok/0002_use_tree_list.rs
crates/syntax/test_data/parser/inline/ok/0020_use_star.rast [deleted file]
crates/syntax/test_data/parser/inline/ok/0020_use_star.rs [deleted file]
crates/syntax/test_data/parser/inline/ok/0021_assoc_item_list.rast [new file with mode: 0644]
crates/syntax/test_data/parser/inline/ok/0021_assoc_item_list.rs [new file with mode: 0644]
crates/syntax/test_data/parser/inline/ok/0021_impl_item_list.rast [deleted file]
crates/syntax/test_data/parser/inline/ok/0021_impl_item_list.rs [deleted file]
crates/syntax/test_data/parser/inline/ok/0039_type_arg.rast [deleted file]
crates/syntax/test_data/parser/inline/ok/0039_type_arg.rs [deleted file]
crates/syntax/test_data/parser/inline/ok/0041_trait_item.rast
crates/syntax/test_data/parser/inline/ok/0041_trait_item.rs
crates/syntax/test_data/parser/inline/ok/0043_use_alias.rast [deleted file]
crates/syntax/test_data/parser/inline/ok/0043_use_alias.rs [deleted file]
crates/syntax/test_data/parser/inline/ok/0054_record_field_attrs.rast
crates/syntax/test_data/parser/inline/ok/0054_record_field_attrs.rs
crates/syntax/test_data/parser/inline/ok/0063_impl_def_neg.rast [deleted file]
crates/syntax/test_data/parser/inline/ok/0063_impl_def_neg.rs [deleted file]
crates/syntax/test_data/parser/inline/ok/0063_impl_item_neg.rast [new file with mode: 0644]
crates/syntax/test_data/parser/inline/ok/0063_impl_item_neg.rs [new file with mode: 0644]
crates/syntax/test_data/parser/inline/ok/0068_union_items.rast [deleted file]
crates/syntax/test_data/parser/inline/ok/0068_union_items.rs [deleted file]
crates/syntax/test_data/parser/inline/ok/0069_use_tree_list_after_path.rast [deleted file]
crates/syntax/test_data/parser/inline/ok/0069_use_tree_list_after_path.rs [deleted file]
crates/syntax/test_data/parser/inline/ok/0078_type_alias.rast [new file with mode: 0644]
crates/syntax/test_data/parser/inline/ok/0078_type_alias.rs [new file with mode: 0644]
crates/syntax/test_data/parser/inline/ok/0078_type_item.rast [deleted file]
crates/syntax/test_data/parser/inline/ok/0078_type_item.rs [deleted file]
crates/syntax/test_data/parser/inline/ok/0079_impl_def.rast [deleted file]
crates/syntax/test_data/parser/inline/ok/0079_impl_def.rs [deleted file]
crates/syntax/test_data/parser/inline/ok/0079_impl_item.rast [new file with mode: 0644]
crates/syntax/test_data/parser/inline/ok/0079_impl_item.rs [new file with mode: 0644]
crates/syntax/test_data/parser/inline/ok/0083_struct_items.rs [deleted file]
crates/syntax/test_data/parser/inline/ok/0110_use_path.rast [deleted file]
crates/syntax/test_data/parser/inline/ok/0110_use_path.rs [deleted file]
crates/syntax/test_data/parser/inline/ok/0114_tuple_struct_where.rast
crates/syntax/test_data/parser/inline/ok/0114_tuple_struct_where.rs
crates/syntax/test_data/parser/inline/ok/0115_tuple_field_attrs.rast
crates/syntax/test_data/parser/inline/ok/0115_tuple_field_attrs.rs
crates/syntax/test_data/parser/inline/ok/0118_impl_inner_attributes.rast [deleted file]
crates/syntax/test_data/parser/inline/ok/0118_impl_inner_attributes.rs [deleted file]
crates/syntax/test_data/parser/inline/ok/0122_generic_lifetime_type_attribute.rast [deleted file]
crates/syntax/test_data/parser/inline/ok/0122_generic_lifetime_type_attribute.rs [deleted file]
crates/syntax/test_data/parser/inline/ok/0147_macro_def.rast
crates/syntax/test_data/parser/inline/ok/0147_macro_def.rs
crates/syntax/test_data/parser/inline/ok/0148_pub_macro_def.rast [deleted file]
crates/syntax/test_data/parser/inline/ok/0148_pub_macro_def.rs [deleted file]
crates/syntax/test_data/parser/inline/ok/0151_trait_alias.rast
crates/syntax/test_data/parser/inline/ok/0151_trait_alias.rs
crates/syntax/test_data/parser/inline/ok/0152_impl.rast [deleted file]
crates/syntax/test_data/parser/inline/ok/0152_impl.rs [deleted file]
crates/syntax/test_data/parser/inline/ok/0153_trait.rast [deleted file]
crates/syntax/test_data/parser/inline/ok/0153_trait.rs [deleted file]
crates/syntax/test_data/parser/inline/ok/0161_impl_def_const.rast [deleted file]
crates/syntax/test_data/parser/inline/ok/0161_impl_def_const.rs [deleted file]
crates/syntax/test_data/parser/inline/ok/0161_impl_item_const.rast [new file with mode: 0644]
crates/syntax/test_data/parser/inline/ok/0161_impl_item_const.rs [new file with mode: 0644]
crates/syntax/test_data/parser/inline/ok/0164_const_generic_negated_literal.rast [deleted file]
crates/syntax/test_data/parser/inline/ok/0164_const_generic_negated_literal.rs [deleted file]
crates/syntax/test_data/parser/inline/ok/0165_const_param_defaults.rast
crates/syntax/test_data/parser/inline/ok/0165_const_param_defaults.rs
crates/syntax/test_data/parser/inline/ok/0168_extern_crate_rename.rast [new file with mode: 0644]
crates/syntax/test_data/parser/inline/ok/0168_extern_crate_rename.rs [new file with mode: 0644]
crates/syntax/test_data/parser/inline/ok/0168_extern_crate_self.rast [new file with mode: 0644]
crates/syntax/test_data/parser/inline/ok/0168_extern_crate_self.rs [new file with mode: 0644]
crates/syntax/test_data/parser/inline/ok/0169_mod_item.rast [new file with mode: 0644]
crates/syntax/test_data/parser/inline/ok/0169_mod_item.rs [new file with mode: 0644]
crates/syntax/test_data/parser/inline/ok/0170_mod_item_curly.rast [new file with mode: 0644]
crates/syntax/test_data/parser/inline/ok/0170_mod_item_curly.rs [new file with mode: 0644]
crates/syntax/test_data/parser/inline/ok/0170_tuple_struct.rast [new file with mode: 0644]
crates/syntax/test_data/parser/inline/ok/0170_tuple_struct.rs [new file with mode: 0644]
crates/syntax/test_data/parser/inline/ok/0171_struct_item.rast [new file with mode: 0644]
crates/syntax/test_data/parser/inline/ok/0171_struct_item.rs [new file with mode: 0644]
crates/syntax/test_data/parser/inline/ok/0172_const_item.rast [new file with mode: 0644]
crates/syntax/test_data/parser/inline/ok/0172_const_item.rs [new file with mode: 0644]
crates/syntax/test_data/parser/inline/ok/0172_record_field_list.rast [new file with mode: 0644]
crates/syntax/test_data/parser/inline/ok/0172_record_field_list.rs [new file with mode: 0644]
crates/syntax/test_data/parser/inline/ok/0173_anonymous_const.rast [new file with mode: 0644]
crates/syntax/test_data/parser/inline/ok/0173_anonymous_const.rs [new file with mode: 0644]
crates/syntax/test_data/parser/inline/ok/0173_macro_def_curly.rast [new file with mode: 0644]
crates/syntax/test_data/parser/inline/ok/0173_macro_def_curly.rs [new file with mode: 0644]
crates/syntax/test_data/parser/inline/ok/0173_union_item.rast [new file with mode: 0644]
crates/syntax/test_data/parser/inline/ok/0173_union_item.rs [new file with mode: 0644]
crates/syntax/test_data/parser/inline/ok/0174_trait_item_generic_params.rast [new file with mode: 0644]
crates/syntax/test_data/parser/inline/ok/0174_trait_item_generic_params.rs [new file with mode: 0644]
crates/syntax/test_data/parser/inline/ok/0174_unit_struct.rast [new file with mode: 0644]
crates/syntax/test_data/parser/inline/ok/0174_unit_struct.rs [new file with mode: 0644]
crates/syntax/test_data/parser/inline/ok/0174_use_tree_star.rast [new file with mode: 0644]
crates/syntax/test_data/parser/inline/ok/0174_use_tree_star.rs [new file with mode: 0644]
crates/syntax/test_data/parser/inline/ok/0175_trait_item_bounds.rast [new file with mode: 0644]
crates/syntax/test_data/parser/inline/ok/0175_trait_item_bounds.rs [new file with mode: 0644]
crates/syntax/test_data/parser/inline/ok/0176_trait_item_where_clause.rast [new file with mode: 0644]
crates/syntax/test_data/parser/inline/ok/0176_trait_item_where_clause.rs [new file with mode: 0644]
crates/syntax/test_data/parser/inline/ok/0176_use_tree_alias.rast [new file with mode: 0644]
crates/syntax/test_data/parser/inline/ok/0176_use_tree_alias.rs [new file with mode: 0644]
crates/syntax/test_data/parser/inline/ok/0177_assoc_item_list_inner_attrs.rast [new file with mode: 0644]
crates/syntax/test_data/parser/inline/ok/0177_assoc_item_list_inner_attrs.rs [new file with mode: 0644]
crates/syntax/test_data/parser/inline/ok/0177_trait_alias_where_clause.rast [new file with mode: 0644]
crates/syntax/test_data/parser/inline/ok/0177_trait_alias_where_clause.rs [new file with mode: 0644]
crates/syntax/test_data/parser/inline/ok/0177_use_tree.rast [new file with mode: 0644]
crates/syntax/test_data/parser/inline/ok/0177_use_tree.rs [new file with mode: 0644]
crates/syntax/test_data/parser/inline/ok/0177_use_tree_path.rast [new file with mode: 0644]
crates/syntax/test_data/parser/inline/ok/0177_use_tree_path.rs [new file with mode: 0644]
crates/syntax/test_data/parser/inline/ok/0178_use_tree_path_use_tree.rast [new file with mode: 0644]
crates/syntax/test_data/parser/inline/ok/0178_use_tree_path_use_tree.rs [new file with mode: 0644]
crates/syntax/test_data/parser/inline/ok/0179_use_tree_abs_star.rast [new file with mode: 0644]
crates/syntax/test_data/parser/inline/ok/0179_use_tree_abs_star.rs [new file with mode: 0644]
crates/syntax/test_data/parser/inline/ok/0180_use_tree_path_star.rast [new file with mode: 0644]
crates/syntax/test_data/parser/inline/ok/0180_use_tree_path_star.rs [new file with mode: 0644]
crates/syntax/test_data/parser/inline/ok/0181_generic_param_attribute.rast [new file with mode: 0644]
crates/syntax/test_data/parser/inline/ok/0181_generic_param_attribute.rs [new file with mode: 0644]
crates/syntax/test_data/parser/inline/ok/0181_use_item.rast [new file with mode: 0644]
crates/syntax/test_data/parser/inline/ok/0181_use_item.rs [new file with mode: 0644]
crates/syntax/test_data/parser/inline/ok/0182_lifetime_param.rast [new file with mode: 0644]
crates/syntax/test_data/parser/inline/ok/0182_lifetime_param.rs [new file with mode: 0644]
crates/syntax/test_data/parser/inline/ok/0183_const_arg_block.rast [new file with mode: 0644]
crates/syntax/test_data/parser/inline/ok/0183_const_arg_block.rs [new file with mode: 0644]
crates/syntax/test_data/parser/inline/ok/0183_type_param.rast [new file with mode: 0644]
crates/syntax/test_data/parser/inline/ok/0183_type_param.rs [new file with mode: 0644]
crates/syntax/test_data/parser/inline/ok/0184_const_arg.rast [new file with mode: 0644]
crates/syntax/test_data/parser/inline/ok/0184_const_arg.rs [new file with mode: 0644]
crates/syntax/test_data/parser/inline/ok/0184_generic_param_list.rast [new file with mode: 0644]
crates/syntax/test_data/parser/inline/ok/0184_generic_param_list.rs [new file with mode: 0644]
crates/syntax/test_data/parser/inline/ok/0185_assoc_type_bound.rast [new file with mode: 0644]
crates/syntax/test_data/parser/inline/ok/0185_assoc_type_bound.rs [new file with mode: 0644]
crates/syntax/test_data/parser/inline/ok/0186_lifetime_arg.rast [new file with mode: 0644]
crates/syntax/test_data/parser/inline/ok/0186_lifetime_arg.rs [new file with mode: 0644]
crates/syntax/test_data/parser/inline/ok/0187_assoc_type_eq.rast [new file with mode: 0644]
crates/syntax/test_data/parser/inline/ok/0187_assoc_type_eq.rs [new file with mode: 0644]
crates/syntax/test_data/parser/inline/ok/0188_const_arg_path.rast [new file with mode: 0644]
crates/syntax/test_data/parser/inline/ok/0188_const_arg_path.rs [new file with mode: 0644]
crates/syntax/test_data/parser/inline/ok/0189_const_arg_literal.rast [new file with mode: 0644]
crates/syntax/test_data/parser/inline/ok/0189_const_arg_literal.rs [new file with mode: 0644]
crates/syntax/test_data/parser/inline/ok/0190_generic_arg.rast [new file with mode: 0644]
crates/syntax/test_data/parser/inline/ok/0190_generic_arg.rs [new file with mode: 0644]
crates/syntax/test_data/parser/inline/ok/0191_const_arg_negative_number.rast [new file with mode: 0644]
crates/syntax/test_data/parser/inline/ok/0191_const_arg_negative_number.rs [new file with mode: 0644]
crates/syntax/test_data/parser/inline/ok/0192_const_arg_bool_literal.rast [new file with mode: 0644]
crates/syntax/test_data/parser/inline/ok/0192_const_arg_bool_literal.rs [new file with mode: 0644]
crates/syntax/test_data/parser/ok/0008_mod_item.rast
crates/syntax/test_data/parser/ok/0008_mod_item.rs
crates/syntax/test_data/parser/ok/0012_visibility.rast
crates/syntax/test_data/parser/ok/0012_visibility.rs
crates/syntax/test_data/parser/ok/0024_const_item.rast
crates/syntax/test_data/parser/ok/0024_const_item.rs
crates/test_utils/src/fixture.rs
crates/vfs-notify/Cargo.toml
crates/vfs-notify/src/lib.rs
docs/user/generated_config.adoc
docs/user/manual.adoc
editors/code/package.json
editors/code/src/commands.ts
editors/code/src/net.ts
xtask/src/install.rs
xtask/src/release.rs

index 7c2f752d69aee7356d90a0edd166790f5326512d..3b3d2d0d656d32c909b61bfbeb33b4bbb1b36387 100644 (file)
@@ -1,4 +1,6 @@
 * text=auto eol=lf
+# git grep shouldn't match entries in this benchmark data
+bench_data/** binary
 crates/syntax/test_data/** -text eof=LF
 # Older git versions try to fix line endings on images, this prevents it.
 *.png binary
index ace1e755aa22e15fa5e09e42864aa9a9416c2a47..9170ec3a66bacc6af2fd9e35922d4b63a9528c65 100644 (file)
@@ -99,7 +99,6 @@ dependencies = [
  "profile",
  "rustc-hash",
  "salsa",
- "stdx",
  "syntax",
  "test_utils",
  "tt",
@@ -171,9 +170,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
 
 [[package]]
 name = "chalk-derive"
-version = "0.70.0"
+version = "0.71.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1b29a4ef88867aee29bc709976d9b0a20ddb2c52aeca0bd635893a74fa77d7f6"
+checksum = "059cce4ba41e57dd82f55b348d3e83cb30fd142479d00287f08c4ae66f9e7197"
 dependencies = [
  "proc-macro2",
  "quote",
@@ -183,9 +182,9 @@ dependencies = [
 
 [[package]]
 name = "chalk-ir"
-version = "0.70.0"
+version = "0.71.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3efd5b260d23af3daebae111ea4066604dd3cdb18ca610358ab2e2a7aab71461"
+checksum = "c0f9b041f3fcc136dbf8a92cef5f6ac743f9800467763502f5924349b781cbe0"
 dependencies = [
  "bitflags",
  "chalk-derive",
@@ -194,9 +193,9 @@ dependencies = [
 
 [[package]]
 name = "chalk-recursive"
-version = "0.70.0"
+version = "0.71.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7c8089c69051fa6bfdadb67f6dc951881f99fef6814e0e0c27c47218e34d8adb"
+checksum = "b1dd77179b3310dea3838b73e0f5990fcf4d1c00bfd2bc43d984faa8d2783ff1"
 dependencies = [
  "chalk-derive",
  "chalk-ir",
@@ -207,9 +206,9 @@ dependencies = [
 
 [[package]]
 name = "chalk-solve"
-version = "0.70.0"
+version = "0.71.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3ad0c276126d7787577d22f82785d8f2795318ad30349e66b487a6d6a591e351"
+checksum = "0c8ff6810c6bcac76950d1d292f71862e5757f483b8745a9186e649076b913be"
 dependencies = [
  "chalk-derive",
  "chalk-ir",
@@ -471,7 +470,6 @@ dependencies = [
  "hir_def",
  "hir_expand",
  "hir_ty",
- "indexmap",
  "itertools",
  "once_cell",
  "profile",
@@ -528,7 +526,6 @@ dependencies = [
  "profile",
  "rustc-hash",
  "syntax",
- "test_utils",
  "tracing",
  "tt",
 ]
@@ -641,7 +638,6 @@ dependencies = [
  "syntax",
  "test_utils",
  "text_edit",
- "tracing",
  "xshell",
 ]
 
@@ -649,6 +645,7 @@ dependencies = [
 name = "ide_db"
 version = "0.0.0"
 dependencies = [
+ "arrayvec",
  "base_db",
  "cov-mark",
  "either",
@@ -898,7 +895,6 @@ dependencies = [
  "cov-mark",
  "expect-test",
  "parser",
- "profile",
  "rustc-hash",
  "smallvec",
  "stdx",
@@ -1127,8 +1123,6 @@ dependencies = [
 name = "proc_macro_api"
 version = "0.0.0"
 dependencies = [
- "crossbeam-channel",
- "jod-thread",
  "memmap2",
  "object",
  "paths",
@@ -1145,7 +1139,6 @@ dependencies = [
 name = "proc_macro_srv"
 version = "0.0.0"
 dependencies = [
- "cargo_metadata",
  "expect-test",
  "libloading",
  "mbe",
@@ -1154,8 +1147,6 @@ dependencies = [
  "paths",
  "proc_macro_api",
  "proc_macro_test",
- "test_utils",
- "toolchain",
  "tt",
 ]
 
@@ -1220,9 +1211,9 @@ dependencies = [
 
 [[package]]
 name = "pulldown-cmark-to-cmark"
-version = "6.0.2"
+version = "6.0.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "95048382115a9da7be92ad51c84064d585b7da17472dcaa7f5eed8853c4c3707"
+checksum = "a72d775989b8b4cc8e5e924a99d6b3ed960da727f78394b7abd539301972e08e"
 dependencies = [
  "pulldown-cmark",
 ]
@@ -1296,9 +1287,9 @@ checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b"
 
 [[package]]
 name = "rowan"
-version = "0.13.2"
+version = "0.14.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4a938f42b9c73aeece236481f37adb3debb7dfe3ae347cd6a45b5797d9ce4250"
+checksum = "86f050538a65de83ae021294fb50d57f71fb4530fe79af755fc4d4cd61082c01"
 dependencies = [
  "countme",
  "hashbrown",
@@ -1344,7 +1335,6 @@ dependencies = [
  "stdx",
  "syntax",
  "test_utils",
- "text_edit",
  "threadpool",
  "tikv-jemallocator",
  "toolchain",
@@ -1561,7 +1551,6 @@ dependencies = [
 name = "syntax"
 version = "0.0.0"
 dependencies = [
- "arrayvec",
  "cov-mark",
  "expect-test",
  "indexmap",
@@ -1848,7 +1837,6 @@ dependencies = [
  "jod-thread",
  "notify",
  "paths",
- "rustc-hash",
  "tracing",
  "vfs",
  "walkdir",
@@ -1919,18 +1907,18 @@ checksum = "da260301476ad19a4733a0e930db8227a48ea04561e235a5102978145ec69fcc"
 
 [[package]]
 name = "xshell"
-version = "0.1.15"
+version = "0.1.17"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "07a06b78bf7920975954d1bef9dadedbb522dc886e14532b7cd4c83a10601867"
+checksum = "eaad2035244c56da05573d4d7fda5f903c60a5f35b9110e157a14a1df45a9f14"
 dependencies = [
  "xshell-macros",
 ]
 
 [[package]]
 name = "xshell-macros"
-version = "0.1.15"
+version = "0.1.17"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d2b955be4ccb0caffb7312052b5b9d31f24cac4c4898869290f1580cb6d73dc2"
+checksum = "4916a4a3cad759e499a3620523bf9545cc162d7a06163727dde97ce9aaa4cf39"
 
 [[package]]
 name = "xtask"
index 1301d22275586d075e73a8c4af063f0b34f91b4c..b43e5efb2a001cc370034d719557eb0a5a7bd45e 100644 (file)
@@ -18,4 +18,3 @@ profile = { path = "../profile", version = "0.0.0" }
 tt = { path = "../tt", version = "0.0.0" }
 test_utils = { path = "../test_utils", version = "0.0.0" }
 vfs = { path = "../vfs", version = "0.0.0" }
-stdx = { path = "../stdx", version = "0.0.0" }
index 8025d6398c427b9ec426c6888f6860174c4b913d..27be3746b71a3770ec9444c79390206c24c8b97c 100644 (file)
@@ -6,11 +6,13 @@
 use test_utils::{
     extract_range_or_offset, Fixture, RangeOrOffset, CURSOR_MARKER, ESCAPED_CURSOR_MARKER,
 };
+use tt::Subtree;
 use vfs::{file_set::FileSet, VfsPath};
 
 use crate::{
     input::CrateName, Change, CrateDisplayName, CrateGraph, CrateId, Edition, Env, FileId,
-    FilePosition, FileRange, SourceDatabaseExt, SourceRoot, SourceRootId,
+    FilePosition, FileRange, ProcMacro, ProcMacroExpander, ProcMacroExpansionError,
+    SourceDatabaseExt, SourceRoot, SourceRootId,
 };
 
 pub const WORKSPACE: SourceRootId = SourceRootId(0);
@@ -81,7 +83,7 @@ pub struct ChangeFixture {
 
 impl ChangeFixture {
     pub fn parse(ra_fixture: &str) -> ChangeFixture {
-        let (mini_core, fixture) = Fixture::parse(ra_fixture);
+        let (mini_core, proc_macros, fixture) = Fixture::parse(ra_fixture);
         let mut change = Change::new();
 
         let mut files = Vec::new();
@@ -203,6 +205,40 @@ pub fn parse(ra_fixture: &str) -> ChangeFixture {
                 crate_graph.add_dep(krate, CrateName::new("core").unwrap(), core_crate).unwrap();
             }
         }
+
+        if !proc_macros.is_empty() {
+            let proc_lib_file = file_id;
+            file_id.0 += 1;
+
+            let (proc_macro, source) = test_proc_macros(&proc_macros);
+            let mut fs = FileSet::default();
+            fs.insert(
+                proc_lib_file,
+                VfsPath::new_virtual_path("/sysroot/proc_macros/lib.rs".to_string()),
+            );
+            roots.push(SourceRoot::new_library(fs));
+
+            change.change_file(proc_lib_file, Some(Arc::new(source)));
+
+            let all_crates = crate_graph.crates_in_topological_order();
+
+            let proc_macros_crate = crate_graph.add_crate_root(
+                proc_lib_file,
+                Edition::Edition2021,
+                Some(CrateDisplayName::from_canonical_name("proc_macros".to_string())),
+                CfgOptions::default(),
+                CfgOptions::default(),
+                Env::default(),
+                proc_macro,
+            );
+
+            for krate in all_crates {
+                crate_graph
+                    .add_dep(krate, CrateName::new("proc_macros").unwrap(), proc_macros_crate)
+                    .unwrap();
+            }
+        }
+
         let root = match current_source_root_kind {
             SourceRootKind::Local => SourceRoot::new_local(mem::take(&mut file_set)),
             SourceRootKind::Library => SourceRoot::new_library(mem::take(&mut file_set)),
@@ -215,6 +251,44 @@ pub fn parse(ra_fixture: &str) -> ChangeFixture {
     }
 }
 
+fn test_proc_macros(proc_macros: &[String]) -> (Vec<ProcMacro>, String) {
+    // The source here is only required so that paths to the macros exist and are resolvable.
+    let source = r#"
+#[proc_macro_attribute]
+pub fn identity(_attr: TokenStream, item: TokenStream) -> TokenStream {
+    item
+}
+#[proc_macro_attribute]
+pub fn input_replace(attr: TokenStream, _item: TokenStream) -> TokenStream {
+    attr
+}
+#[proc_macro]
+pub fn mirror(input: TokenStream) -> TokenStream {
+    input
+}
+"#;
+    let proc_macros = std::array::IntoIter::new([
+        ProcMacro {
+            name: "identity".into(),
+            kind: crate::ProcMacroKind::Attr,
+            expander: Arc::new(IdentityProcMacroExpander),
+        },
+        ProcMacro {
+            name: "input_replace".into(),
+            kind: crate::ProcMacroKind::Attr,
+            expander: Arc::new(AttributeInputReplaceProcMacroExpander),
+        },
+        ProcMacro {
+            name: "mirror".into(),
+            kind: crate::ProcMacroKind::FuncLike,
+            expander: Arc::new(MirrorProcMacroExpander),
+        },
+    ])
+    .filter(|pm| proc_macros.iter().any(|name| name == pm.name))
+    .collect();
+    (proc_macros, source.into())
+}
+
 #[derive(Debug, Clone, Copy)]
 enum SourceRootKind {
     Local,
@@ -253,3 +327,58 @@ fn from(f: Fixture) -> FileMeta {
         }
     }
 }
+
+// Identity mapping
+#[derive(Debug)]
+struct IdentityProcMacroExpander;
+impl ProcMacroExpander for IdentityProcMacroExpander {
+    fn expand(
+        &self,
+        subtree: &Subtree,
+        _: Option<&Subtree>,
+        _: &Env,
+    ) -> Result<Subtree, ProcMacroExpansionError> {
+        Ok(subtree.clone())
+    }
+}
+
+// Pastes the attribute input as its output
+#[derive(Debug)]
+struct AttributeInputReplaceProcMacroExpander;
+impl ProcMacroExpander for AttributeInputReplaceProcMacroExpander {
+    fn expand(
+        &self,
+        _: &Subtree,
+        attrs: Option<&Subtree>,
+        _: &Env,
+    ) -> Result<Subtree, ProcMacroExpansionError> {
+        attrs
+            .cloned()
+            .ok_or_else(|| ProcMacroExpansionError::Panic("Expected attribute input".into()))
+    }
+}
+
+#[derive(Debug)]
+struct MirrorProcMacroExpander;
+impl ProcMacroExpander for MirrorProcMacroExpander {
+    fn expand(
+        &self,
+        input: &Subtree,
+        _: Option<&Subtree>,
+        _: &Env,
+    ) -> Result<Subtree, ProcMacroExpansionError> {
+        fn traverse(input: &Subtree) -> Subtree {
+            let mut res = Subtree::default();
+            res.delimiter = input.delimiter;
+            for tt in input.token_trees.iter().rev() {
+                let tt = match tt {
+                    tt::TokenTree::Leaf(leaf) => tt::TokenTree::Leaf(leaf.clone()),
+                    tt::TokenTree::Subtree(sub) => tt::TokenTree::Subtree(traverse(sub)),
+                };
+                res.token_trees.push(tt);
+            }
+            res
+        }
+        Ok(traverse(input))
+    }
+}
index a636d865025f10b1731db0c91a5254d758f7714e..40dfe6f51172bddc2971ec9b14ff55dca679bde3 100644 (file)
@@ -55,7 +55,7 @@ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
 pub struct FlycheckHandle {
     // XXX: drop order is significant
     sender: Sender<Restart>,
-    thread: jod_thread::JoinHandle,
+    _thread: jod_thread::JoinHandle,
 }
 
 impl FlycheckHandle {
@@ -71,7 +71,7 @@ pub fn spawn(
             .name("Flycheck".to_owned())
             .spawn(move || actor.run(receiver))
             .expect("failed to spawn thread");
-        FlycheckHandle { sender, thread }
+        FlycheckHandle { sender, _thread: thread }
     }
 
     /// Schedule a re-start of the cargo check worker.
index ea1afb46d6cebac07846cf7187f69e078c30b993..43059bb8cce1d07562a046f4cf44b386269e602c 100644 (file)
@@ -15,7 +15,6 @@ arrayvec = "0.7"
 itertools = "0.10.0"
 smallvec = "1.4.0"
 once_cell = "1"
-indexmap = "1.7"
 
 stdx = { path = "../stdx", version = "0.0.0" }
 syntax = { path = "../syntax", version = "0.0.0" }
index dff4372738b7f3025e329ccaeed76e23b774803d..4ef7d399de3ec078f1d2de9c10586e203d303133 100644 (file)
@@ -5,16 +5,19 @@
     type_ref::{TypeBound, TypeRef},
     AdtId, GenericDefId,
 };
-use hir_ty::display::{
-    write_bounds_like_dyn_trait_with_prefix, write_visibility, HirDisplay, HirDisplayError,
-    HirFormatter, SizedByDefault,
+use hir_ty::{
+    display::{
+        write_bounds_like_dyn_trait_with_prefix, write_visibility, HirDisplay, HirDisplayError,
+        HirFormatter, SizedByDefault,
+    },
+    Interner, TraitRefExt, WhereClause,
 };
-use hir_ty::Interner;
 use syntax::ast::{self, NameOwner};
 
 use crate::{
-    Adt, Const, ConstParam, Enum, Field, Function, GenericParam, HasVisibility, LifetimeParam,
-    Module, Static, Struct, Trait, TyBuilder, Type, TypeAlias, TypeParam, Union, Variant,
+    Adt, Const, ConstParam, Enum, Field, Function, GenericParam, HasCrate, HasVisibility,
+    LifetimeParam, Module, Static, Struct, Trait, TyBuilder, Type, TypeAlias, TypeParam, Union,
+    Variant,
 };
 
 impl HirDisplay for Function {
@@ -234,12 +237,24 @@ fn hir_fmt(&self, f: &mut HirFormatter) -> Result<(), HirDisplayError> {
 impl HirDisplay for TypeParam {
     fn hir_fmt(&self, f: &mut HirFormatter) -> Result<(), HirDisplayError> {
         write!(f, "{}", self.name(f.db))?;
+        if f.omit_verbose_types() {
+            return Ok(());
+        }
+
         let bounds = f.db.generic_predicates_for_param(self.id);
         let substs = TyBuilder::type_params_subst(f.db, self.id.parent);
-        let predicates =
-            bounds.iter().cloned().map(|b| b.substitute(&Interner, &substs)).collect::<Vec<_>>();
-        if !(predicates.is_empty() || f.omit_verbose_types()) {
-            let default_sized = SizedByDefault::Sized { anchor: self.module(f.db).krate().id };
+        let predicates: Vec<_> =
+            bounds.iter().cloned().map(|b| b.substitute(&Interner, &substs)).collect();
+        let krate = self.id.parent.krate(f.db).id;
+        let sized_trait =
+            f.db.lang_item(krate, "sized".into()).and_then(|lang_item| lang_item.as_trait());
+        let has_only_sized_bound = predicates.iter().all(move |pred| match pred.skip_binders() {
+            WhereClause::Implemented(it) => Some(it.hir_trait_id()) == sized_trait,
+            _ => false,
+        });
+        let has_only_not_sized_bound = predicates.is_empty();
+        if !has_only_sized_bound || has_only_not_sized_bound {
+            let default_sized = SizedByDefault::Sized { anchor: krate };
             write_bounds_like_dyn_trait_with_prefix(":", &predicates, default_sized, f)?;
         }
         Ok(())
index 62c634b549f1fe732f8f36f55f97f9f323903756..3b0c29e87e8e7145d3a37fde0341a214dfdc0961 100644 (file)
@@ -31,7 +31,7 @@
 
 mod display;
 
-use std::{iter, sync::Arc};
+use std::{iter, ops::ControlFlow, sync::Arc};
 
 use arrayvec::ArrayVec;
 use base_db::{CrateDisplayName, CrateId, Edition, FileId};
@@ -2573,12 +2573,14 @@ pub fn iterate_method_candidates<T>(
             krate,
             traits_in_scope,
             name,
-            &mut |ty, assoc_item_id| match assoc_item_id {
-                AssocItemId::FunctionId(it) => {
-                    slot = callback(self.derived(ty.clone()), it.into());
-                    slot.is_some()
+            &mut |ty, assoc_item_id| {
+                if let AssocItemId::FunctionId(func) = assoc_item_id {
+                    if let Some(res) = callback(self.derived(ty.clone()), func.into()) {
+                        slot = Some(res);
+                        return ControlFlow::Break(());
+                    }
                 }
-                AssocItemId::ConstId(_) | AssocItemId::TypeAliasId(_) => false,
+                ControlFlow::Continue(())
             },
         );
         slot
@@ -2590,7 +2592,7 @@ fn iterate_method_candidates_dyn(
         krate: Crate,
         traits_in_scope: &FxHashSet<TraitId>,
         name: Option<&Name>,
-        callback: &mut dyn FnMut(&Ty, AssocItemId) -> bool,
+        callback: &mut dyn FnMut(&Ty, AssocItemId) -> ControlFlow<()>,
     ) {
         // There should be no inference vars in types passed here
         // FIXME check that?
@@ -2610,7 +2612,7 @@ fn iterate_method_candidates_dyn(
             None,
             name,
             method_resolution::LookupMode::MethodCall,
-            callback,
+            &mut |ty, id| callback(&ty.value, id),
         );
     }
 
@@ -2630,8 +2632,11 @@ pub fn iterate_path_candidates<T>(
             traits_in_scope,
             name,
             &mut |ty, assoc_item_id| {
-                slot = callback(self.derived(ty.clone()), assoc_item_id.into());
-                slot.is_some()
+                if let Some(res) = callback(self.derived(ty.clone()), assoc_item_id.into()) {
+                    slot = Some(res);
+                    return ControlFlow::Break(());
+                }
+                ControlFlow::Continue(())
             },
         );
         slot
@@ -2643,7 +2648,7 @@ fn iterate_path_candidates_dyn(
         krate: Crate,
         traits_in_scope: &FxHashSet<TraitId>,
         name: Option<&Name>,
-        callback: &mut dyn FnMut(&Ty, AssocItemId) -> bool,
+        callback: &mut dyn FnMut(&Ty, AssocItemId) -> ControlFlow<()>,
     ) {
         let canonical = hir_ty::replace_errors_with_variables(&self.ty);
 
@@ -2659,7 +2664,7 @@ fn iterate_path_candidates_dyn(
             None,
             name,
             method_resolution::LookupMode::Path,
-            callback,
+            &mut |ty, id| callback(&ty.value, id),
         );
     }
 
index b81332c68fa5745d82e6795e51dc65e2fab8bcaf..51befc698c053d210ea5d3fdf71e7acac121013e 100644 (file)
@@ -16,9 +16,9 @@
 use rustc_hash::{FxHashMap, FxHashSet};
 use smallvec::{smallvec, SmallVec};
 use syntax::{
-    algo::find_node_at_offset,
+    algo::skip_trivia_token,
     ast::{self, GenericParamsOwner, LoopBodyOwner},
-    match_ast, AstNode, SyntaxNode, SyntaxNodePtr, SyntaxToken, TextRange, TextSize,
+    match_ast, AstNode, Direction, SyntaxNode, SyntaxNodePtr, SyntaxToken, TextRange, TextSize,
 };
 
 use crate::{
@@ -166,6 +166,15 @@ pub fn speculative_expand(
         self.imp.speculative_expand(actual_macro_call, speculative_args, token_to_map)
     }
 
+    pub fn speculative_expand_attr_macro(
+        &self,
+        actual_macro_call: &ast::Item,
+        speculative_args: &ast::Item,
+        token_to_map: SyntaxToken,
+    ) -> Option<(SyntaxNode, SyntaxToken)> {
+        self.imp.speculative_expand_attr(actual_macro_call, speculative_args, token_to_map)
+    }
+
     // FIXME: Rename to descend_into_macros_single
     pub fn descend_into_macros(&self, token: SyntaxToken) -> SyntaxToken {
         self.imp.descend_into_macros(token).pop().unwrap()
@@ -176,12 +185,9 @@ pub fn descend_into_macros(&self, token: SyntaxToken) -> SyntaxToken {
         self.imp.descend_into_macros(token)
     }
 
-    pub fn descend_node_at_offset<N: ast::AstNode>(
-        &self,
-        node: &SyntaxNode,
-        offset: TextSize,
-    ) -> Option<N> {
-        self.imp.descend_node_at_offset(node, offset).flatten().find_map(N::cast)
+    /// Maps a node down by mapping its first and last token down.
+    pub fn descend_node_into_attributes<N: AstNode>(&self, node: N) -> SmallVec<[N; 1]> {
+        self.imp.descend_node_into_attributes(node)
     }
 
     pub fn hir_file_for(&self, syntax_node: &SyntaxNode) -> HirFileId {
@@ -192,6 +198,10 @@ pub fn original_range(&self, node: &SyntaxNode) -> FileRange {
         self.imp.original_range(node)
     }
 
+    pub fn original_range_opt(&self, node: &SyntaxNode) -> Option<FileRange> {
+        self.imp.original_range_opt(node)
+    }
+
     pub fn diagnostics_display_range(&self, diagnostics: InFile<SyntaxNodePtr>) -> FileRange {
         self.imp.diagnostics_display_range(diagnostics)
     }
@@ -202,6 +212,7 @@ pub fn token_ancestors_with_macros(
     ) -> impl Iterator<Item = SyntaxNode> + '_ {
         token.parent().into_iter().flat_map(move |it| self.ancestors_with_macros(it))
     }
+
     pub fn ancestors_with_macros(&self, node: SyntaxNode) -> impl Iterator<Item = SyntaxNode> + '_ {
         self.imp.ancestors_with_macros(node)
     }
@@ -231,10 +242,6 @@ pub fn find_node_at_offset_with_descend<N: AstNode>(
         node: &SyntaxNode,
         offset: TextSize,
     ) -> Option<N> {
-        if let Some(it) = find_node_at_offset(node, offset) {
-            return Some(it);
-        }
-
         self.imp.descend_node_at_offset(node, offset).flatten().find_map(N::cast)
     }
 
@@ -452,107 +459,182 @@ fn speculative_expand(
         hir_expand::db::expand_speculative(
             self.db.upcast(),
             macro_call_id,
-            speculative_args,
+            speculative_args.syntax(),
             token_to_map,
         )
     }
 
+    fn speculative_expand_attr(
+        &self,
+        actual_macro_call: &ast::Item,
+        speculative_args: &ast::Item,
+        token_to_map: SyntaxToken,
+    ) -> Option<(SyntaxNode, SyntaxToken)> {
+        let sa = self.analyze(actual_macro_call.syntax());
+        let macro_call = InFile::new(sa.file_id, actual_macro_call.clone());
+        let macro_call_id = self.with_ctx(|ctx| ctx.item_to_macro_call(macro_call))?;
+        hir_expand::db::expand_speculative(
+            self.db.upcast(),
+            macro_call_id,
+            speculative_args.syntax(),
+            token_to_map,
+        )
+    }
+
+    // This might not be the correct way to do this, but it works for now
+    fn descend_node_into_attributes<N: AstNode>(&self, node: N) -> SmallVec<[N; 1]> {
+        let mut res = smallvec![];
+        let tokens = (|| {
+            let first = skip_trivia_token(node.syntax().first_token()?, Direction::Next)?;
+            let last = skip_trivia_token(node.syntax().last_token()?, Direction::Prev)?;
+            Some((first, last))
+        })();
+        let (first, last) = match tokens {
+            Some(it) => it,
+            None => return res,
+        };
+
+        if first == last {
+            self.descend_into_macros_impl(first, |InFile { value, .. }| {
+                if let Some(node) = value.ancestors().find_map(N::cast) {
+                    res.push(node)
+                }
+            });
+        } else {
+            // Descend first and last token, then zip them to look for the node they belong to
+            let mut scratch: SmallVec<[_; 1]> = smallvec![];
+            self.descend_into_macros_impl(first, |token| {
+                scratch.push(token);
+            });
+
+            let mut scratch = scratch.into_iter();
+            self.descend_into_macros_impl(last, |InFile { value: last, file_id: last_fid }| {
+                if let Some(InFile { value: first, file_id: first_fid }) = scratch.next() {
+                    if first_fid == last_fid {
+                        if let Some(p) = first.parent() {
+                            let range = first.text_range().cover(last.text_range());
+                            let node = find_root(&p)
+                                .covering_element(range)
+                                .ancestors()
+                                .take_while(|it| it.text_range() == range)
+                                .find_map(N::cast);
+                            if let Some(node) = node {
+                                res.push(node);
+                            }
+                        }
+                    }
+                }
+            });
+        }
+        res
+    }
+
     fn descend_into_macros(&self, token: SyntaxToken) -> SmallVec<[SyntaxToken; 1]> {
+        let mut res = smallvec![];
+        self.descend_into_macros_impl(token, |InFile { value, .. }| res.push(value));
+        res
+    }
+
+    fn descend_into_macros_impl(&self, token: SyntaxToken, mut f: impl FnMut(InFile<SyntaxToken>)) {
         let _p = profile::span("descend_into_macros");
         let parent = match token.parent() {
             Some(it) => it,
-            None => return smallvec![token],
+            None => return,
         };
         let sa = self.analyze(&parent);
         let mut queue = vec![InFile::new(sa.file_id, token)];
         let mut cache = self.expansion_info_cache.borrow_mut();
-        let mut res = smallvec![];
         // Remap the next token in the queue into a macro call its in, if it is not being remapped
         // either due to not being in a macro-call or because its unused push it into the result vec,
         // otherwise push the remapped tokens back into the queue as they can potentially be remapped again.
         while let Some(token) = queue.pop() {
             self.db.unwind_if_cancelled();
-
             let was_not_remapped = (|| {
-                for node in token.value.ancestors() {
-                    if let Some(macro_call) = ast::MacroCall::cast(node.clone()) {
-                        let tt = match macro_call.token_tree() {
-                            Some(tt) => tt,
-                            None => continue,
-                        };
-                        let l_delim = match tt.left_delimiter_token() {
-                            Some(it) => it.text_range().end(),
-                            None => tt.syntax().text_range().start(),
-                        };
-                        let r_delim = match tt.right_delimiter_token() {
-                            Some(it) => it.text_range().start(),
-                            None => tt.syntax().text_range().end(),
-                        };
-                        if !TextRange::new(l_delim, r_delim)
-                            .contains_range(token.value.text_range())
-                        {
-                            continue;
-                        }
-                        let file_id = match sa.expand(self.db, token.with_value(&macro_call)) {
-                            Some(file_id) => file_id,
-                            None => continue,
-                        };
-                        let tokens = cache
-                            .entry(file_id)
-                            .or_insert_with(|| file_id.expansion_info(self.db.upcast()))
-                            .as_ref()?
-                            .map_token_down(self.db.upcast(), None, token.as_ref())?;
-
-                        let len = queue.len();
-                        queue.extend(tokens.inspect(|token| {
-                            if let Some(parent) = token.value.parent() {
-                                self.cache(find_root(&parent), token.file_id);
-                            }
-                        }));
-                        return (queue.len() != len).then(|| ());
-                    } else if let Some(item) = ast::Item::cast(node.clone()) {
-                        if let Some(call_id) = self
-                            .with_ctx(|ctx| ctx.item_to_macro_call(token.with_value(item.clone())))
-                        {
-                            let file_id = call_id.as_file();
-                            let tokens = cache
-                                .entry(file_id)
-                                .or_insert_with(|| file_id.expansion_info(self.db.upcast()))
-                                .as_ref()?
-                                .map_token_down(self.db.upcast(), Some(item), token.as_ref())?;
-
-                            let len = queue.len();
-                            queue.extend(tokens.inspect(|token| {
-                                if let Some(parent) = token.value.parent() {
-                                    self.cache(find_root(&parent), token.file_id);
-                                }
-                            }));
-                            return (queue.len() != len).then(|| ());
+                if let Some((call_id, item)) = token
+                    .value
+                    .ancestors()
+                    .filter_map(ast::Item::cast)
+                    .filter_map(|item| {
+                        self.with_ctx(|ctx| ctx.item_to_macro_call(token.with_value(item.clone())))
+                            .zip(Some(item))
+                    })
+                    .last()
+                {
+                    let file_id = call_id.as_file();
+                    let tokens = cache
+                        .entry(file_id)
+                        .or_insert_with(|| file_id.expansion_info(self.db.upcast()))
+                        .as_ref()?
+                        .map_token_down(self.db.upcast(), Some(item), token.as_ref())?;
+
+                    let len = queue.len();
+                    queue.extend(tokens.inspect(|token| {
+                        if let Some(parent) = token.value.parent() {
+                            self.cache(find_root(&parent), token.file_id);
                         }
+                    }));
+                    return (queue.len() != len).then(|| ());
+                }
+
+                if let Some(macro_call) = token.value.ancestors().find_map(ast::MacroCall::cast) {
+                    let tt = macro_call.token_tree()?;
+                    let l_delim = match tt.left_delimiter_token() {
+                        Some(it) => it.text_range().end(),
+                        None => tt.syntax().text_range().start(),
+                    };
+                    let r_delim = match tt.right_delimiter_token() {
+                        Some(it) => it.text_range().start(),
+                        None => tt.syntax().text_range().end(),
+                    };
+                    if !TextRange::new(l_delim, r_delim).contains_range(token.value.text_range()) {
+                        return None;
                     }
+                    let file_id = sa.expand(self.db, token.with_value(&macro_call))?;
+                    let tokens = cache
+                        .entry(file_id)
+                        .or_insert_with(|| file_id.expansion_info(self.db.upcast()))
+                        .as_ref()?
+                        .map_token_down(self.db.upcast(), None, token.as_ref())?;
+
+                    let len = queue.len();
+                    queue.extend(tokens.inspect(|token| {
+                        if let Some(parent) = token.value.parent() {
+                            self.cache(find_root(&parent), token.file_id);
+                        }
+                    }));
+                    return (queue.len() != len).then(|| ());
                 }
                 None
             })()
             .is_none();
+
             if was_not_remapped {
-                res.push(token.value)
+                f(token)
             }
         }
-        res
     }
 
     // Note this return type is deliberate as [`find_nodes_at_offset_with_descend`] wants to stop
     // traversing the inner iterator when it finds a node.
+    // The outer iterator is over the tokens descendants
+    // The inner iterator is the ancestors of a descendant
     fn descend_node_at_offset(
         &self,
         node: &SyntaxNode,
         offset: TextSize,
     ) -> impl Iterator<Item = impl Iterator<Item = SyntaxNode> + '_> + '_ {
-        // Handle macro token cases
         node.token_at_offset(offset)
             .map(move |token| self.descend_into_macros(token))
-            .map(|it| it.into_iter().map(move |it| self.token_ancestors_with_macros(it)))
-            .flatten()
+            .map(|descendants| {
+                descendants.into_iter().map(move |it| self.token_ancestors_with_macros(it))
+            })
+            // re-order the tokens from token_at_offset by returning the ancestors with the smaller first nodes first
+            // See algo::ancestors_at_offset, which uses the same approach
+            .kmerge_by(|left, right| {
+                left.clone()
+                    .map(|node| node.text_range().len())
+                    .lt(right.clone().map(|node| node.text_range().len()))
+            })
     }
 
     fn original_range(&self, node: &SyntaxNode) -> FileRange {
@@ -560,6 +642,11 @@ fn original_range(&self, node: &SyntaxNode) -> FileRange {
         node.as_ref().original_file_range(self.db.upcast())
     }
 
+    fn original_range_opt(&self, node: &SyntaxNode) -> Option<FileRange> {
+        let node = self.find_file(node.clone());
+        node.as_ref().original_file_range_opt(self.db.upcast())
+    }
+
     fn diagnostics_display_range(&self, src: InFile<SyntaxNodePtr>) -> FileRange {
         let root = self.db.parse_or_expand(src.file_id).unwrap();
         let node = src.value.to_node(&root);
@@ -570,11 +657,14 @@ fn diagnostics_display_range(&self, src: InFile<SyntaxNodePtr>) -> FileRange {
     fn token_ancestors_with_macros(
         &self,
         token: SyntaxToken,
-    ) -> impl Iterator<Item = SyntaxNode> + '_ {
+    ) -> impl Iterator<Item = SyntaxNode> + Clone + '_ {
         token.parent().into_iter().flat_map(move |parent| self.ancestors_with_macros(parent))
     }
 
-    fn ancestors_with_macros(&self, node: SyntaxNode) -> impl Iterator<Item = SyntaxNode> + '_ {
+    fn ancestors_with_macros(
+        &self,
+        node: SyntaxNode,
+    ) -> impl Iterator<Item = SyntaxNode> + Clone + '_ {
         let node = self.find_file(node);
         node.ancestors_with_macros(self.db.upcast()).map(|it| it.value)
     }
@@ -592,20 +682,7 @@ fn ancestors_at_offset_with_macros(
     fn resolve_lifetime_param(&self, lifetime: &ast::Lifetime) -> Option<LifetimeParam> {
         let text = lifetime.text();
         let lifetime_param = lifetime.syntax().ancestors().find_map(|syn| {
-            let gpl = match_ast! {
-                match syn {
-                    ast::Fn(it) => it.generic_param_list()?,
-                    ast::TypeAlias(it) => it.generic_param_list()?,
-                    ast::Struct(it) => it.generic_param_list()?,
-                    ast::Enum(it) => it.generic_param_list()?,
-                    ast::Union(it) => it.generic_param_list()?,
-                    ast::Trait(it) => it.generic_param_list()?,
-                    ast::Impl(it) => it.generic_param_list()?,
-                    ast::WherePred(it) => it.generic_param_list()?,
-                    ast::ForType(it) => it.generic_param_list()?,
-                    _ => return None,
-                }
-            };
+            let gpl = ast::DynGenericParamsOwner::cast(syn)?.generic_param_list()?;
             gpl.lifetime_params()
                 .find(|tp| tp.lifetime().as_ref().map(|lt| lt.text()).as_ref() == Some(&text))
         })?;
index 1f80fd3404e6865335036410605ef14f02edc164..723c7a1727b2f65ca9bd628dd06d2200a95514a5 100644 (file)
@@ -131,8 +131,12 @@ impl SourceToDefCtx<'_, '_> {
 
     pub(super) fn module_to_def(&mut self, src: InFile<ast::Module>) -> Option<ModuleId> {
         let _p = profile::span("module_to_def");
-        let parent_declaration =
-            src.syntax().cloned().ancestors_with_macros(self.db.upcast()).skip(1).find_map(|it| {
+        let parent_declaration = src
+            .syntax()
+            .cloned()
+            .ancestors_with_macros_skip_attr_item(self.db.upcast())
+            .skip(1)
+            .find_map(|it| {
                 let m = ast::Module::cast(it.value.clone())?;
                 Some(it.with_value(m))
             });
@@ -306,7 +310,8 @@ pub(super) fn macro_to_def(&mut self, src: InFile<ast::Macro>) -> Option<MacroDe
     }
 
     pub(super) fn find_container(&mut self, src: InFile<&SyntaxNode>) -> Option<ChildContainer> {
-        for container in src.cloned().ancestors_with_macros(self.db.upcast()).skip(1) {
+        for container in src.cloned().ancestors_with_macros_skip_attr_item(self.db.upcast()).skip(1)
+        {
             if let Some(res) = self.container_to_def(container) {
                 return Some(res);
             }
@@ -370,7 +375,8 @@ fn container_to_def(&mut self, container: InFile<SyntaxNode>) -> Option<ChildCon
     }
 
     fn find_generic_param_container(&mut self, src: InFile<&SyntaxNode>) -> Option<GenericDefId> {
-        for container in src.cloned().ancestors_with_macros(self.db.upcast()).skip(1) {
+        for container in src.cloned().ancestors_with_macros_skip_attr_item(self.db.upcast()).skip(1)
+        {
             let res: GenericDefId = match_ast! {
                 match (container.value) {
                     ast::Fn(it) => self.fn_to_def(container.with_value(it))?.into(),
@@ -388,7 +394,8 @@ fn find_generic_param_container(&mut self, src: InFile<&SyntaxNode>) -> Option<G
     }
 
     fn find_pat_or_label_container(&mut self, src: InFile<&SyntaxNode>) -> Option<DefWithBodyId> {
-        for container in src.cloned().ancestors_with_macros(self.db.upcast()).skip(1) {
+        for container in src.cloned().ancestors_with_macros_skip_attr_item(self.db.upcast()).skip(1)
+        {
             let res: DefWithBodyId = match_ast! {
                 match (container.value) {
                     ast::Const(it) => self.const_to_def(container.with_value(it))?.into(),
index b6ee5968fa2f26eddf399d942c798427fababa26..960b290e26689759f1ad188179c3c8d3cf79714d 100644 (file)
@@ -43,7 +43,6 @@ pub(crate) struct SourceAnalyzer {
     body: Option<Arc<Body>>,
     body_source_map: Option<Arc<BodySourceMap>>,
     infer: Option<Arc<InferenceResult>>,
-    scopes: Option<Arc<ExprScopes>>,
 }
 
 impl SourceAnalyzer {
@@ -65,7 +64,6 @@ pub(crate) fn new_for_body(
             body: Some(body),
             body_source_map: Some(source_map),
             infer: Some(db.infer(def)),
-            scopes: Some(scopes),
             file_id: node.file_id,
         }
     }
@@ -79,7 +77,6 @@ pub(crate) fn new_for_resolver(
             body: None,
             body_source_map: None,
             infer: None,
-            scopes: None,
             file_id: node.file_id,
         }
     }
index f8cc940486d43356c51dab4e35df67280a06dcc3..70fa249a5b08584beb1bf8496446e510f68ee579 100644 (file)
@@ -17,7 +17,7 @@
 use mbe::{syntax_node_to_token_tree, DelimiterKind};
 use smallvec::{smallvec, SmallVec};
 use syntax::{
-    ast::{self, AstNode, AttrsOwner},
+    ast::{self, AstNode, AttrsOwner, IsString},
     match_ast, AstPtr, AstToken, SmolStr, SyntaxNode, TextRange, TextSize,
 };
 use tt::Subtree;
@@ -411,47 +411,47 @@ pub fn source_map(&self, db: &dyn DefDatabase) -> AttrSourceMap {
                 let file_id = id.parent.file_id(db);
                 let root = db.parse_or_expand(file_id).unwrap();
                 let owner = match &map[id.local_id] {
-                    Either::Left(it) => ast::AttrsOwnerNode::new(it.to_node(&root)),
-                    Either::Right(it) => ast::AttrsOwnerNode::new(it.to_node(&root)),
+                    Either::Left(it) => ast::DynAttrsOwner::new(it.to_node(&root)),
+                    Either::Right(it) => ast::DynAttrsOwner::new(it.to_node(&root)),
                 };
                 InFile::new(file_id, owner)
             }
             AttrDefId::AdtId(adt) => match adt {
-                AdtId::StructId(id) => id.lookup(db).source(db).map(ast::AttrsOwnerNode::new),
-                AdtId::UnionId(id) => id.lookup(db).source(db).map(ast::AttrsOwnerNode::new),
-                AdtId::EnumId(id) => id.lookup(db).source(db).map(ast::AttrsOwnerNode::new),
+                AdtId::StructId(id) => id.lookup(db).source(db).map(ast::DynAttrsOwner::new),
+                AdtId::UnionId(id) => id.lookup(db).source(db).map(ast::DynAttrsOwner::new),
+                AdtId::EnumId(id) => id.lookup(db).source(db).map(ast::DynAttrsOwner::new),
             },
-            AttrDefId::FunctionId(id) => id.lookup(db).source(db).map(ast::AttrsOwnerNode::new),
+            AttrDefId::FunctionId(id) => id.lookup(db).source(db).map(ast::DynAttrsOwner::new),
             AttrDefId::EnumVariantId(id) => {
                 let map = db.variants_attrs_source_map(id.parent);
                 let file_id = id.parent.lookup(db).id.file_id();
                 let root = db.parse_or_expand(file_id).unwrap();
-                InFile::new(file_id, ast::AttrsOwnerNode::new(map[id.local_id].to_node(&root)))
+                InFile::new(file_id, ast::DynAttrsOwner::new(map[id.local_id].to_node(&root)))
             }
-            AttrDefId::StaticId(id) => id.lookup(db).source(db).map(ast::AttrsOwnerNode::new),
-            AttrDefId::ConstId(id) => id.lookup(db).source(db).map(ast::AttrsOwnerNode::new),
-            AttrDefId::TraitId(id) => id.lookup(db).source(db).map(ast::AttrsOwnerNode::new),
-            AttrDefId::TypeAliasId(id) => id.lookup(db).source(db).map(ast::AttrsOwnerNode::new),
+            AttrDefId::StaticId(id) => id.lookup(db).source(db).map(ast::DynAttrsOwner::new),
+            AttrDefId::ConstId(id) => id.lookup(db).source(db).map(ast::DynAttrsOwner::new),
+            AttrDefId::TraitId(id) => id.lookup(db).source(db).map(ast::DynAttrsOwner::new),
+            AttrDefId::TypeAliasId(id) => id.lookup(db).source(db).map(ast::DynAttrsOwner::new),
             AttrDefId::MacroDefId(id) => id.ast_id().either(
-                |it| it.with_value(ast::AttrsOwnerNode::new(it.to_node(db.upcast()))),
-                |it| it.with_value(ast::AttrsOwnerNode::new(it.to_node(db.upcast()))),
+                |it| it.with_value(ast::DynAttrsOwner::new(it.to_node(db.upcast()))),
+                |it| it.with_value(ast::DynAttrsOwner::new(it.to_node(db.upcast()))),
             ),
-            AttrDefId::ImplId(id) => id.lookup(db).source(db).map(ast::AttrsOwnerNode::new),
+            AttrDefId::ImplId(id) => id.lookup(db).source(db).map(ast::DynAttrsOwner::new),
             AttrDefId::GenericParamId(id) => match id {
                 GenericParamId::TypeParamId(id) => {
                     id.parent.child_source(db).map(|source| match &source[id.local_id] {
-                        Either::Left(id) => ast::AttrsOwnerNode::new(id.clone()),
-                        Either::Right(id) => ast::AttrsOwnerNode::new(id.clone()),
+                        Either::Left(id) => ast::DynAttrsOwner::new(id.clone()),
+                        Either::Right(id) => ast::DynAttrsOwner::new(id.clone()),
                     })
                 }
                 GenericParamId::LifetimeParamId(id) => id
                     .parent
                     .child_source(db)
-                    .map(|source| ast::AttrsOwnerNode::new(source[id.local_id].clone())),
+                    .map(|source| ast::DynAttrsOwner::new(source[id.local_id].clone())),
                 GenericParamId::ConstParamId(id) => id
                     .parent
                     .child_source(db)
-                    .map(|source| ast::AttrsOwnerNode::new(source[id.local_id].clone())),
+                    .map(|source| ast::DynAttrsOwner::new(source[id.local_id].clone())),
             },
         };
 
@@ -547,6 +547,7 @@ fn inner_attributes(
     Some((attrs, docs))
 }
 
+#[derive(Debug)]
 pub struct AttrSourceMap {
     attrs: Vec<InFile<ast::Attr>>,
     doc_comments: Vec<InFile<ast::Comment>>,
@@ -599,6 +600,7 @@ fn source_of_id(&self, id: AttrId) -> InFile<Either<ast::Attr, ast::Comment>> {
 }
 
 /// A struct to map text ranges from [`Documentation`] back to TextRanges in the syntax tree.
+#[derive(Debug)]
 pub struct DocsRangeMap {
     source_map: AttrSourceMap,
     // (docstring-line-range, attr_index, attr-string-range)
@@ -608,6 +610,7 @@ pub struct DocsRangeMap {
 }
 
 impl DocsRangeMap {
+    /// Maps a [`TextRange`] relative to the documentation string back to its AST range
     pub fn map(&self, range: TextRange) -> Option<InFile<TextRange>> {
         let found = self.mapping.binary_search_by(|(probe, ..)| probe.ordering(range)).ok()?;
         let (line_docs_range, idx, original_line_src_range) = self.mapping[found];
@@ -619,8 +622,15 @@ pub fn map(&self, range: TextRange) -> Option<InFile<TextRange>> {
 
         let InFile { file_id, value: source } = self.source_map.source_of_id(idx);
         match source {
-            Either::Left(_) => None, // FIXME, figure out a nice way to handle doc attributes here
-            // as well as for whats done in syntax highlight doc injection
+            Either::Left(attr) => {
+                let string = get_doc_string_in_attr(&attr)?;
+                let text_range = string.open_quote_text_range()?;
+                let range = TextRange::at(
+                    text_range.end() + original_line_src_range.start() + relative_range.start(),
+                    string.syntax().text_range().len().min(range.len()),
+                );
+                Some(InFile { file_id, value: range })
+            }
             Either::Right(comment) => {
                 let text_range = comment.syntax().text_range();
                 let range = TextRange::at(
@@ -636,6 +646,22 @@ pub fn map(&self, range: TextRange) -> Option<InFile<TextRange>> {
     }
 }
 
+fn get_doc_string_in_attr(it: &ast::Attr) -> Option<ast::String> {
+    match it.expr() {
+        // #[doc = lit]
+        Some(ast::Expr::Literal(lit)) => match lit.kind() {
+            ast::LiteralKind::String(it) => Some(it),
+            _ => None,
+        },
+        // #[cfg_attr(..., doc = "", ...)]
+        None => {
+            // FIXME: See highlight injection for what to do here
+            None
+        }
+        _ => None,
+    }
+}
+
 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
 pub(crate) struct AttrId {
     is_doc_comment: bool,
index 165cdcba00c7dd7cba993f12b501493aec081e9b..24083172b304964ab9516a8b62336d9874925ba7 100644 (file)
@@ -1812,7 +1812,20 @@ fn collect_macro_rules(&mut self, id: FileItemTreeId<MacroRules>) {
                     name = tt::Ident { text: it.clone(), id: tt::TokenId::unspecified() }.as_name();
                     &name
                 }
-                None => &mac.name,
+                None => {
+                    match attrs.by_key("rustc_builtin_macro").tt_values().next().and_then(|tt| {
+                        match tt.token_trees.first() {
+                            Some(tt::TokenTree::Leaf(tt::Leaf::Ident(name))) => Some(name),
+                            _ => None,
+                        }
+                    }) {
+                        Some(ident) => {
+                            name = ident.as_name();
+                            &name
+                        }
+                        None => &mac.name,
+                    }
+                }
             };
             let krate = self.def_collector.def_map.krate;
             match find_builtin_macro(name, krate, ast_id) {
index 5655a8fdffeaef063fe5a163ed84f27c87a7de0b..b39de7cfaeeaeadf4e3632044be230976d17cff7 100644 (file)
@@ -25,5 +25,4 @@ mbe = { path = "../mbe", version = "0.0.0" }
 limit = { path = "../limit", version = "0.0.0" }
 
 [dev-dependencies]
-test_utils = { path = "../test_utils" }
 expect-test = "1.1"
index a4b5632e32d5e1cbcbfd3afc5e9f574e181ca7ae..74f9b23d731ec2f6b7c60bbad303792935296c20 100644 (file)
@@ -792,6 +792,23 @@ macro_rules! format_args {
         );
     }
 
+    #[test]
+    fn test_format_args_expand_with_broken_member_access() {
+        check_expansion(
+            r#"
+            #[rustc_builtin_macro]
+            macro_rules! format_args {
+                ($fmt:expr) => ({ /* compiler built-in */ });
+                ($fmt:expr, $($args:tt)*) => ({ /* compiler built-in */ })
+            }
+            format_args!("{} {:?}", a.);
+            "#,
+            expect![[
+                r#"unsafe{std::fmt::Arguments::new_v1(&[], &[std::fmt::ArgumentV1::new(&(a.),std::fmt::Display::fmt),])}"#
+            ]],
+        );
+    }
+
     #[test]
     fn test_include_bytes_expand() {
         check_expansion(
index 14fe5cd3bbc141776db9387b2db43b993f427ba3..09ff9729602b97d948ad1b268c4e829d891853d4 100644 (file)
@@ -3,13 +3,13 @@
 use std::sync::Arc;
 
 use base_db::{salsa, SourceDatabase};
-use itertools::Itertools;
 use limit::Limit;
-use mbe::{ExpandError, ExpandResult};
+use mbe::{syntax_node_to_token_tree, ExpandError, ExpandResult};
+use rustc_hash::FxHashSet;
 use syntax::{
     algo::diff,
     ast::{self, AttrsOwner, NameOwner},
-    AstNode, GreenNode, Parse, SyntaxNode, SyntaxToken, TextRange, T,
+    AstNode, GreenNode, Parse, SyntaxNode, SyntaxToken, T,
 };
 
 use crate::{
@@ -141,27 +141,72 @@ fn parse_macro_expansion(
 pub fn expand_speculative(
     db: &dyn AstDatabase,
     actual_macro_call: MacroCallId,
-    speculative_args: &ast::TokenTree,
+    speculative_args: &SyntaxNode,
     token_to_map: SyntaxToken,
 ) -> Option<(SyntaxNode, SyntaxToken)> {
-    let (tt, tmap_1) = mbe::syntax_node_to_token_tree(speculative_args.syntax());
-    let range =
-        token_to_map.text_range().checked_sub(speculative_args.syntax().text_range().start())?;
-    let token_id = tmap_1.token_by_range(range)?;
-
-    let macro_def = {
-        let loc: MacroCallLoc = db.lookup_intern_macro(actual_macro_call);
-        db.macro_def(loc.def)?
+    let loc = db.lookup_intern_macro(actual_macro_call);
+    let macro_def = db.macro_def(loc.def)?;
+    let token_range = token_to_map.text_range();
+
+    // Build the subtree and token mapping for the speculative args
+    let censor = censor_for_macro_input(&loc, &speculative_args);
+    let (mut tt, spec_args_tmap) =
+        mbe::syntax_node_to_token_tree_censored(&speculative_args, &censor);
+
+    let (attr_arg, token_id) = match loc.kind {
+        MacroCallKind::Attr { invoc_attr_index, .. } => {
+            // Attributes may have an input token tree, build the subtree and map for this as well
+            // then try finding a token id for our token if it is inside this input subtree.
+            let item = ast::Item::cast(speculative_args.clone())?;
+            let attr = item.attrs().nth(invoc_attr_index as usize)?;
+            match attr.token_tree() {
+                Some(token_tree) => {
+                    let (mut tree, map) = syntax_node_to_token_tree(attr.token_tree()?.syntax());
+                    tree.delimiter = None;
+
+                    let shift = mbe::Shift::new(&tt);
+                    shift.shift_all(&mut tree);
+
+                    let token_id = if token_tree.syntax().text_range().contains_range(token_range) {
+                        let attr_input_start =
+                            token_tree.left_delimiter_token()?.text_range().start();
+                        let range = token_range.checked_sub(attr_input_start)?;
+                        let token_id = shift.shift(map.token_by_range(range)?);
+                        Some(token_id)
+                    } else {
+                        None
+                    };
+                    (Some(tree), token_id)
+                }
+                _ => (None, None),
+            }
+        }
+        _ => (None, None),
+    };
+    let token_id = match token_id {
+        Some(token_id) => token_id,
+        // token wasn't inside an attribute input so it has to be in the general macro input
+        None => {
+            let range = token_range.checked_sub(speculative_args.text_range().start())?;
+            let token_id = spec_args_tmap.token_by_range(range)?;
+            macro_def.map_id_down(token_id)
+        }
     };
 
-    let speculative_expansion = macro_def.expand(db, actual_macro_call, &tt);
+    // Do the actual expansion, we need to directly expand the proc macro due to the attribute args
+    // Otherwise the expand query will fetch the non speculative attribute args and pass those instead.
+    let speculative_expansion = if let MacroDefKind::ProcMacro(expander, ..) = loc.def.kind {
+        tt.delimiter = None;
+        expander.expand(db, loc.krate, &tt, attr_arg.as_ref())
+    } else {
+        macro_def.expand(db, actual_macro_call, &tt)
+    };
 
     let expand_to = macro_expand_to(db, actual_macro_call);
+    let (node, rev_tmap) =
+        token_tree_to_syntax_node(&speculative_expansion.value, expand_to).ok()?;
 
-    let (node, tmap_2) = token_tree_to_syntax_node(&speculative_expansion.value, expand_to).ok()?;
-
-    let token_id = macro_def.map_id_down(token_id);
-    let range = tmap_2.first_range_by_token(token_id, token_to_map.kind())?;
+    let range = rev_tmap.first_range_by_token(token_id, token_to_map.kind())?;
     let token = node.syntax_node().covering_element(range).into_token()?;
     Some((node.syntax_node(), token))
 }
@@ -259,24 +304,8 @@ fn macro_arg(db: &dyn AstDatabase, id: MacroCallId) -> Option<Arc<(tt::Subtree,
     let loc = db.lookup_intern_macro(id);
 
     let node = SyntaxNode::new_root(arg);
-    let censor = match loc.kind {
-        MacroCallKind::FnLike { .. } => None,
-        MacroCallKind::Derive { derive_attr_index, .. } => match ast::Item::cast(node.clone()) {
-            Some(item) => item
-                .attrs()
-                .map(|attr| attr.syntax().text_range())
-                .take(derive_attr_index as usize + 1)
-                .fold1(TextRange::cover),
-            None => None,
-        },
-        MacroCallKind::Attr { invoc_attr_index, .. } => match ast::Item::cast(node.clone()) {
-            Some(item) => {
-                item.attrs().nth(invoc_attr_index as usize).map(|attr| attr.syntax().text_range())
-            }
-            None => None,
-        },
-    };
-    let (mut tt, tmap) = mbe::syntax_node_to_token_tree_censored(&node, censor);
+    let censor = censor_for_macro_input(&loc, &node);
+    let (mut tt, tmap) = mbe::syntax_node_to_token_tree_censored(&node, &censor);
 
     if loc.def.is_proc_macro() {
         // proc macros expect their inputs without parentheses, MBEs expect it with them included
@@ -286,6 +315,28 @@ fn macro_arg(db: &dyn AstDatabase, id: MacroCallId) -> Option<Arc<(tt::Subtree,
     Some(Arc::new((tt, tmap)))
 }
 
+fn censor_for_macro_input(loc: &MacroCallLoc, node: &SyntaxNode) -> FxHashSet<SyntaxNode> {
+    (|| {
+        let censor = match loc.kind {
+            MacroCallKind::FnLike { .. } => return None,
+            MacroCallKind::Derive { derive_attr_index, .. } => ast::Item::cast(node.clone())?
+                .attrs()
+                .take(derive_attr_index as usize + 1)
+                .filter(|attr| attr.simple_name().as_deref() == Some("derive"))
+                .map(|it| it.syntax().clone())
+                .collect(),
+            MacroCallKind::Attr { invoc_attr_index, .. } => ast::Item::cast(node.clone())?
+                .attrs()
+                .nth(invoc_attr_index as usize)
+                .map(|attr| attr.syntax().clone())
+                .into_iter()
+                .collect(),
+        };
+        Some(censor)
+    })()
+    .unwrap_or_default()
+}
+
 fn macro_arg_text(db: &dyn AstDatabase, id: MacroCallId) -> Option<GreenNode> {
     let loc = db.lookup_intern_macro(id);
     let arg = loc.kind.arg(db)?;
@@ -367,11 +418,11 @@ fn macro_expand(db: &dyn AstDatabase, id: MacroCallId) -> ExpandResult<Option<Ar
         None => return ExpandResult::str_err("Failed to lower macro args to token tree".into()),
     };
 
-    let macro_rules = match db.macro_def(loc.def) {
+    let expander = match db.macro_def(loc.def) {
         Some(it) => it,
         None => return ExpandResult::str_err("Failed to find macro definition".into()),
     };
-    let ExpandResult { value: tt, err } = macro_rules.expand(db, id, &macro_arg.0);
+    let ExpandResult { value: tt, err } = expander.expand(db, id, &macro_arg.0);
     // Set a hard limit for the expanded tt
     let count = tt.count();
     // XXX: Make ExpandResult a real error and use .map_err instead?
index ac87c5102335e6a2d8f805e4cfe8e71c742b3255..b78d740c53ce7ab350b95975f28984721b78045b 100644 (file)
@@ -204,8 +204,13 @@ fn eager_macro_recur(
 
     // Collect replacement
     for child in children {
-        let def = diagnostic_sink
-            .option_with(|| macro_resolver(child.path()?), || err("failed to resolve macro"))?;
+        let def = diagnostic_sink.option_with(
+            || macro_resolver(child.path()?),
+            || {
+                let path = child.path().map(|path| format!(" `{}!`", path)).unwrap_or_default();
+                err(format!("failed to resolve macro{}", path))
+            },
+        )?;
         let insert = match def.kind {
             MacroDefKind::BuiltInEager(..) => {
                 let id = expand_eager_macro(
index a814a2886d29bb75e7851d61e4d6e76cc9000a11..8bb56e0700dbc247b4adf6cf5a031541cbb57321 100644 (file)
@@ -186,6 +186,17 @@ pub fn is_include_macro(&self, db: &dyn db::AstDatabase) -> bool {
         }
     }
 
+    /// Return whether this file is an include macro
+    pub fn is_attr_macro(&self, db: &dyn db::AstDatabase) -> bool {
+        match self.0 {
+            HirFileIdRepr::MacroFile(macro_file) => {
+                let loc: MacroCallLoc = db.lookup_intern_macro(macro_file.macro_call_id);
+                matches!(loc.kind, MacroCallKind::Attr { .. })
+            }
+            _ => false,
+        }
+    }
+
     pub fn is_macro(self) -> bool {
         matches!(self.0, HirFileIdRepr::MacroFile(_))
     }
@@ -370,6 +381,7 @@ pub fn map_token_down(
     ) -> Option<impl Iterator<Item = InFile<SyntaxToken>> + '_> {
         assert_eq!(token.file_id, self.arg.file_id);
         let token_id = if let Some(item) = item {
+            // check if we are mapping down in an attribute input
             let call_id = match self.expanded.file_id.0 {
                 HirFileIdRepr::FileId(_) => return None,
                 HirFileIdRepr::MacroFile(macro_file) => macro_file.macro_call_id,
@@ -524,7 +536,7 @@ impl InFile<SyntaxNode> {
     pub fn ancestors_with_macros(
         self,
         db: &dyn db::AstDatabase,
-    ) -> impl Iterator<Item = InFile<SyntaxNode>> + '_ {
+    ) -> impl Iterator<Item = InFile<SyntaxNode>> + Clone + '_ {
         iter::successors(Some(self), move |node| match node.value.parent() {
             Some(parent) => Some(node.with_value(parent)),
             None => {
@@ -533,6 +545,26 @@ pub fn ancestors_with_macros(
             }
         })
     }
+
+    /// Skips the attributed item that caused the macro invocation we are climbing up
+    pub fn ancestors_with_macros_skip_attr_item(
+        self,
+        db: &dyn db::AstDatabase,
+    ) -> impl Iterator<Item = InFile<SyntaxNode>> + '_ {
+        iter::successors(Some(self), move |node| match node.value.parent() {
+            Some(parent) => Some(node.with_value(parent)),
+            None => {
+                let parent_node = node.file_id.call_node(db)?;
+                if node.file_id.is_attr_macro(db) {
+                    // macro call was an attributed item, skip it
+                    // FIXME: does this fail if this is a direct expansion of another macro?
+                    parent_node.map(|node| node.parent()).transpose()
+                } else {
+                    Some(parent_node)
+                }
+            }
+        })
+    }
 }
 
 impl<'a> InFile<&'a SyntaxNode> {
index e98d2c1e437c84775d3cde85db8b8fc2a89fb5c4..7566bf687bf9cf81d290aae7b625a33a82b262bb 100644 (file)
@@ -17,9 +17,9 @@ ena = "0.14.0"
 tracing = "0.1"
 rustc-hash = "1.1.0"
 scoped-tls = "1"
-chalk-solve = { version = "0.70", default-features = false }
-chalk-ir = "0.70"
-chalk-recursive = { version = "0.70", default-features = false }
+chalk-solve = { version = "0.71", default-features = false }
+chalk-ir = "0.71"
+chalk-recursive = { version = "0.71", default-features = false }
 la-arena = { version = "0.2.0", path = "../../lib/arena" }
 once_cell = { version = "1.5.0" }
 
@@ -40,4 +40,3 @@ tracing-subscriber = { version = "0.2", default-features = false, features = [
     "registry",
 ] }
 tracing-tree = { version = "0.1.10" }
-once_cell = { version = "1.5.0", features = ["unstable"] }
index 8c18c6dfed015be7882759df2d7327ecc8950316..e3f008645cd55d01664e04e1549f1d05c0eed22e 100644 (file)
@@ -194,7 +194,7 @@ fn deref_by_trait(
             // would have to pass the solution up to the inference context, but
             // that requires a larger refactoring (especially if the deref
             // happens during method resolution). So for the moment, we just
-            // check that we're not in the situation we're we would actually
+            // check that we're not in the situation where we would actually
             // need to handle the values of the additional variables, i.e.
             // they're just being 'passed through'. In the 'standard' case where
             // we have `impl<T> Deref for Foo<T> { Target = T }`, that should be
index 8723e24bf650ee1a64859bfdbe3163944fa3cc16..f58339286f410bb236694874c9a765864f041a98 100644 (file)
@@ -508,6 +508,7 @@ fn lang_attr_from_well_known_trait(attr: WellKnownTrait) -> &'static str {
         WellKnownTrait::Unpin => "unpin",
         WellKnownTrait::CoerceUnsized => "coerce_unsized",
         WellKnownTrait::DiscriminantKind => "discriminant_kind",
+        WellKnownTrait::Generator => "generator",
     }
 }
 
index 899a8b793db5c70616480e78c9a298b37e2bd2ae..abcb84401b4af04b32dd645e21bba5c0d0d7d955 100644 (file)
@@ -22,7 +22,7 @@
 };
 
 pub(crate) use hir_def::{
-    body::{Body, BodySourceMap},
+    body::Body,
     expr::{Expr, ExprId, MatchArm, Pat, PatId},
     LocalFieldId, VariantId,
 };
@@ -264,8 +264,7 @@ fn validate_match(
         db: &dyn HirDatabase,
         infer: Arc<InferenceResult>,
     ) {
-        let (body, source_map): (Arc<Body>, Arc<BodySourceMap>) =
-            db.body_with_source_map(self.owner);
+        let body = db.body(self.owner);
 
         let match_expr_ty = if infer.type_of_expr[match_expr].is_unknown() {
             return;
@@ -330,21 +329,6 @@ fn validate_match(
             infer: &infer,
             db,
             pattern_arena: &pattern_arena,
-            panic_context: &|| {
-                use syntax::AstNode;
-                let match_expr_text = source_map
-                    .expr_syntax(match_expr)
-                    .ok()
-                    .and_then(|scrutinee_sptr| {
-                        let root = scrutinee_sptr.file_syntax(db.upcast());
-                        scrutinee_sptr.value.to_node(&root).syntax().parent()
-                    })
-                    .map(|node| node.to_string());
-                format!(
-                    "expression:\n{}",
-                    match_expr_text.as_deref().unwrap_or("<synthesized expr>")
-                )
-            },
         };
         let report = compute_match_usefulness(&cx, &m_arms);
 
index ecaa3daeafdfab0f3cae2735dc82ddff80cc549e..2fa456a035a3323b63964faf510f3c77acdd7f27 100644 (file)
@@ -49,6 +49,7 @@
 
 use hir_def::{EnumVariantId, HasModule, LocalFieldId, VariantId};
 use smallvec::{smallvec, SmallVec};
+use stdx::never;
 
 use crate::{AdtId, Interner, Scalar, Ty, TyExt, TyKind};
 
@@ -324,7 +325,10 @@ pub(super) fn from_pat(cx: &MatchCheckCtx<'_>, pat: PatId) -> Self {
             PatKind::Leaf { .. } | PatKind::Deref { .. } => Single,
             &PatKind::Variant { enum_variant, .. } => Variant(enum_variant),
             &PatKind::LiteralBool { value } => IntRange(IntRange::from_bool(value)),
-            PatKind::Or { .. } => cx.bug("Or-pattern should have been expanded earlier on."),
+            PatKind::Or { .. } => {
+                never!("Or-pattern should have been expanded earlier on.");
+                Wildcard
+            }
         }
     }
 
@@ -371,7 +375,7 @@ pub(super) fn split<'a>(
     /// this checks for inclusion.
     // We inline because this has a single call site in `Matrix::specialize_constructor`.
     #[inline]
-    pub(super) fn is_covered_by(&self, pcx: PatCtxt<'_>, other: &Self) -> bool {
+    pub(super) fn is_covered_by(&self, _pcx: PatCtxt<'_>, other: &Self) -> bool {
         // This must be kept in sync with `is_covered_by_any`.
         match (self, other) {
             // Wildcards cover anything
@@ -396,17 +400,18 @@ pub(super) fn is_covered_by(&self, pcx: PatCtxt<'_>, other: &Self) -> bool {
             // Only a wildcard pattern can match the special extra constructor.
             (NonExhaustive, _) => false,
 
-            _ => pcx.cx.bug(&format!(
-                "trying to compare incompatible constructors {:?} and {:?}",
-                self, other
-            )),
+            _ => {
+                never!("trying to compare incompatible constructors {:?} and {:?}", self, other);
+                // Continue with 'whatever is covered' supposed to result in false no-error diagnostic.
+                true
+            }
         }
     }
 
     /// Faster version of `is_covered_by` when applied to many constructors. `used_ctors` is
     /// assumed to be built from `matrix.head_ctors()` with wildcards filtered out, and `self` is
     /// assumed to have been split from a wildcard.
-    fn is_covered_by_any(&self, pcx: PatCtxt<'_>, used_ctors: &[Constructor]) -> bool {
+    fn is_covered_by_any(&self, _pcx: PatCtxt<'_>, used_ctors: &[Constructor]) -> bool {
         if used_ctors.is_empty() {
             return false;
         }
@@ -427,7 +432,8 @@ fn is_covered_by_any(&self, pcx: PatCtxt<'_>, used_ctors: &[Constructor]) -> boo
             // This constructor is never covered by anything else
             NonExhaustive => false,
             Str(..) | FloatRange(..) | Opaque | Missing | Wildcard => {
-                pcx.cx.bug(&format!("found unexpected ctor in all_ctors: {:?}", self))
+                never!("found unexpected ctor in all_ctors: {:?}", self);
+                true
             }
         }
     }
@@ -683,7 +689,8 @@ pub(crate) fn wildcards(pcx: PatCtxt<'_>, constructor: &Constructor) -> Self {
                     }
                 }
                 ty_kind => {
-                    cx.bug(&format!("Unexpected type for `Single` constructor: {:?}", ty_kind))
+                    never!("Unexpected type for `Single` constructor: {:?}", ty_kind);
+                    Fields::from_single_pattern(wildcard_from_ty(ty))
                 }
             },
             Slice(..) => {
@@ -745,7 +752,8 @@ pub(super) fn apply(self, pcx: PatCtxt<'_>, ctor: &Constructor) -> Pat {
                 // can ignore this issue.
                 TyKind::Ref(..) => PatKind::Deref { subpattern: subpatterns.next().unwrap() },
                 TyKind::Slice(..) | TyKind::Array(..) => {
-                    pcx.cx.bug(&format!("bad slice pattern {:?} {:?}", ctor, pcx.ty))
+                    never!("bad slice pattern {:?} {:?}", ctor, pcx.ty);
+                    PatKind::Wild
                 }
                 _ => PatKind::Wild,
             },
@@ -755,11 +763,17 @@ pub(super) fn apply(self, pcx: PatCtxt<'_>, ctor: &Constructor) -> Pat {
             Constructor::IntRange(_) => UNHANDLED,
             NonExhaustive => PatKind::Wild,
             Wildcard => return Pat::wildcard_from_ty(pcx.ty.clone()),
-            Opaque => pcx.cx.bug("we should not try to apply an opaque constructor"),
-            Missing => pcx.cx.bug(
-                "trying to apply the `Missing` constructor;\
-                this should have been done in `apply_constructors`",
-            ),
+            Opaque => {
+                never!("we should not try to apply an opaque constructor");
+                PatKind::Wild
+            }
+            Missing => {
+                never!(
+                    "trying to apply the `Missing` constructor; \
+                    this should have been done in `apply_constructors`",
+                );
+                PatKind::Wild
+            }
         };
 
         Pat { ty: pcx.ty.clone(), kind: Box::new(pat) }
index b5d238116ffabc2c6d22e51de7ff6667e5d387a6..bb072ae70ad73a9b3b7d8d31426b4d257a6bc2aa 100644 (file)
@@ -295,7 +295,6 @@ pub(crate) struct MatchCheckCtx<'a> {
     pub(crate) db: &'a dyn HirDatabase,
     /// Lowered patterns from arms plus generated by the check.
     pub(crate) pattern_arena: &'a RefCell<PatternArena>,
-    pub(crate) panic_context: &'a dyn Fn() -> String,
 }
 
 impl<'a> MatchCheckCtx<'a> {
@@ -328,11 +327,6 @@ pub(super) fn alloc_pat(&self, pat: Pat) -> PatId {
     pub(super) fn type_of(&self, pat: PatId) -> Ty {
         self.pattern_arena.borrow()[pat].ty.clone()
     }
-
-    #[track_caller]
-    pub(super) fn bug(&self, info: &str) -> ! {
-        panic!("bug: {}\n{}", info, (self.panic_context)());
-    }
 }
 
 #[derive(Copy, Clone)]
@@ -1131,7 +1125,7 @@ pub(crate) fn compute_match_usefulness(
     arms: &[MatchArm],
 ) -> UsefulnessReport {
     let mut matrix = Matrix::empty();
-    let arm_usefulness: Vec<_> = arms
+    let arm_usefulness = arms
         .iter()
         .copied()
         .map(|arm| {
index 0f5859c433f2bf50ef1f51d8205cd6ce37e6535a..4d7ac6fd85fc7fc1a220057936e2b85fa80adda0 100644 (file)
@@ -277,7 +277,7 @@ fn coerce_ref(&mut self, from_ty: Ty, to_ty: &Ty, to_mt: Mutability) -> CoerceRe
                 continue;
             }
 
-            let referent_ty = canonicalized.decanonicalize_ty(referent_ty.value);
+            let referent_ty = canonicalized.decanonicalize_ty(&mut self.table, referent_ty);
 
             // At this point, we have deref'd `a` to `referent_ty`.  So
             // imagine we are coercing from `&'a mut Vec<T>` to `&'b mut [T]`.
index ea71cdd8102229076bf1e185f6b4780620619897..b0306d9148ae8c1f4e5a7793d0afe5d5134c1301 100644 (file)
@@ -328,10 +328,8 @@ fn infer_expr_inner(&mut self, tgt_expr: ExprId, expected: &Expectation) -> Ty {
                     },
                 );
                 let res = derefs.by_ref().find_map(|(callee_deref_ty, _)| {
-                    self.callable_sig(
-                        &canonicalized.decanonicalize_ty(callee_deref_ty.value),
-                        args.len(),
-                    )
+                    let ty = &canonicalized.decanonicalize_ty(&mut self.table, callee_deref_ty);
+                    self.callable_sig(ty, args.len())
                 });
                 let (param_tys, ret_ty): (Vec<Ty>, Ty) = match res {
                     Some(res) => {
@@ -510,17 +508,20 @@ fn infer_expr_inner(&mut self, tgt_expr: ExprId, expected: &Expectation) -> Ty {
                     },
                 );
                 let ty = autoderef.by_ref().find_map(|(derefed_ty, _)| {
-                    let def_db = self.db.upcast();
                     let module = self.resolver.module();
+                    let db = self.db;
                     let is_visible = |field_id: &FieldId| {
                         module
                             .map(|mod_id| {
-                                self.db.field_visibilities(field_id.parent)[field_id.local_id]
-                                    .is_visible_from(def_db, mod_id)
+                                db.field_visibilities(field_id.parent)[field_id.local_id]
+                                    .is_visible_from(db.upcast(), mod_id)
                             })
                             .unwrap_or(true)
                     };
-                    match canonicalized.decanonicalize_ty(derefed_ty.value).kind(&Interner) {
+                    match canonicalized
+                        .decanonicalize_ty(&mut self.table, derefed_ty)
+                        .kind(&Interner)
+                    {
                         TyKind::Tuple(_, substs) => name.as_tuple_index().and_then(|idx| {
                             substs
                                 .as_slice(&Interner)
@@ -637,7 +638,7 @@ fn infer_expr_inner(&mut self, tgt_expr: ExprId, expected: &Expectation) -> Ty {
                                 },
                             ) {
                                 Some(derefed_ty) => {
-                                    canonicalized.decanonicalize_ty(derefed_ty.value)
+                                    canonicalized.decanonicalize_ty(&mut self.table, derefed_ty)
                                 }
                                 None => self.err_ty(),
                             }
@@ -740,8 +741,9 @@ fn infer_expr_inner(&mut self, tgt_expr: ExprId, expected: &Expectation) -> Ty {
                         krate,
                         index_trait,
                     );
-                    let self_ty =
-                        self_ty.map_or(self.err_ty(), |t| canonicalized.decanonicalize_ty(t.value));
+                    let self_ty = self_ty.map_or(self.err_ty(), |t| {
+                        canonicalized.decanonicalize_ty(&mut self.table, t)
+                    });
                     self.resolve_associated_type_with_params(
                         self_ty,
                         self.resolve_ops_index_output(),
@@ -987,7 +989,7 @@ fn infer_method_call(
         });
         let (receiver_ty, method_ty, substs) = match resolved {
             Some((ty, func)) => {
-                let ty = canonicalized_receiver.decanonicalize_ty(ty);
+                let ty = canonicalized_receiver.decanonicalize_ty(&mut self.table, ty);
                 let generics = generics(self.db.upcast(), func.into());
                 let substs = self.substs_for_method_call(generics, generic_args, &ty);
                 self.write_method_resolution(tgt_expr, func, substs.clone());
index 9f1253825ef4b4412e30fa45a6b2f5fd12203c85..4c1e758904ec62c5aa07c3f638683baa20ad3c42 100644 (file)
@@ -41,8 +41,13 @@ pub(super) struct Canonicalized<T>
 }
 
 impl<T: HasInterner<Interner = Interner>> Canonicalized<T> {
-    pub(super) fn decanonicalize_ty(&self, ty: Ty) -> Ty {
-        chalk_ir::Substitute::apply(&self.free_vars, ty, &Interner)
+    /// this method is wrong and shouldn't exist
+    pub(super) fn decanonicalize_ty(&self, table: &mut InferenceTable, ty: Canonical<Ty>) -> Ty {
+        let mut vars = self.free_vars.clone();
+        while ty.binders.len(&Interner) > vars.len() {
+            vars.push(table.new_type_var().cast(&Interner));
+        }
+        chalk_ir::Substitute::apply(&vars, ty.value, &Interner)
     }
 
     pub(super) fn apply_solution(
index ad45a293468b808a40e2764562a2d419fad187be..3734eb1013106e5db1d5f65a3210c17f8d1de7f8 100644 (file)
@@ -307,17 +307,12 @@ pub fn lower_ty_ext(&self, type_ref: &TypeRef) -> (Ty, Option<TypeNs>) {
                     let mut expander = self.expander.borrow_mut();
                     if expander.is_some() {
                         (Some(expander), false)
+                    } else if let Some(module_id) = self.resolver.module() {
+                        *expander =
+                            Some(Expander::new(self.db.upcast(), macro_call.file_id, module_id));
+                        (Some(expander), true)
                     } else {
-                        if let Some(module_id) = self.resolver.module() {
-                            *expander = Some(Expander::new(
-                                self.db.upcast(),
-                                macro_call.file_id,
-                                module_id,
-                            ));
-                            (Some(expander), true)
-                        } else {
-                            (None, false)
-                        }
+                        (None, false)
                     }
                 };
                 let ty = if let Some(mut expander) = expander {
@@ -410,52 +405,60 @@ pub(crate) fn lower_partly_resolved_path(
     ) -> (Ty, Option<TypeNs>) {
         let ty = match resolution {
             TypeNs::TraitId(trait_) => {
-                let ty = if remaining_segments.len() == 1 {
-                    let trait_ref =
-                        self.lower_trait_ref_from_resolved_path(trait_, resolved_segment, None);
-                    let segment = remaining_segments.first().unwrap();
-                    let found = self
-                        .db
-                        .trait_data(trait_ref.hir_trait_id())
-                        .associated_type_by_name(segment.name);
-                    match found {
-                        Some(associated_ty) => {
-                            // FIXME handle type parameters on the segment
-                            TyKind::Alias(AliasTy::Projection(ProjectionTy {
-                                associated_ty_id: to_assoc_type_id(associated_ty),
-                                substitution: trait_ref.substitution,
-                            }))
-                            .intern(&Interner)
-                        }
-                        None => {
-                            // FIXME: report error (associated type not found)
-                            TyKind::Error.intern(&Interner)
+                let ty = match remaining_segments.len() {
+                    1 => {
+                        let trait_ref =
+                            self.lower_trait_ref_from_resolved_path(trait_, resolved_segment, None);
+                        let segment = remaining_segments.first().unwrap();
+                        let found = self
+                            .db
+                            .trait_data(trait_ref.hir_trait_id())
+                            .associated_type_by_name(segment.name);
+                        match found {
+                            Some(associated_ty) => {
+                                // FIXME handle type parameters on the segment
+                                TyKind::Alias(AliasTy::Projection(ProjectionTy {
+                                    associated_ty_id: to_assoc_type_id(associated_ty),
+                                    substitution: trait_ref.substitution,
+                                }))
+                                .intern(&Interner)
+                            }
+                            None => {
+                                // FIXME: report error (associated type not found)
+                                TyKind::Error.intern(&Interner)
+                            }
                         }
                     }
-                } else if remaining_segments.len() > 1 {
-                    // FIXME report error (ambiguous associated type)
-                    TyKind::Error.intern(&Interner)
-                } else {
-                    let self_ty = Some(
-                        TyKind::BoundVar(BoundVar::new(DebruijnIndex::INNERMOST, 0))
-                            .intern(&Interner),
-                    );
-                    let trait_ref = self.with_shifted_in(DebruijnIndex::ONE, |ctx| {
-                        ctx.lower_trait_ref_from_resolved_path(trait_, resolved_segment, self_ty)
-                    });
-                    let dyn_ty = DynTy {
-                        bounds: crate::make_only_type_binders(
-                            1,
-                            QuantifiedWhereClauses::from_iter(
-                                &Interner,
-                                Some(crate::wrap_empty_binders(WhereClause::Implemented(
-                                    trait_ref,
-                                ))),
+                    0 => {
+                        let self_ty = Some(
+                            TyKind::BoundVar(BoundVar::new(DebruijnIndex::INNERMOST, 0))
+                                .intern(&Interner),
+                        );
+                        let trait_ref = self.with_shifted_in(DebruijnIndex::ONE, |ctx| {
+                            ctx.lower_trait_ref_from_resolved_path(
+                                trait_,
+                                resolved_segment,
+                                self_ty,
+                            )
+                        });
+                        let dyn_ty = DynTy {
+                            bounds: crate::make_only_type_binders(
+                                1,
+                                QuantifiedWhereClauses::from_iter(
+                                    &Interner,
+                                    Some(crate::wrap_empty_binders(WhereClause::Implemented(
+                                        trait_ref,
+                                    ))),
+                                ),
                             ),
-                        ),
-                        lifetime: static_lifetime(),
-                    };
-                    TyKind::Dyn(dyn_ty).intern(&Interner)
+                            lifetime: static_lifetime(),
+                        };
+                        TyKind::Dyn(dyn_ty).intern(&Interner)
+                    }
+                    _ => {
+                        // FIXME report error (ambiguous associated type)
+                        TyKind::Error.intern(&Interner)
+                    }
                 };
                 return (ty, None);
             }
@@ -1024,7 +1027,7 @@ pub(crate) fn generic_predicates_for_param_query(
     let ctx =
         TyLoweringContext::new(db, &resolver).with_type_param_mode(TypeParamLoweringMode::Variable);
     let generics = generics(db.upcast(), param_id.parent);
-    resolver
+    let mut predicates: Vec<_> = resolver
         .where_predicates_in_scope()
         // we have to filter out all other predicates *first*, before attempting to lower them
         .filter(|pred| match pred {
@@ -1038,7 +1041,15 @@ pub(crate) fn generic_predicates_for_param_query(
             WherePredicate::Lifetime { .. } => false,
         })
         .flat_map(|pred| ctx.lower_where_predicate(pred, true).map(|p| make_binders(&generics, p)))
-        .collect()
+        .collect();
+
+    let subst = generics.bound_vars_subst(DebruijnIndex::INNERMOST);
+    let explicitly_unsized_tys = ctx.unsized_types.into_inner();
+    let implicitly_sized_predicates =
+        implicitly_sized_clauses(db, param_id.parent, &explicitly_unsized_tys, &subst, &resolver)
+            .map(|p| make_binders(&generics, crate::wrap_empty_binders(p)));
+    predicates.extend(implicitly_sized_predicates);
+    predicates.into()
 }
 
 pub(crate) fn generic_predicates_for_param_recover(
index f12ced24cc4328db00fb07dfd2baec5dbf22f093..c88a8b6535888e0a472056b21c70468376294165 100644 (file)
@@ -2,7 +2,7 @@
 //! For details about how this works in rustc, see the method lookup page in the
 //! [rustc guide](https://rust-lang.github.io/rustc-guide/method-lookup.html)
 //! and the corresponding code mostly in librustc_typeck/check/method/probe.rs.
-use std::{iter, sync::Arc};
+use std::{iter, ops::ControlFlow, sync::Arc};
 
 use arrayvec::ArrayVec;
 use base_db::{CrateId, Edition};
@@ -380,7 +380,7 @@ pub(crate) fn lookup_method(
     traits_in_scope: &FxHashSet<TraitId>,
     visible_from_module: Option<ModuleId>,
     name: &Name,
-) -> Option<(Ty, FunctionId)> {
+) -> Option<(Canonical<Ty>, FunctionId)> {
     iterate_method_candidates(
         ty,
         db,
@@ -421,7 +421,7 @@ pub fn iterate_method_candidates<T>(
     visible_from_module: Option<ModuleId>,
     name: Option<&Name>,
     mode: LookupMode,
-    mut callback: impl FnMut(&Ty, AssocItemId) -> Option<T>,
+    mut callback: impl FnMut(&Canonical<Ty>, AssocItemId) -> Option<T>,
 ) -> Option<T> {
     let mut slot = None;
     iterate_method_candidates_dyn(
@@ -435,8 +435,11 @@ pub fn iterate_method_candidates<T>(
         mode,
         &mut |ty, item| {
             assert!(slot.is_none());
-            slot = callback(ty, item);
-            slot.is_some()
+            if let Some(it) = callback(ty, item) {
+                slot = Some(it);
+                return ControlFlow::Break(());
+            }
+            ControlFlow::Continue(())
         },
     );
     slot
@@ -451,8 +454,8 @@ pub fn iterate_method_candidates_dyn(
     visible_from_module: Option<ModuleId>,
     name: Option<&Name>,
     mode: LookupMode,
-    callback: &mut dyn FnMut(&Ty, AssocItemId) -> bool,
-) -> bool {
+    callback: &mut dyn FnMut(&Canonical<Ty>, AssocItemId) -> ControlFlow<()>,
+) -> ControlFlow<()> {
     match mode {
         LookupMode::MethodCall => {
             // For method calls, rust first does any number of autoderef, and then one
@@ -480,7 +483,7 @@ pub fn iterate_method_candidates_dyn(
 
             let deref_chain = autoderef_method_receiver(db, krate, ty);
             for i in 0..deref_chain.len() {
-                if iterate_method_candidates_with_autoref(
+                iterate_method_candidates_with_autoref(
                     &deref_chain[i..],
                     db,
                     env.clone(),
@@ -489,11 +492,9 @@ pub fn iterate_method_candidates_dyn(
                     visible_from_module,
                     name,
                     callback,
-                ) {
-                    return true;
-                }
+                )?;
             }
-            false
+            ControlFlow::Continue(())
         }
         LookupMode::Path => {
             // No autoderef for path lookups
@@ -519,9 +520,9 @@ fn iterate_method_candidates_with_autoref(
     traits_in_scope: &FxHashSet<TraitId>,
     visible_from_module: Option<ModuleId>,
     name: Option<&Name>,
-    mut callback: &mut dyn FnMut(&Ty, AssocItemId) -> bool,
-) -> bool {
-    if iterate_method_candidates_by_receiver(
+    mut callback: &mut dyn FnMut(&Canonical<Ty>, AssocItemId) -> ControlFlow<()>,
+) -> ControlFlow<()> {
+    iterate_method_candidates_by_receiver(
         &deref_chain[0],
         &deref_chain[1..],
         db,
@@ -531,15 +532,15 @@ fn iterate_method_candidates_with_autoref(
         visible_from_module,
         name,
         &mut callback,
-    ) {
-        return true;
-    }
+    )?;
+
     let refed = Canonical {
         binders: deref_chain[0].binders.clone(),
         value: TyKind::Ref(Mutability::Not, static_lifetime(), deref_chain[0].value.clone())
             .intern(&Interner),
     };
-    if iterate_method_candidates_by_receiver(
+
+    iterate_method_candidates_by_receiver(
         &refed,
         deref_chain,
         db,
@@ -549,15 +550,15 @@ fn iterate_method_candidates_with_autoref(
         visible_from_module,
         name,
         &mut callback,
-    ) {
-        return true;
-    }
+    )?;
+
     let ref_muted = Canonical {
         binders: deref_chain[0].binders.clone(),
         value: TyKind::Ref(Mutability::Mut, static_lifetime(), deref_chain[0].value.clone())
             .intern(&Interner),
     };
-    if iterate_method_candidates_by_receiver(
+
+    iterate_method_candidates_by_receiver(
         &ref_muted,
         deref_chain,
         db,
@@ -567,10 +568,7 @@ fn iterate_method_candidates_with_autoref(
         visible_from_module,
         name,
         &mut callback,
-    ) {
-        return true;
-    }
-    false
+    )
 }
 
 fn iterate_method_candidates_by_receiver(
@@ -582,13 +580,13 @@ fn iterate_method_candidates_by_receiver(
     traits_in_scope: &FxHashSet<TraitId>,
     visible_from_module: Option<ModuleId>,
     name: Option<&Name>,
-    mut callback: &mut dyn FnMut(&Ty, AssocItemId) -> bool,
-) -> bool {
+    mut callback: &mut dyn FnMut(&Canonical<Ty>, AssocItemId) -> ControlFlow<()>,
+) -> ControlFlow<()> {
     // We're looking for methods with *receiver* type receiver_ty. These could
     // be found in any of the derefs of receiver_ty, so we have to go through
     // that.
     for self_ty in std::iter::once(receiver_ty).chain(rest_of_deref_chain) {
-        if iterate_inherent_methods(
+        iterate_inherent_methods(
             self_ty,
             db,
             env.clone(),
@@ -597,12 +595,11 @@ fn iterate_method_candidates_by_receiver(
             krate,
             visible_from_module,
             &mut callback,
-        ) {
-            return true;
-        }
+        )?
     }
+
     for self_ty in std::iter::once(receiver_ty).chain(rest_of_deref_chain) {
-        if iterate_trait_method_candidates(
+        iterate_trait_method_candidates(
             self_ty,
             db,
             env.clone(),
@@ -611,11 +608,10 @@ fn iterate_method_candidates_by_receiver(
             name,
             Some(receiver_ty),
             &mut callback,
-        ) {
-            return true;
-        }
+        )?
     }
-    false
+
+    ControlFlow::Continue(())
 }
 
 fn iterate_method_candidates_for_self_ty(
@@ -626,9 +622,9 @@ fn iterate_method_candidates_for_self_ty(
     traits_in_scope: &FxHashSet<TraitId>,
     visible_from_module: Option<ModuleId>,
     name: Option<&Name>,
-    mut callback: &mut dyn FnMut(&Ty, AssocItemId) -> bool,
-) -> bool {
-    if iterate_inherent_methods(
+    mut callback: &mut dyn FnMut(&Canonical<Ty>, AssocItemId) -> ControlFlow<()>,
+) -> ControlFlow<()> {
+    iterate_inherent_methods(
         self_ty,
         db,
         env.clone(),
@@ -637,9 +633,7 @@ fn iterate_method_candidates_for_self_ty(
         krate,
         visible_from_module,
         &mut callback,
-    ) {
-        return true;
-    }
+    )?;
     iterate_trait_method_candidates(self_ty, db, env, krate, traits_in_scope, name, None, callback)
 }
 
@@ -651,22 +645,24 @@ fn iterate_trait_method_candidates(
     traits_in_scope: &FxHashSet<TraitId>,
     name: Option<&Name>,
     receiver_ty: Option<&Canonical<Ty>>,
-    callback: &mut dyn FnMut(&Ty, AssocItemId) -> bool,
-) -> bool {
+    callback: &mut dyn FnMut(&Canonical<Ty>, AssocItemId) -> ControlFlow<()>,
+) -> ControlFlow<()> {
     let receiver_is_array = matches!(self_ty.value.kind(&Interner), chalk_ir::TyKind::Array(..));
     // if ty is `dyn Trait`, the trait doesn't need to be in scope
     let inherent_trait =
         self_ty.value.dyn_trait().into_iter().flat_map(|t| all_super_traits(db.upcast(), t));
-    let env_traits = if let TyKind::Placeholder(_) = self_ty.value.kind(&Interner) {
-        // if we have `T: Trait` in the param env, the trait doesn't need to be in scope
-        env.traits_in_scope_from_clauses(&self_ty.value)
-            .flat_map(|t| all_super_traits(db.upcast(), t))
-            .collect()
-    } else {
-        Vec::new()
+    let env_traits = match self_ty.value.kind(&Interner) {
+        TyKind::Placeholder(_) => {
+            // if we have `T: Trait` in the param env, the trait doesn't need to be in scope
+            env.traits_in_scope_from_clauses(&self_ty.value)
+                .flat_map(|t| all_super_traits(db.upcast(), t))
+                .collect()
+        }
+        _ => Vec::new(),
     };
     let traits =
         inherent_trait.chain(env_traits.into_iter()).chain(traits_in_scope.iter().copied());
+
     'traits: for t in traits {
         let data = db.trait_data(t);
 
@@ -701,12 +697,10 @@ fn iterate_trait_method_candidates(
             }
             known_implemented = true;
             // FIXME: we shouldn't be ignoring the binders here
-            if callback(&self_ty.value, *item) {
-                return true;
-            }
+            callback(self_ty, *item)?
         }
     }
-    false
+    ControlFlow::Continue(())
 }
 
 fn filter_inherent_impls_for_self_ty<'i>(
@@ -744,12 +738,13 @@ fn iterate_inherent_methods(
     receiver_ty: Option<&Canonical<Ty>>,
     krate: CrateId,
     visible_from_module: Option<ModuleId>,
-    callback: &mut dyn FnMut(&Ty, AssocItemId) -> bool,
-) -> bool {
+    callback: &mut dyn FnMut(&Canonical<Ty>, AssocItemId) -> ControlFlow<()>,
+) -> ControlFlow<()> {
     let def_crates = match def_crates(db, &self_ty.value, krate) {
         Some(k) => k,
-        None => return false,
+        None => return ControlFlow::Continue(()),
     };
+
     for krate in def_crates {
         let impls = db.inherent_impls_in_crate(krate);
 
@@ -778,14 +773,12 @@ fn iterate_inherent_methods(
                     cov_mark::hit!(impl_self_type_match_without_receiver);
                     continue;
                 }
-                let receiver_ty = receiver_ty.map(|x| &x.value).unwrap_or(&self_ty.value);
-                if callback(receiver_ty, item) {
-                    return true;
-                }
+                let receiver_ty = receiver_ty.unwrap_or(self_ty);
+                callback(receiver_ty, item)?;
             }
         }
     }
-    false
+    ControlFlow::Continue(())
 }
 
 /// Returns the self type for the index trait call.
index 70a1c37dc077d55e53434b5aa7e31f4d6cc480ac..618499fdc191ccc9b57da0783c57c9c34fd58da8 100644 (file)
@@ -1145,3 +1145,35 @@ pub fn new(out: T, metadata_lookup: &'a DB::MetadataLookup) -> Self {
         "#,
     );
 }
+
+#[test]
+fn bitslice_panic() {
+    check_no_mismatches(
+        r#"
+//- minicore: option, deref
+
+pub trait BitView {
+    type Store;
+}
+
+pub struct Lsb0;
+
+pub struct BitArray<V: BitView> { }
+
+pub struct BitSlice<T> { }
+
+impl<V: BitView> core::ops::Deref for BitArray<V> {
+    type Target = BitSlice<V::Store>;
+}
+
+impl<T> BitSlice<T> {
+    pub fn split_first(&self) -> Option<(T, &Self)> { loop {} }
+}
+
+fn multiexp_inner() {
+    let exp: &BitArray<Foo>;
+    exp.split_first();
+}
+        "#,
+    );
+}
index c04a2b4362ca2dc4140baa681e6618d8502ab1b6..242b7b40e18916e6d1d9c4118778e5ef6a9d0ce3 100644 (file)
@@ -3,12 +3,16 @@
 use indexmap::IndexMap;
 
 use hir::Semantics;
-use ide_db::{call_info::FnCallNode, RootDatabase};
-use syntax::{ast, AstNode, TextRange};
-
-use crate::{
-    display::TryToNav, goto_definition, references, FilePosition, NavigationTarget, RangeInfo,
+use ide_db::{
+    call_info::FnCallNode,
+    defs::{Definition, NameClass, NameRefClass},
+    helpers::pick_best_token,
+    search::FileReference,
+    RootDatabase,
 };
+use syntax::{ast, AstNode, SyntaxKind::NAME, TextRange};
+
+use crate::{display::TryToNav, goto_definition, FilePosition, NavigationTarget, RangeInfo};
 
 #[derive(Debug, Clone)]
 pub struct CallItem {
@@ -17,12 +21,6 @@ pub struct CallItem {
 }
 
 impl CallItem {
-    #[cfg(test)]
-    pub(crate) fn assert_match(&self, expected: &str) {
-        let actual = self.debug_render();
-        test_utils::assert_eq_text!(expected.trim(), actual.trim(),);
-    }
-
     #[cfg(test)]
     pub(crate) fn debug_render(&self) -> String {
         format!("{} : {:?}", self.target.debug_render(), self.ranges)
@@ -36,30 +34,45 @@ pub(crate) fn call_hierarchy(
     goto_definition::goto_definition(db, position)
 }
 
-pub(crate) fn incoming_calls(db: &RootDatabase, position: FilePosition) -> Option<Vec<CallItem>> {
-    let sema = Semantics::new(db);
-
-    // 1. Find all refs
-    // 2. Loop through refs and determine unique fndef. This will become our `from: CallHierarchyItem,` in the reply.
-    // 3. Add ranges relative to the start of the fndef.
-    let refs = references::find_all_refs(&sema, position, None)?;
+pub(crate) fn incoming_calls(
+    db: &RootDatabase,
+    FilePosition { file_id, offset }: FilePosition,
+) -> Option<Vec<CallItem>> {
+    let sema = &Semantics::new(db);
 
+    let file = sema.parse(file_id);
+    let file = file.syntax();
     let mut calls = CallLocations::default();
 
-    for (file_id, references) in refs.into_iter().flat_map(|refs| refs.references) {
-        let file = sema.parse(file_id);
-        let file = file.syntax();
-        for (relative_range, token) in references
-            .into_iter()
-            .filter_map(|(range, _)| Some(range).zip(file.token_at_offset(range.start()).next()))
-        {
-            let token = sema.descend_into_macros(token);
+    let references = sema
+        .find_nodes_at_offset_with_descend(file, offset)
+        .filter_map(move |node| match node {
+            ast::NameLike::NameRef(name_ref) => match NameRefClass::classify(sema, &name_ref)? {
+                NameRefClass::Definition(
+                    def @ Definition::ModuleDef(hir::ModuleDef::Function(_)),
+                ) => Some(def),
+                _ => None,
+            },
+            ast::NameLike::Name(name) => match NameClass::classify(sema, &name)? {
+                NameClass::Definition(def @ Definition::ModuleDef(hir::ModuleDef::Function(_))) => {
+                    Some(def)
+                }
+                _ => None,
+            },
+            ast::NameLike::Lifetime(_) => None,
+        })
+        .flat_map(|func| func.usages(sema).all());
+
+    for (_, references) in references {
+        let references = references.into_iter().map(|FileReference { name, .. }| name);
+        for name in references {
             // This target is the containing function
-            if let Some(nav) = token.ancestors().find_map(|node| {
+            let nav = sema.ancestors_with_macros(name.syntax().clone()).find_map(|node| {
                 let def = ast::Fn::cast(node).and_then(|fn_| sema.to_def(&fn_))?;
                 def.try_to_nav(sema.db)
-            }) {
-                calls.add(&nav, relative_range);
+            });
+            if let Some(nav) = nav {
+                calls.add(nav, sema.original_range(name.syntax()).range);
             }
         }
     }
@@ -72,15 +85,22 @@ pub(crate) fn outgoing_calls(db: &RootDatabase, position: FilePosition) -> Optio
     let file_id = position.file_id;
     let file = sema.parse(file_id);
     let file = file.syntax();
-    let token = file.token_at_offset(position.offset).next()?;
-    let token = sema.descend_into_macros(token);
-
+    let token = pick_best_token(file.token_at_offset(position.offset), |kind| match kind {
+        NAME => 1,
+        _ => 0,
+    })?;
     let mut calls = CallLocations::default();
 
-    token
-        .parent()
+    sema.descend_into_macros_many(token)
         .into_iter()
-        .flat_map(|it| it.descendants())
+        .filter_map(|it| it.ancestors().nth(1).and_then(ast::Item::cast))
+        .filter_map(|item| match item {
+            ast::Item::Const(c) => c.body().map(|it| it.syntax().descendants()),
+            ast::Item::Fn(f) => f.body().map(|it| it.syntax().descendants()),
+            ast::Item::Static(s) => s.body().map(|it| it.syntax().descendants()),
+            _ => None,
+        })
+        .flatten()
         .filter_map(|node| FnCallNode::with_node_exact(&node))
         .filter_map(|call_node| {
             let name_ref = call_node.name_ref()?;
@@ -99,7 +119,7 @@ pub(crate) fn outgoing_calls(db: &RootDatabase, position: FilePosition) -> Optio
             }?;
             Some((func_target, name_ref.syntax().text_range()))
         })
-        .for_each(|(nav, range)| calls.add(&nav, range));
+        .for_each(|(nav, range)| calls.add(nav, range));
 
     Some(calls.into_items())
 }
@@ -110,8 +130,8 @@ struct CallLocations {
 }
 
 impl CallLocations {
-    fn add(&mut self, target: &NavigationTarget, range: TextRange) {
-        self.funcs.entry(target.clone()).or_default().push(range);
+    fn add(&mut self, target: NavigationTarget, range: TextRange) {
+        self.funcs.entry(target).or_default().push(range);
     }
 
     fn into_items(self) -> Vec<CallItem> {
@@ -121,38 +141,34 @@ fn into_items(self) -> Vec<CallItem> {
 
 #[cfg(test)]
 mod tests {
+    use expect_test::{expect, Expect};
     use ide_db::base_db::FilePosition;
+    use itertools::Itertools;
 
     use crate::fixture;
 
     fn check_hierarchy(
         ra_fixture: &str,
-        expected: &str,
-        expected_incoming: &[&str],
-        expected_outgoing: &[&str],
+        expected: Expect,
+        expected_incoming: Expect,
+        expected_outgoing: Expect,
     ) {
         let (analysis, pos) = fixture::position(ra_fixture);
 
         let mut navs = analysis.call_hierarchy(pos).unwrap().unwrap().info;
         assert_eq!(navs.len(), 1);
         let nav = navs.pop().unwrap();
-        nav.assert_match(expected);
+        expected.assert_eq(&nav.debug_render());
 
         let item_pos =
             FilePosition { file_id: nav.file_id, offset: nav.focus_or_full_range().start() };
         let incoming_calls = analysis.incoming_calls(item_pos).unwrap().unwrap();
-        assert_eq!(incoming_calls.len(), expected_incoming.len());
-
-        for call in 0..incoming_calls.len() {
-            incoming_calls[call].assert_match(expected_incoming[call]);
-        }
+        expected_incoming
+            .assert_eq(&incoming_calls.into_iter().map(|call| call.debug_render()).join("\n"));
 
         let outgoing_calls = analysis.outgoing_calls(item_pos).unwrap().unwrap();
-        assert_eq!(outgoing_calls.len(), expected_outgoing.len());
-
-        for call in 0..outgoing_calls.len() {
-            outgoing_calls[call].assert_match(expected_outgoing[call]);
-        }
+        expected_outgoing
+            .assert_eq(&outgoing_calls.into_iter().map(|call| call.debug_render()).join("\n"));
     }
 
     #[test]
@@ -165,9 +181,9 @@ fn caller() {
     call$0ee();
 }
 "#,
-            "callee Function FileId(0) 0..14 3..9",
-            &["caller Function FileId(0) 15..44 18..24 : [33..39]"],
-            &[],
+            expect![["callee Function FileId(0) 0..14 3..9"]],
+            expect![["caller Function FileId(0) 15..44 18..24 : [33..39]"]],
+            expect![[]],
         );
     }
 
@@ -181,9 +197,9 @@ fn caller() {
     callee();
 }
 "#,
-            "callee Function FileId(0) 0..14 3..9",
-            &["caller Function FileId(0) 15..44 18..24 : [33..39]"],
-            &[],
+            expect![["callee Function FileId(0) 0..14 3..9"]],
+            expect![["caller Function FileId(0) 15..44 18..24 : [33..39]"]],
+            expect![[]],
         );
     }
 
@@ -198,9 +214,9 @@ fn caller() {
     callee();
 }
 "#,
-            "callee Function FileId(0) 0..14 3..9",
-            &["caller Function FileId(0) 15..58 18..24 : [33..39, 47..53]"],
-            &[],
+            expect![["callee Function FileId(0) 0..14 3..9"]],
+            expect![["caller Function FileId(0) 15..58 18..24 : [33..39, 47..53]"]],
+            expect![[]],
         );
     }
 
@@ -218,12 +234,11 @@ fn caller2() {
     callee();
 }
 "#,
-            "callee Function FileId(0) 0..14 3..9",
-            &[
-                "caller1 Function FileId(0) 15..45 18..25 : [34..40]",
-                "caller2 Function FileId(0) 47..77 50..57 : [66..72]",
-            ],
-            &[],
+            expect![["callee Function FileId(0) 0..14 3..9"]],
+            expect![["
+                caller1 Function FileId(0) 15..45 18..25 : [34..40]
+                caller2 Function FileId(0) 47..77 50..57 : [66..72]"]],
+            expect![[]],
         );
     }
 
@@ -247,12 +262,11 @@ fn test_caller() {
     }
 }
 "#,
-            "callee Function FileId(0) 0..14 3..9",
-            &[
-                "caller1 Function FileId(0) 15..45 18..25 : [34..40]",
-                "test_caller Function FileId(0) 95..149 110..121 : [134..140]",
-            ],
-            &[],
+            expect![["callee Function FileId(0) 0..14 3..9"]],
+            expect![[r#"
+                caller1 Function FileId(0) 15..45 18..25 : [34..40]
+                test_caller Function FileId(0) 95..149 110..121 : [134..140]"#]],
+            expect![[]],
         );
     }
 
@@ -271,9 +285,9 @@ fn caller() {
 //- /foo/mod.rs
 pub fn callee() {}
 "#,
-            "callee Function FileId(1) 0..18 7..13",
-            &["caller Function FileId(0) 27..56 30..36 : [45..51]"],
-            &[],
+            expect![["callee Function FileId(1) 0..18 7..13"]],
+            expect![["caller Function FileId(0) 27..56 30..36 : [45..51]"]],
+            expect![[]],
         );
     }
 
@@ -288,9 +302,9 @@ fn call$0er() {
     callee();
 }
 "#,
-            "caller Function FileId(0) 15..58 18..24",
-            &[],
-            &["callee Function FileId(0) 0..14 3..9 : [33..39, 47..53]"],
+            expect![["caller Function FileId(0) 15..58 18..24"]],
+            expect![[]],
+            expect![["callee Function FileId(0) 0..14 3..9 : [33..39, 47..53]"]],
         );
     }
 
@@ -309,9 +323,9 @@ fn call$0er() {
 //- /foo/mod.rs
 pub fn callee() {}
 "#,
-            "caller Function FileId(0) 27..56 30..36",
-            &[],
-            &["callee Function FileId(1) 0..18 7..13 : [45..51]"],
+            expect![["caller Function FileId(0) 27..56 30..36"]],
+            expect![[]],
+            expect![["callee Function FileId(1) 0..18 7..13 : [45..51]"]],
         );
     }
 
@@ -332,9 +346,9 @@ fn caller3() {
 
 }
 "#,
-            "caller2 Function FileId(0) 33..64 36..43",
-            &["caller1 Function FileId(0) 0..31 3..10 : [19..26]"],
-            &["caller3 Function FileId(0) 66..83 69..76 : [52..59]"],
+            expect![["caller2 Function FileId(0) 33..64 36..43"]],
+            expect![["caller1 Function FileId(0) 0..31 3..10 : [19..26]"]],
+            expect![["caller3 Function FileId(0) 66..83 69..76 : [52..59]"]],
         );
     }
 
@@ -352,9 +366,9 @@ fn main() {
     a$0()
 }
 "#,
-            "a Function FileId(0) 0..18 3..4",
-            &["main Function FileId(0) 31..52 34..38 : [47..48]"],
-            &["b Function FileId(0) 20..29 23..24 : [13..14]"],
+            expect![["a Function FileId(0) 0..18 3..4"]],
+            expect![["main Function FileId(0) 31..52 34..38 : [47..48]"]],
+            expect![["b Function FileId(0) 20..29 23..24 : [13..14]"]],
         );
 
         check_hierarchy(
@@ -369,9 +383,81 @@ fn main() {
     a()
 }
 "#,
-            "b Function FileId(0) 20..29 23..24",
-            &["a Function FileId(0) 0..18 3..4 : [13..14]"],
-            &[],
+            expect![["b Function FileId(0) 20..29 23..24"]],
+            expect![["a Function FileId(0) 0..18 3..4 : [13..14]"]],
+            expect![[]],
+        );
+    }
+
+    #[test]
+    fn test_call_hierarchy_in_macros_incoming() {
+        check_hierarchy(
+            r#"
+macro_rules! define {
+    ($ident:ident) => {
+        fn $ident {}
+    }
+}
+macro_rules! call {
+    ($ident:ident) => {
+        $ident()
+    }
+}
+define!(callee)
+fn caller() {
+    call!(call$0ee);
+}
+"#,
+            expect![[r#"callee Function FileId(0) 144..159 152..158"#]],
+            expect![[r#"caller Function FileId(0) 160..194 163..169 : [184..190]"#]],
+            expect![[]],
+        );
+        check_hierarchy(
+            r#"
+macro_rules! define {
+    ($ident:ident) => {
+        fn $ident {}
+    }
+}
+macro_rules! call {
+    ($ident:ident) => {
+        $ident()
+    }
+}
+define!(cal$0lee)
+fn caller() {
+    call!(callee);
+}
+"#,
+            expect![[r#"callee Function FileId(0) 144..159 152..158"#]],
+            expect![[r#"caller Function FileId(0) 160..194 163..169 : [184..190]"#]],
+            expect![[]],
+        );
+    }
+
+    #[test]
+    fn test_call_hierarchy_in_macros_outgoing() {
+        check_hierarchy(
+            r#"
+macro_rules! define {
+    ($ident:ident) => {
+        fn $ident {}
+    }
+}
+macro_rules! call {
+    ($ident:ident) => {
+        $ident()
+    }
+}
+define!(callee)
+fn caller$0() {
+    call!(callee);
+}
+"#,
+            expect![[r#"caller Function FileId(0) 160..194 163..169"#]],
+            expect![[]],
+            // FIXME
+            expect![[]],
         );
     }
 }
index 71e97d6b48ed523bb73db90a431a00933dd5e339..474db2e8ded42319a6b688de3bc5b624e0dce3f8 100644 (file)
@@ -2,7 +2,4 @@
 //! into types that may be used to render in a UI.
 
 pub(crate) mod navigation_target;
-
 pub(crate) use navigation_target::{ToNav, TryToNav};
-
-pub(crate) use syntax::display::macro_label;
index 4a0c0fcca9c21cc09ba221c2732b0cacc93e7570..6978e4b88ff7df4d53fb74c7b9e553a60e765389 100644 (file)
@@ -112,12 +112,6 @@ pub(crate) fn from_module_to_decl(db: &RootDatabase, module: hir::Module) -> Nav
         module.to_nav(db)
     }
 
-    #[cfg(test)]
-    pub(crate) fn assert_match(&self, expected: &str) {
-        let actual = self.debug_render();
-        test_utils::assert_eq_text!(expected.trim(), actual.trim(),);
-    }
-
     #[cfg(test)]
     pub(crate) fn debug_render(&self) -> String {
         let mut buf = format!(
index 36c13fe54ec40fdb5610219fcad90685fdebc0c1..adaaece719fee269928088dabff31b4866cfd626 100644 (file)
     helpers::pick_best_token,
     RootDatabase,
 };
-use syntax::{ast, match_ast, AstNode, SyntaxKind::*, SyntaxNode, TextRange, T};
+use syntax::{
+    ast::{self, IsString},
+    match_ast, AstNode, AstToken,
+    SyntaxKind::*,
+    SyntaxNode, SyntaxToken, TextRange, TextSize, T,
+};
 
 use crate::{
     doc_links::intra_doc_links::{parse_intra_doc_link, strip_prefixes_suffixes},
@@ -220,6 +225,66 @@ pub(crate) fn doc_attributes(
     }
 }
 
+pub(crate) struct DocCommentToken {
+    doc_token: SyntaxToken,
+    prefix_len: TextSize,
+}
+
+pub(crate) fn token_as_doc_comment(doc_token: &SyntaxToken) -> Option<DocCommentToken> {
+    (match_ast! {
+        match doc_token {
+            ast::Comment(comment) => TextSize::try_from(comment.prefix().len()).ok(),
+            ast::String(string) => doc_token.ancestors().find_map(ast::Attr::cast)
+                .filter(|attr| attr.simple_name().as_deref() == Some("doc")).and_then(|_| string.open_quote_text_range().map(|it| it.len())),
+            _ => None,
+        }
+    }).map(|prefix_len| DocCommentToken { prefix_len, doc_token: doc_token.clone() })
+}
+
+impl DocCommentToken {
+    pub(crate) fn get_definition_with_descend_at<T>(
+        self,
+        sema: &Semantics<RootDatabase>,
+        offset: TextSize,
+        // Definition, CommentOwner, range of intra doc link in original file
+        mut cb: impl FnMut(Definition, SyntaxNode, TextRange) -> Option<T>,
+    ) -> Option<T> {
+        let DocCommentToken { prefix_len, doc_token } = self;
+        // offset relative to the comments contents
+        let original_start = doc_token.text_range().start();
+        let relative_comment_offset = offset - original_start - prefix_len;
+
+        sema.descend_into_macros_many(doc_token.clone()).into_iter().find_map(|t| {
+            let (node, descended_prefix_len) = match_ast! {
+                match t {
+                    ast::Comment(comment) => (t.parent()?, TextSize::try_from(comment.prefix().len()).ok()?),
+                    ast::String(string) => (t.ancestors().skip_while(|n| n.kind() != ATTR).nth(1)?, string.open_quote_text_range()?.len()),
+                    _ => return None,
+                }
+            };
+            let token_start = t.text_range().start();
+            let abs_in_expansion_offset = token_start + relative_comment_offset + descended_prefix_len;
+
+            let (attributes, def) = doc_attributes(sema, &node)?;
+            let (docs, doc_mapping) = attributes.docs_with_rangemap(sema.db)?;
+            let (in_expansion_range, link, ns) =
+                extract_definitions_from_docs(&docs).into_iter().find_map(|(range, link, ns)| {
+                    let mapped = doc_mapping.map(range)?;
+                    (mapped.value.contains(abs_in_expansion_offset)).then(|| (mapped.value, link, ns))
+                })?;
+            // get the relative range to the doc/attribute in the expansion
+            let in_expansion_relative_range = in_expansion_range - descended_prefix_len - token_start;
+            // Apply relative range to the original input comment
+            let absolute_range = in_expansion_relative_range + original_start + prefix_len;
+            let def = match resolve_doc_path_for_def(sema.db, def, &link, ns)? {
+                Either::Left(it) => Definition::ModuleDef(it),
+                Either::Right(it) => Definition::Macro(it),
+            };
+            cb(def, node, absolute_range)
+        })
+    }
+}
+
 fn broken_link_clone_cb<'a, 'b>(link: BrokenLink<'a>) -> Option<(CowStr<'b>, CowStr<'b>)> {
     // These allocations are actually unnecessary but the lifetimes on BrokenLinkCallback are wrong
     // this is fixed in the repo but not on the crates.io release yet
index 8ec5e10c4df56fa565a7ede18d386c78fc738dc9..079f847030f54e6395e6ebb0caf0d802cf23dc32 100644 (file)
@@ -31,6 +31,7 @@ pub(crate) fn expand_macro(db: &RootDatabase, position: FilePosition) -> Option<
         SyntaxKind::IDENT => 1,
         _ => 0,
     })?;
+
     let descended = sema.descend_into_macros(tok.clone());
     if let Some(attr) = descended.ancestors().find_map(ast::Attr::cast) {
         if let Some((path, tt)) = attr.as_simple_call() {
@@ -45,6 +46,9 @@ pub(crate) fn expand_macro(db: &RootDatabase, position: FilePosition) -> Option<
             }
         }
     }
+
+    // FIXME: Intermix attribute and bang! expansions
+    // currently we only recursively expand one of the two types
     let mut expanded = None;
     let mut name = None;
     for node in tok.ancestors() {
index 35c219cadd94f4437be3bd9cf1f7740dbea73269..700f4dc95500a06dc6d8b438fd837f6a6e833a35 100644 (file)
@@ -1,4 +1,5 @@
 //! Utilities for creating `Analysis` instances for tests.
+use hir::db::DefDatabase;
 use ide_db::base_db::fixture::ChangeFixture;
 use test_utils::{extract_annotations, RangeOrOffset};
 
@@ -8,6 +9,7 @@
 pub(crate) fn file(ra_fixture: &str) -> (Analysis, FileId) {
     let mut host = AnalysisHost::default();
     let change_fixture = ChangeFixture::parse(ra_fixture);
+    host.db.set_enable_proc_attr_macros(true);
     host.db.apply_change(change_fixture.change);
     (host.analysis(), change_fixture.files[0])
 }
@@ -16,6 +18,7 @@ pub(crate) fn file(ra_fixture: &str) -> (Analysis, FileId) {
 pub(crate) fn position(ra_fixture: &str) -> (Analysis, FilePosition) {
     let mut host = AnalysisHost::default();
     let change_fixture = ChangeFixture::parse(ra_fixture);
+    host.db.set_enable_proc_attr_macros(true);
     host.db.apply_change(change_fixture.change);
     let (file_id, range_or_offset) = change_fixture.file_position.expect("expected a marker ($0)");
     let offset = range_or_offset.expect_offset();
@@ -26,6 +29,7 @@ pub(crate) fn position(ra_fixture: &str) -> (Analysis, FilePosition) {
 pub(crate) fn range(ra_fixture: &str) -> (Analysis, FileRange) {
     let mut host = AnalysisHost::default();
     let change_fixture = ChangeFixture::parse(ra_fixture);
+    host.db.set_enable_proc_attr_macros(true);
     host.db.apply_change(change_fixture.change);
     let (file_id, range_or_offset) = change_fixture.file_position.expect("expected a marker ($0)");
     let range = range_or_offset.expect_range();
@@ -36,6 +40,7 @@ pub(crate) fn range(ra_fixture: &str) -> (Analysis, FileRange) {
 pub(crate) fn range_or_position(ra_fixture: &str) -> (Analysis, FileId, RangeOrOffset) {
     let mut host = AnalysisHost::default();
     let change_fixture = ChangeFixture::parse(ra_fixture);
+    host.db.set_enable_proc_attr_macros(true);
     host.db.apply_change(change_fixture.change);
     let (file_id, range_or_offset) = change_fixture.file_position.expect("expected a marker ($0)");
     (host.analysis(), file_id, range_or_offset)
@@ -45,6 +50,7 @@ pub(crate) fn range_or_position(ra_fixture: &str) -> (Analysis, FileId, RangeOrO
 pub(crate) fn annotations(ra_fixture: &str) -> (Analysis, FilePosition, Vec<(FileRange, String)>) {
     let mut host = AnalysisHost::default();
     let change_fixture = ChangeFixture::parse(ra_fixture);
+    host.db.set_enable_proc_attr_macros(true);
     host.db.apply_change(change_fixture.change);
     let (file_id, range_or_offset) = change_fixture.file_position.expect("expected a marker ($0)");
     let offset = range_or_offset.expect_offset();
index ca22b10dfcb53edda387d21e13ebca94eb0568b8..911998d69b5b4e321c76bc51198960cbbf87afbb 100644 (file)
@@ -1,20 +1,17 @@
-use std::{convert::TryInto, iter};
+use std::convert::TryInto;
 
-use either::Either;
-use hir::{AsAssocItem, InFile, ModuleDef, Semantics};
+use crate::{
+    display::TryToNav, doc_links::token_as_doc_comment, FilePosition, NavigationTarget, RangeInfo,
+};
+use hir::{AsAssocItem, ModuleDef, Semantics};
 use ide_db::{
     base_db::{AnchoredPath, FileId, FileLoader},
-    defs::{Definition, NameClass, NameRefClass},
-    helpers::{pick_best_token, try_resolve_derive_input_at},
+    defs::Definition,
+    helpers::pick_best_token,
     RootDatabase,
 };
-use syntax::{ast, match_ast, AstNode, AstToken, SyntaxKind::*, SyntaxToken, TextRange, T};
-
-use crate::{
-    display::{ToNav, TryToNav},
-    doc_links::{doc_attributes, extract_definitions_from_docs, resolve_doc_path_for_def},
-    FilePosition, NavigationTarget, RangeInfo,
-};
+use itertools::Itertools;
+use syntax::{ast, AstNode, AstToken, SyntaxKind::*, SyntaxToken, TextRange, T};
 
 // Feature: Go to Definition
 //
@@ -31,7 +28,7 @@ pub(crate) fn goto_definition(
     db: &RootDatabase,
     position: FilePosition,
 ) -> Option<RangeInfo<Vec<NavigationTarget>>> {
-    let sema = Semantics::new(db);
+    let sema = &Semantics::new(db);
     let file = sema.parse(position.file_id).syntax().clone();
     let original_token =
         pick_best_token(file.token_at_offset(position.offset), |kind| match kind {
@@ -39,87 +36,66 @@ pub(crate) fn goto_definition(
             kind if kind.is_trivia() => 0,
             _ => 1,
         })?;
-    let token = sema.descend_into_macros(original_token.clone());
-    let parent = token.parent()?;
-    if let Some(_) = ast::Comment::cast(token.clone()) {
-        let (attributes, def) = doc_attributes(&sema, &parent)?;
-        let (docs, doc_mapping) = attributes.docs_with_rangemap(db)?;
-        let (_, link, ns) =
-            extract_definitions_from_docs(&docs).into_iter().find(|&(range, ..)| {
-                doc_mapping.map(range).map_or(false, |InFile { file_id, value: range }| {
-                    file_id == position.file_id.into() && range.contains(position.offset)
-                })
-            })?;
-        let nav = resolve_doc_path_for_def(db, def, &link, ns)?.try_to_nav(db)?;
-        return Some(RangeInfo::new(original_token.text_range(), vec![nav]));
-    }
-
-    let navs = match_ast! {
-        match parent {
-            ast::NameRef(name_ref) => {
-                reference_definition(&sema, Either::Right(&name_ref))
-            },
-            ast::Name(name) => {
-                match NameClass::classify(&sema, &name)? {
-                    NameClass::Definition(def) | NameClass::ConstReference(def) => {
-                        try_find_trait_item_definition(sema.db, &def).unwrap_or_else(|| def_to_nav(sema.db, def))
-                    }
-                    NameClass::PatFieldShorthand { local_def, field_ref } => {
-                        local_and_field_to_nav(sema.db, local_def, field_ref)
-                    },
-                }
-            },
-            ast::Lifetime(lt) => if let Some(name_class) = NameClass::classify_lifetime(&sema, &lt) {
-                match name_class {
-                    NameClass::Definition(def) => def_to_nav(sema.db, def),
-                    _ => return None,
+    if let Some(doc_comment) = token_as_doc_comment(&original_token) {
+        return doc_comment.get_definition_with_descend_at(sema, position.offset, |def, _, _| {
+            let nav = def.try_to_nav(db)?;
+            Some(RangeInfo::new(original_token.text_range(), vec![nav]))
+        });
+    }
+    let navs = sema
+        .descend_into_macros_many(original_token.clone())
+        .into_iter()
+        .filter_map(|token| {
+            let parent = token.parent()?;
+            if let Some(tt) = ast::TokenTree::cast(parent.clone()) {
+                if let x @ Some(_) =
+                    try_lookup_include_path(&sema, tt, token.clone(), position.file_id)
+                {
+                    return x;
                 }
-            } else {
-                reference_definition(&sema, Either::Left(&lt))
-            },
-            ast::TokenTree(tt) => try_lookup_include_path_or_derive(&sema, tt, token, position.file_id)?,
-            _ => return None,
-        }
-    };
+            }
+            Some(
+                Definition::from_token(&sema, &token)
+                    .into_iter()
+                    .flat_map(|def| {
+                        try_find_trait_item_definition(sema.db, &def)
+                            .unwrap_or_else(|| def_to_nav(sema.db, def))
+                    })
+                    .collect::<Vec<_>>(),
+            )
+        })
+        .flatten()
+        .unique()
+        .collect::<Vec<NavigationTarget>>();
 
     Some(RangeInfo::new(original_token.text_range(), navs))
 }
 
-fn try_lookup_include_path_or_derive(
+fn try_lookup_include_path(
     sema: &Semantics<RootDatabase>,
     tt: ast::TokenTree,
     token: SyntaxToken,
     file_id: FileId,
 ) -> Option<Vec<NavigationTarget>> {
-    match ast::String::cast(token.clone()) {
-        Some(token) => {
-            let path = token.value()?.into_owned();
-            let macro_call = tt.syntax().parent().and_then(ast::MacroCall::cast)?;
-            let name = macro_call.path()?.segment()?.name_ref()?;
-            if !matches!(&*name.text(), "include" | "include_str" | "include_bytes") {
-                return None;
-            }
-            let file_id = sema.db.resolve_path(AnchoredPath { anchor: file_id, path: &path })?;
-            let size = sema.db.file_text(file_id).len().try_into().ok()?;
-            Some(vec![NavigationTarget {
-                file_id,
-                full_range: TextRange::new(0.into(), size),
-                name: path.into(),
-                focus_range: None,
-                kind: None,
-                container_name: None,
-                description: None,
-                docs: None,
-            }])
-        }
-        None => try_resolve_derive_input_at(
-            sema,
-            &tt.syntax().ancestors().nth(2).and_then(ast::Attr::cast)?,
-            &token,
-        )
-        .and_then(|it| it.try_to_nav(sema.db))
-        .map(|it| vec![it]),
-    }
+    let token = ast::String::cast(token.clone())?;
+    let path = token.value()?.into_owned();
+    let macro_call = tt.syntax().parent().and_then(ast::MacroCall::cast)?;
+    let name = macro_call.path()?.segment()?.name_ref()?;
+    if !matches!(&*name.text(), "include" | "include_str" | "include_bytes") {
+        return None;
+    }
+    let file_id = sema.db.resolve_path(AnchoredPath { anchor: file_id, path: &path })?;
+    let size = sema.db.file_text(file_id).len().try_into().ok()?;
+    Some(vec![NavigationTarget {
+        file_id,
+        full_range: TextRange::new(0.into(), size),
+        name: path.into(),
+        focus_range: None,
+        kind: None,
+        container_name: None,
+        description: None,
+        docs: None,
+    }])
 }
 
 /// finds the trait definition of an impl'd item
@@ -154,37 +130,10 @@ fn try_find_trait_item_definition(
         .map(|it| vec![it])
 }
 
-pub(crate) fn reference_definition(
-    sema: &Semantics<RootDatabase>,
-    name_ref: Either<&ast::Lifetime, &ast::NameRef>,
-) -> Vec<NavigationTarget> {
-    let name_kind = match name_ref.either(
-        |lifetime| NameRefClass::classify_lifetime(sema, lifetime),
-        |name_ref| NameRefClass::classify(sema, name_ref),
-    ) {
-        Some(class) => class,
-        None => return Vec::new(),
-    };
-    match name_kind {
-        NameRefClass::Definition(def) => def_to_nav(sema.db, def),
-        NameRefClass::FieldShorthand { local_ref, field_ref } => {
-            local_and_field_to_nav(sema.db, local_ref, field_ref)
-        }
-    }
-}
-
 fn def_to_nav(db: &RootDatabase, def: Definition) -> Vec<NavigationTarget> {
     def.try_to_nav(db).map(|it| vec![it]).unwrap_or_default()
 }
 
-fn local_and_field_to_nav(
-    db: &RootDatabase,
-    local: hir::Local,
-    field: hir::Field,
-) -> Vec<NavigationTarget> {
-    iter::once(local.to_nav(db)).chain(field.try_to_nav(db)).collect()
-}
-
 #[cfg(test)]
 mod tests {
     use ide_db::base_db::FileRange;
@@ -192,6 +141,7 @@ mod tests {
 
     use crate::fixture;
 
+    #[track_caller]
     fn check(ra_fixture: &str) {
         let (analysis, position, expected) = fixture::annotations(ra_fixture);
         let navs = analysis.goto_definition(position).unwrap().expect("no definition found").info;
@@ -220,6 +170,29 @@ fn check_unresolved(ra_fixture: &str) {
         assert!(navs.is_empty(), "didn't expect this to resolve anywhere: {:?}", navs)
     }
 
+    #[test]
+    fn goto_def_in_mac_call_in_attr_invoc() {
+        check(
+            r#"
+//- proc_macros: identity
+pub struct Struct {
+        // ^^^^^^
+    field: i32,
+}
+
+macro_rules! identity {
+    ($($tt:tt)*) => {$($tt)*};
+}
+
+#[proc_macros::identity]
+fn function() {
+    identity!(Struct$0 { field: 0 });
+}
+
+"#,
+        )
+    }
+
     #[test]
     fn goto_def_for_extern_crate() {
         check(
@@ -1417,4 +1390,41 @@ mod foo {
             "#,
         );
     }
+
+    #[test]
+    fn goto_def_in_macro_multi() {
+        check(
+            r#"
+struct Foo {
+    foo: ()
+  //^^^
+}
+macro_rules! foo {
+    ($ident:ident) => {
+        fn $ident(Foo { $ident }: Foo) {}
+    }
+}
+foo!(foo$0);
+   //^^^
+   //^^^
+"#,
+        );
+        check(
+            r#"
+fn bar() {}
+ //^^^
+struct bar;
+     //^^^
+macro_rules! foo {
+    ($ident:ident) => {
+        fn foo() {
+            let _: $ident = $ident;
+        }
+    }
+}
+
+foo!(bar$0);
+"#,
+        );
+    }
 }
index 6994e41c8aa40f55ee6b8140e3d5da67e05c84c1..7cce99c3b0b2d82ff59d03ef7dcb9537c089d01b 100644 (file)
@@ -413,6 +413,22 @@ fn foo() {
         );
     }
 
+    #[test]
+    fn test_hl_local_in_attr() {
+        check(
+            r#"
+//- proc_macros: identity
+#[proc_macros::identity]
+fn foo() {
+    let mut bar = 3;
+         // ^^^ write
+    bar$0;
+ // ^^^ read
+}
+"#,
+        );
+    }
+
     #[test]
     fn test_multi_macro_usage() {
         check(
index 62a322f97690b00d63be3fa24a2845a96b2a6fc2..d9e990f0291e4bee59537649fa54bcd25a0c1858 100644 (file)
@@ -1,28 +1,24 @@
+mod render;
+
+#[cfg(test)]
+mod tests;
+
+use std::iter;
+
 use either::Either;
-use hir::{AsAssocItem, HasAttrs, HasSource, HirDisplay, Semantics, TypeInfo};
+use hir::{HasSource, Semantics};
 use ide_db::{
-    base_db::{FileRange, SourceDatabase},
-    defs::{Definition, NameClass, NameRefClass},
-    helpers::{
-        generated_lints::{CLIPPY_LINTS, DEFAULT_LINTS, FEATURES},
-        pick_best_token, try_resolve_derive_input_at, FamousDefs,
-    },
+    base_db::FileRange,
+    defs::Definition,
+    helpers::{pick_best_token, FamousDefs},
     RootDatabase,
 };
 use itertools::Itertools;
-use stdx::format_to;
-use syntax::{
-    algo, ast, display::fn_as_proc_macro_label, match_ast, AstNode, Direction, SyntaxKind::*,
-    SyntaxNode, SyntaxToken, T,
-};
+use syntax::{ast, match_ast, AstNode, SyntaxKind::*, SyntaxNode, SyntaxToken, T};
 
 use crate::{
-    display::{macro_label, TryToNav},
-    doc_links::{
-        doc_attributes, extract_definitions_from_docs, remove_links, resolve_doc_path_for_def,
-        rewrite_links,
-    },
-    markdown_remove::remove_markdown,
+    display::TryToNav,
+    doc_links::token_as_doc_comment,
     markup::Markup,
     runnables::{runnable_fn, runnable_mod},
     FileId, FilePosition, NavigationTarget, RangeInfo, Runnable,
@@ -60,7 +56,7 @@ fn goto_type_from_targets(db: &RootDatabase, targets: Vec<hir::ModuleDef>) -> Se
             .into_iter()
             .filter_map(|it| {
                 Some(HoverGotoTypeData {
-                    mod_path: render_path(
+                    mod_path: render::path(
                         db,
                         it.module(db)?,
                         it.name(db).map(|name| name.to_string()),
@@ -97,7 +93,7 @@ pub(crate) fn hover(
     FileRange { file_id, range }: FileRange,
     config: &HoverConfig,
 ) -> Option<RangeInfo<HoverResult>> {
-    let sema = hir::Semantics::new(db);
+    let sema = &hir::Semantics::new(db);
     let file = sema.parse(file_id).syntax().clone();
 
     if !range.is_empty() {
@@ -105,125 +101,94 @@ pub(crate) fn hover(
     }
     let offset = range.start();
 
-    let token = pick_best_token(file.token_at_offset(offset), |kind| match kind {
+    let original_token = pick_best_token(file.token_at_offset(offset), |kind| match kind {
         IDENT | INT_NUMBER | LIFETIME_IDENT | T![self] | T![super] | T![crate] => 3,
         T!['('] | T![')'] => 2,
         kind if kind.is_trivia() => 0,
         _ => 1,
     })?;
-    let token = sema.descend_into_macros(token);
-    let node = token.parent()?;
-    let mut range_override = None;
-    let definition = match_ast! {
-        match node {
-            ast::Name(name) => NameClass::classify(&sema, &name).map(|class| match class {
-                NameClass::Definition(it) | NameClass::ConstReference(it) => it,
-                NameClass::PatFieldShorthand { local_def, field_ref: _ } => Definition::Local(local_def),
-            }),
-            ast::NameRef(name_ref) => NameRefClass::classify(&sema, &name_ref).map(|class| match class {
-                NameRefClass::Definition(def) => def,
-                NameRefClass::FieldShorthand { local_ref: _, field_ref } => {
-                    Definition::Field(field_ref)
-                }
-            }),
-            ast::Lifetime(lifetime) => NameClass::classify_lifetime(&sema, &lifetime).map_or_else(
-                || {
-                    NameRefClass::classify_lifetime(&sema, &lifetime).and_then(|class| match class {
-                        NameRefClass::Definition(it) => Some(it),
-                        _ => None,
-                    })
-                },
-                NameClass::defined,
-            ),
-            _ => {
-                // intra-doc links
-                if token.kind() == COMMENT {
-                    cov_mark::hit!(no_highlight_on_comment_hover);
-                    let (attributes, def) = doc_attributes(&sema, &node)?;
-                    let (docs, doc_mapping) = attributes.docs_with_rangemap(db)?;
-                    let (idl_range, link, ns) =
-                        extract_definitions_from_docs(&docs).into_iter().find_map(|(range, link, ns)| {
-                            let mapped = doc_mapping.map(range)?;
-                            (mapped.file_id == file_id.into() && mapped.value.contains(offset)).then(||(mapped.value, link, ns))
-                        })?;
-                    range_override = Some(idl_range);
-                    Some(match resolve_doc_path_for_def(db,def, &link,ns)? {
-                        Either::Left(it) => Definition::ModuleDef(it),
-                        Either::Right(it) => Definition::Macro(it),
-                    })
-                // attributes, require special machinery as they are mere ident tokens
-                } else if let Some(attr) = token.ancestors().find_map(ast::Attr::cast) {
-                    // lints
-                    if let res@Some(_) = try_hover_for_lint(&attr, &token) {
-                        return res;
-                    // derives
-                    } else {
-                        range_override = Some(token.text_range());
-                        try_resolve_derive_input_at(&sema, &attr, &token).map(Definition::Macro)
-                    }
-                } else {
-                    None
-                }
-            },
-        }
-    };
-
-    if let Some(definition) = definition {
-        let famous_defs = match &definition {
-            Definition::ModuleDef(hir::ModuleDef::BuiltinType(_)) => {
-                Some(FamousDefs(&sema, sema.scope(&node).krate()))
-            }
-            _ => None,
-        };
-        if let Some(markup) = hover_for_definition(db, definition, famous_defs.as_ref(), config) {
-            let mut res = HoverResult::default();
-            res.markup = process_markup(sema.db, definition, &markup, config);
-            if let Some(action) = show_implementations_action(db, definition) {
-                res.actions.push(action);
-            }
-
-            if let Some(action) = show_fn_references_action(db, definition) {
-                res.actions.push(action);
-            }
 
-            if let Some(action) = runnable_action(&sema, definition, file_id) {
-                res.actions.push(action);
-            }
+    if let Some(doc_comment) = token_as_doc_comment(&original_token) {
+        cov_mark::hit!(no_highlight_on_comment_hover);
+        return doc_comment.get_definition_with_descend_at(sema, offset, |def, node, range| {
+            let res = hover_for_definition(sema, file_id, def, &node, config)?;
+            Some(RangeInfo::new(range, res))
+        });
+    }
 
-            if let Some(action) = goto_type_action_for_def(db, definition) {
-                res.actions.push(action);
-            }
+    let descended = sema.descend_into_macros_many(original_token.clone());
 
-            let range = range_override.unwrap_or_else(|| sema.original_range(&node).range);
-            return Some(RangeInfo::new(range, res));
-        }
+    // FIXME: Definition should include known lints and the like instead of having this special case here
+    if let Some(res) = descended.iter().find_map(|token| {
+        let attr = token.ancestors().find_map(ast::Attr::cast)?;
+        render::try_for_lint(&attr, &token)
+    }) {
+        return Some(RangeInfo::new(original_token.text_range(), res));
     }
 
-    if let res @ Some(_) = hover_for_keyword(&sema, config, &token) {
-        return res;
+    let result = descended
+        .iter()
+        .filter_map(|token| {
+            let node = token.parent()?;
+            let defs = Definition::from_token(sema, token);
+            Some(defs.into_iter().zip(iter::once(node).cycle()))
+        })
+        .flatten()
+        .unique_by(|&(def, _)| def)
+        .filter_map(|(def, node)| hover_for_definition(sema, file_id, def, &node, config))
+        .reduce(|mut acc, HoverResult { markup, actions }| {
+            acc.actions.extend(actions);
+            acc.markup = Markup::from(format!("{}\n---\n{}", acc.markup, markup));
+            acc
+        });
+    if result.is_none() {
+        // fallbacks, show keywords or types
+        if let Some(res) = render::keyword(sema, config, &original_token) {
+            return Some(RangeInfo::new(original_token.text_range(), res));
+        }
+        if let res @ Some(_) =
+            descended.iter().find_map(|token| hover_type_fallback(sema, config, token))
+        {
+            return res;
+        }
     }
+    result.map(|res| RangeInfo::new(original_token.text_range(), res))
+}
 
-    // No definition below cursor, fall back to showing type hovers.
+pub(crate) fn hover_for_definition(
+    sema: &Semantics<RootDatabase>,
+    file_id: FileId,
+    definition: Definition,
+    node: &SyntaxNode,
+    config: &HoverConfig,
+) -> Option<HoverResult> {
+    let famous_defs = match &definition {
+        Definition::ModuleDef(hir::ModuleDef::BuiltinType(_)) => {
+            Some(FamousDefs(&sema, sema.scope(&node).krate()))
+        }
+        _ => None,
+    };
+    if let Some(markup) = render::definition(sema.db, definition, famous_defs.as_ref(), config) {
+        let mut res = HoverResult::default();
+        res.markup = render::process_markup(sema.db, definition, &markup, config);
+        if let Some(action) = show_implementations_action(sema.db, definition) {
+            res.actions.push(action);
+        }
 
-    let node = token
-        .ancestors()
-        .take_while(|it| !ast::Item::can_cast(it.kind()))
-        .find(|n| ast::Expr::can_cast(n.kind()) || ast::Pat::can_cast(n.kind()))?;
+        if let Some(action) = show_fn_references_action(sema.db, definition) {
+            res.actions.push(action);
+        }
 
-    let expr_or_pat = match_ast! {
-        match node {
-            ast::Expr(it) => Either::Left(it),
-            ast::Pat(it) => Either::Right(it),
-            // If this node is a MACRO_CALL, it means that `descend_into_macros` failed to resolve.
-            // (e.g expanding a builtin macro). So we give up here.
-            ast::MacroCall(_it) => return None,
-            _ => return None,
+        if let Some(action) = runnable_action(&sema, definition, file_id) {
+            res.actions.push(action);
         }
-    };
 
-    let res = hover_type_info(&sema, config, &expr_or_pat)?;
-    let range = sema.original_range(&node).range;
-    Some(RangeInfo::new(range, res))
+        if let Some(action) = goto_type_action_for_def(sema.db, definition) {
+            res.actions.push(action);
+        }
+        return Some(res);
+    }
+    None
 }
 
 fn hover_ranged(
@@ -242,10 +207,15 @@ fn hover_ranged(
         }
     })?;
     let res = match &expr_or_pat {
-        Either::Left(ast::Expr::TryExpr(try_expr)) => hover_try_expr(sema, config, try_expr),
+        Either::Left(ast::Expr::TryExpr(try_expr)) => render::try_expr(sema, config, try_expr),
+        Either::Left(ast::Expr::PrefixExpr(prefix_expr))
+            if prefix_expr.op_kind() == Some(ast::UnaryOp::Deref) =>
+        {
+            render::deref_expr(sema, config, prefix_expr)
+        }
         _ => None,
     };
-    let res = res.or_else(|| hover_type_info(sema, config, &expr_or_pat));
+    let res = res.or_else(|| render::type_info(sema, config, &expr_or_pat));
     res.map(|it| {
         let range = match expr_or_pat {
             Either::Left(it) => it.syntax().text_range(),
@@ -255,184 +225,30 @@ fn hover_ranged(
     })
 }
 
-fn hover_try_expr(
-    sema: &Semantics<RootDatabase>,
-    config: &HoverConfig,
-    try_expr: &ast::TryExpr,
-) -> Option<HoverResult> {
-    let inner_ty = sema.type_of_expr(&try_expr.expr()?)?.original;
-    let mut ancestors = try_expr.syntax().ancestors();
-    let mut body_ty = loop {
-        let next = ancestors.next()?;
-        break match_ast! {
-            match next {
-                ast::Fn(fn_) => sema.to_def(&fn_)?.ret_type(sema.db),
-                ast::Item(__) => return None,
-                ast::ClosureExpr(closure) => sema.type_of_expr(&closure.body()?)?.original,
-                ast::EffectExpr(effect) => if matches!(effect.effect(), ast::Effect::Async(_) | ast::Effect::Try(_)| ast::Effect::Const(_)) {
-                    sema.type_of_expr(&effect.block_expr()?.into())?.original
-                } else {
-                    continue;
-                },
-                _ => continue,
-            }
-        };
-    };
-
-    if inner_ty == body_ty {
-        return None;
-    }
-
-    let mut inner_ty = inner_ty;
-    let mut s = "Try Target".to_owned();
-
-    let adts = inner_ty.as_adt().zip(body_ty.as_adt());
-    if let Some((hir::Adt::Enum(inner), hir::Adt::Enum(body))) = adts {
-        let famous_defs = FamousDefs(sema, sema.scope(&try_expr.syntax()).krate());
-        // special case for two options, there is no value in showing them
-        if let Some(option_enum) = famous_defs.core_option_Option() {
-            if inner == option_enum && body == option_enum {
-                cov_mark::hit!(hover_try_expr_opt_opt);
-                return None;
-            }
-        }
-
-        // special case two results to show the error variants only
-        if let Some(result_enum) = famous_defs.core_result_Result() {
-            if inner == result_enum && body == result_enum {
-                let error_type_args =
-                    inner_ty.type_arguments().nth(1).zip(body_ty.type_arguments().nth(1));
-                if let Some((inner, body)) = error_type_args {
-                    inner_ty = inner;
-                    body_ty = body;
-                    s = "Try Error".to_owned();
-                }
-            }
-        }
-    }
-
-    let mut res = HoverResult::default();
-
-    let mut targets: Vec<hir::ModuleDef> = Vec::new();
-    let mut push_new_def = |item: hir::ModuleDef| {
-        if !targets.contains(&item) {
-            targets.push(item);
-        }
-    };
-    walk_and_push_ty(sema.db, &inner_ty, &mut push_new_def);
-    walk_and_push_ty(sema.db, &body_ty, &mut push_new_def);
-    res.actions.push(HoverAction::goto_type_from_targets(sema.db, targets));
-
-    let inner_ty = inner_ty.display(sema.db).to_string();
-    let body_ty = body_ty.display(sema.db).to_string();
-    let ty_len_max = inner_ty.len().max(body_ty.len());
-
-    let l = "Propagated as: ".len() - " Type: ".len();
-    let static_text_len_diff = l as isize - s.len() as isize;
-    let tpad = static_text_len_diff.max(0) as usize;
-    let ppad = static_text_len_diff.min(0).abs() as usize;
-
-    res.markup = format!(
-        "{bt_start}{} Type: {:>pad0$}\nPropagated as: {:>pad1$}\n{bt_end}",
-        s,
-        inner_ty,
-        body_ty,
-        pad0 = ty_len_max + tpad,
-        pad1 = ty_len_max + ppad,
-        bt_start = if config.markdown() { "```text\n" } else { "" },
-        bt_end = if config.markdown() { "```\n" } else { "" }
-    )
-    .into();
-    Some(res)
-}
-
-fn hover_type_info(
+fn hover_type_fallback(
     sema: &Semantics<RootDatabase>,
     config: &HoverConfig,
-    expr_or_pat: &Either<ast::Expr, ast::Pat>,
-) -> Option<HoverResult> {
-    let TypeInfo { original, adjusted } = match expr_or_pat {
-        Either::Left(expr) => sema.type_of_expr(expr)?,
-        Either::Right(pat) => sema.type_of_pat(pat)?,
-    };
-
-    let mut res = HoverResult::default();
-    let mut targets: Vec<hir::ModuleDef> = Vec::new();
-    let mut push_new_def = |item: hir::ModuleDef| {
-        if !targets.contains(&item) {
-            targets.push(item);
-        }
-    };
-    walk_and_push_ty(sema.db, &original, &mut push_new_def);
-
-    res.markup = if let Some(adjusted_ty) = adjusted {
-        walk_and_push_ty(sema.db, &adjusted_ty, &mut push_new_def);
-        let original = original.display(sema.db).to_string();
-        let adjusted = adjusted_ty.display(sema.db).to_string();
-        let static_text_diff_len = "Coerced to: ".len() - "Type: ".len();
-        format!(
-            "{bt_start}Type: {:>apad$}\nCoerced to: {:>opad$}\n{bt_end}",
-            original,
-            adjusted,
-            apad = static_text_diff_len + adjusted.len().max(original.len()),
-            opad = original.len(),
-            bt_start = if config.markdown() { "```text\n" } else { "" },
-            bt_end = if config.markdown() { "```\n" } else { "" }
-        )
-        .into()
-    } else {
-        if config.markdown() {
-            Markup::fenced_block(&original.display(sema.db))
-        } else {
-            original.display(sema.db).to_string().into()
-        }
-    };
-    res.actions.push(HoverAction::goto_type_from_targets(sema.db, targets));
-    Some(res)
-}
+    token: &SyntaxToken,
+) -> Option<RangeInfo<HoverResult>> {
+    let node = token
+        .ancestors()
+        .take_while(|it| !ast::Item::can_cast(it.kind()))
+        .find(|n| ast::Expr::can_cast(n.kind()) || ast::Pat::can_cast(n.kind()))?;
 
-fn try_hover_for_lint(attr: &ast::Attr, token: &SyntaxToken) -> Option<RangeInfo<HoverResult>> {
-    let (path, tt) = attr.as_simple_call()?;
-    if !tt.syntax().text_range().contains(token.text_range().start()) {
-        return None;
-    }
-    let (is_clippy, lints) = match &*path {
-        "feature" => (false, FEATURES),
-        "allow" | "deny" | "forbid" | "warn" => {
-            let is_clippy = algo::non_trivia_sibling(token.clone().into(), Direction::Prev)
-                .filter(|t| t.kind() == T![:])
-                .and_then(|t| algo::non_trivia_sibling(t, Direction::Prev))
-                .filter(|t| t.kind() == T![:])
-                .and_then(|t| algo::non_trivia_sibling(t, Direction::Prev))
-                .map_or(false, |t| {
-                    t.kind() == T![ident] && t.into_token().map_or(false, |t| t.text() == "clippy")
-                });
-            if is_clippy {
-                (true, CLIPPY_LINTS)
-            } else {
-                (false, DEFAULT_LINTS)
-            }
+    let expr_or_pat = match_ast! {
+        match node {
+            ast::Expr(it) => Either::Left(it),
+            ast::Pat(it) => Either::Right(it),
+            // If this node is a MACRO_CALL, it means that `descend_into_macros_many` failed to resolve.
+            // (e.g expanding a builtin macro). So we give up here.
+            ast::MacroCall(_it) => return None,
+            _ => return None,
         }
-        _ => return None,
-    };
-
-    let tmp;
-    let needle = if is_clippy {
-        tmp = format!("clippy::{}", token.text());
-        &tmp
-    } else {
-        &*token.text()
     };
 
-    let lint =
-        lints.binary_search_by_key(&needle, |lint| lint.label).ok().map(|idx| &lints[idx])?;
-    Some(RangeInfo::new(
-        token.text_range(),
-        HoverResult {
-            markup: Markup::from(format!("```\n{}\n```\n___\n\n{}", lint.label, lint.description)),
-            ..Default::default()
-        },
-    ))
+    let res = render::type_info(&sema, config, &expr_or_pat)?;
+    let range = sema.original_range(&node).range;
+    Some(RangeInfo::new(range, res))
 }
 
 fn show_implementations_action(db: &RootDatabase, def: Definition) -> Option<HoverAction> {
@@ -533,3961 +349,3 @@ fn walk_and_push_ty(
         }
     });
 }
-
-fn hover_markup(docs: Option<String>, desc: String, mod_path: Option<String>) -> Option<Markup> {
-    let mut buf = String::new();
-
-    if let Some(mod_path) = mod_path {
-        if !mod_path.is_empty() {
-            format_to!(buf, "```rust\n{}\n```\n\n", mod_path);
-        }
-    }
-    format_to!(buf, "```rust\n{}\n```", desc);
-
-    if let Some(doc) = docs {
-        format_to!(buf, "\n___\n\n{}", doc);
-    }
-    Some(buf.into())
-}
-
-fn process_markup(
-    db: &RootDatabase,
-    def: Definition,
-    markup: &Markup,
-    config: &HoverConfig,
-) -> Markup {
-    let markup = markup.as_str();
-    let markup = if !config.markdown() {
-        remove_markdown(markup)
-    } else if config.links_in_hover {
-        rewrite_links(db, markup, def)
-    } else {
-        remove_links(markup)
-    };
-    Markup::from(markup)
-}
-
-fn definition_owner_name(db: &RootDatabase, def: &Definition) -> Option<String> {
-    match def {
-        Definition::Field(f) => Some(f.parent_def(db).name(db)),
-        Definition::Local(l) => l.parent(db).name(db),
-        Definition::ModuleDef(md) => match md {
-            hir::ModuleDef::Function(f) => match f.as_assoc_item(db)?.container(db) {
-                hir::AssocItemContainer::Trait(t) => Some(t.name(db)),
-                hir::AssocItemContainer::Impl(i) => i.self_ty(db).as_adt().map(|adt| adt.name(db)),
-            },
-            hir::ModuleDef::Variant(e) => Some(e.parent_enum(db).name(db)),
-            _ => None,
-        },
-        _ => None,
-    }
-    .map(|name| name.to_string())
-}
-
-fn render_path(db: &RootDatabase, module: hir::Module, item_name: Option<String>) -> String {
-    let crate_name =
-        db.crate_graph()[module.krate().into()].display_name.as_ref().map(|it| it.to_string());
-    let module_path = module
-        .path_to_root(db)
-        .into_iter()
-        .rev()
-        .flat_map(|it| it.name(db).map(|name| name.to_string()));
-    crate_name.into_iter().chain(module_path).chain(item_name).join("::")
-}
-
-fn definition_mod_path(db: &RootDatabase, def: &Definition) -> Option<String> {
-    if let Definition::GenericParam(_) = def {
-        return None;
-    }
-    def.module(db).map(|module| render_path(db, module, definition_owner_name(db, def)))
-}
-
-fn hover_for_definition(
-    db: &RootDatabase,
-    def: Definition,
-    famous_defs: Option<&FamousDefs>,
-    config: &HoverConfig,
-) -> Option<Markup> {
-    let mod_path = definition_mod_path(db, &def);
-    let (label, docs) = match def {
-        Definition::Macro(it) => (
-            match &it.source(db)?.value {
-                Either::Left(mac) => macro_label(mac),
-                Either::Right(mac_fn) => fn_as_proc_macro_label(mac_fn),
-            },
-            it.attrs(db).docs(),
-        ),
-        Definition::Field(def) => label_and_docs(db, def),
-        Definition::ModuleDef(it) => match it {
-            hir::ModuleDef::Module(it) => label_and_docs(db, it),
-            hir::ModuleDef::Function(it) => label_and_docs(db, it),
-            hir::ModuleDef::Adt(it) => label_and_docs(db, it),
-            hir::ModuleDef::Variant(it) => label_and_docs(db, it),
-            hir::ModuleDef::Const(it) => label_and_docs(db, it),
-            hir::ModuleDef::Static(it) => label_and_docs(db, it),
-            hir::ModuleDef::Trait(it) => label_and_docs(db, it),
-            hir::ModuleDef::TypeAlias(it) => label_and_docs(db, it),
-            hir::ModuleDef::BuiltinType(it) => {
-                return famous_defs
-                    .and_then(|fd| hover_for_builtin(fd, it))
-                    .or_else(|| Some(Markup::fenced_block(&it.name())))
-            }
-        },
-        Definition::Local(it) => return hover_for_local(it, db),
-        Definition::SelfType(impl_def) => {
-            impl_def.self_ty(db).as_adt().map(|adt| label_and_docs(db, adt))?
-        }
-        Definition::GenericParam(it) => label_and_docs(db, it),
-        Definition::Label(it) => return Some(Markup::fenced_block(&it.name(db))),
-    };
-
-    return hover_markup(
-        docs.filter(|_| config.documentation.is_some()).map(Into::into),
-        label,
-        mod_path,
-    );
-
-    fn label_and_docs<D>(db: &RootDatabase, def: D) -> (String, Option<hir::Documentation>)
-    where
-        D: HasAttrs + HirDisplay,
-    {
-        let label = def.display(db).to_string();
-        let docs = def.attrs(db).docs();
-        (label, docs)
-    }
-}
-
-fn hover_for_local(it: hir::Local, db: &RootDatabase) -> Option<Markup> {
-    let ty = it.ty(db);
-    let ty = ty.display(db);
-    let is_mut = if it.is_mut(db) { "mut " } else { "" };
-    let desc = match it.source(db).value {
-        Either::Left(ident) => {
-            let name = it.name(db).unwrap();
-            let let_kw = if ident
-                .syntax()
-                .parent()
-                .map_or(false, |p| p.kind() == LET_STMT || p.kind() == CONDITION)
-            {
-                "let "
-            } else {
-                ""
-            };
-            format!("{}{}{}: {}", let_kw, is_mut, name, ty)
-        }
-        Either::Right(_) => format!("{}self: {}", is_mut, ty),
-    };
-    hover_markup(None, desc, None)
-}
-
-fn hover_for_keyword(
-    sema: &Semantics<RootDatabase>,
-    config: &HoverConfig,
-    token: &SyntaxToken,
-) -> Option<RangeInfo<HoverResult>> {
-    if !token.kind().is_keyword() || !config.documentation.is_some() {
-        return None;
-    }
-    let famous_defs = FamousDefs(sema, sema.scope(&token.parent()?).krate());
-    // std exposes {}_keyword modules with docstrings on the root to document keywords
-    let keyword_mod = format!("{}_keyword", token.text());
-    let doc_owner = find_std_module(&famous_defs, &keyword_mod)?;
-    let docs = doc_owner.attrs(sema.db).docs()?;
-    let markup = process_markup(
-        sema.db,
-        Definition::ModuleDef(doc_owner.into()),
-        &hover_markup(Some(docs.into()), token.text().into(), None)?,
-        config,
-    );
-    Some(RangeInfo::new(token.text_range(), HoverResult { markup, actions: Default::default() }))
-}
-
-fn hover_for_builtin(famous_defs: &FamousDefs, builtin: hir::BuiltinType) -> Option<Markup> {
-    // std exposes prim_{} modules with docstrings on the root to document the builtins
-    let primitive_mod = format!("prim_{}", builtin.name());
-    let doc_owner = find_std_module(famous_defs, &primitive_mod)?;
-    let docs = doc_owner.attrs(famous_defs.0.db).docs()?;
-    hover_markup(Some(docs.into()), builtin.name().to_string(), None)
-}
-
-fn find_std_module(famous_defs: &FamousDefs, name: &str) -> Option<hir::Module> {
-    let db = famous_defs.0.db;
-    let std_crate = famous_defs.std()?;
-    let std_root_module = std_crate.root_module(db);
-    std_root_module
-        .children(db)
-        .find(|module| module.name(db).map_or(false, |module| module.to_string() == name))
-}
-
-#[cfg(test)]
-mod tests {
-    use expect_test::{expect, Expect};
-    use ide_db::base_db::{FileLoader, FileRange};
-    use syntax::TextRange;
-
-    use crate::{fixture, hover::HoverDocFormat, HoverConfig};
-
-    fn check_hover_no_result(ra_fixture: &str) {
-        let (analysis, position) = fixture::position(ra_fixture);
-        let hover = analysis
-            .hover(
-                &HoverConfig {
-                    links_in_hover: true,
-                    documentation: Some(HoverDocFormat::Markdown),
-                },
-                FileRange { file_id: position.file_id, range: TextRange::empty(position.offset) },
-            )
-            .unwrap();
-        assert!(hover.is_none());
-    }
-
-    fn check(ra_fixture: &str, expect: Expect) {
-        let (analysis, position) = fixture::position(ra_fixture);
-        let hover = analysis
-            .hover(
-                &HoverConfig {
-                    links_in_hover: true,
-                    documentation: Some(HoverDocFormat::Markdown),
-                },
-                FileRange { file_id: position.file_id, range: TextRange::empty(position.offset) },
-            )
-            .unwrap()
-            .unwrap();
-
-        let content = analysis.db.file_text(position.file_id);
-        let hovered_element = &content[hover.range];
-
-        let actual = format!("*{}*\n{}\n", hovered_element, hover.info.markup);
-        expect.assert_eq(&actual)
-    }
-
-    fn check_hover_no_links(ra_fixture: &str, expect: Expect) {
-        let (analysis, position) = fixture::position(ra_fixture);
-        let hover = analysis
-            .hover(
-                &HoverConfig {
-                    links_in_hover: false,
-                    documentation: Some(HoverDocFormat::Markdown),
-                },
-                FileRange { file_id: position.file_id, range: TextRange::empty(position.offset) },
-            )
-            .unwrap()
-            .unwrap();
-
-        let content = analysis.db.file_text(position.file_id);
-        let hovered_element = &content[hover.range];
-
-        let actual = format!("*{}*\n{}\n", hovered_element, hover.info.markup);
-        expect.assert_eq(&actual)
-    }
-
-    fn check_hover_no_markdown(ra_fixture: &str, expect: Expect) {
-        let (analysis, position) = fixture::position(ra_fixture);
-        let hover = analysis
-            .hover(
-                &HoverConfig {
-                    links_in_hover: true,
-                    documentation: Some(HoverDocFormat::PlainText),
-                },
-                FileRange { file_id: position.file_id, range: TextRange::empty(position.offset) },
-            )
-            .unwrap()
-            .unwrap();
-
-        let content = analysis.db.file_text(position.file_id);
-        let hovered_element = &content[hover.range];
-
-        let actual = format!("*{}*\n{}\n", hovered_element, hover.info.markup);
-        expect.assert_eq(&actual)
-    }
-
-    fn check_actions(ra_fixture: &str, expect: Expect) {
-        let (analysis, file_id, position) = fixture::range_or_position(ra_fixture);
-        let hover = analysis
-            .hover(
-                &HoverConfig {
-                    links_in_hover: true,
-                    documentation: Some(HoverDocFormat::Markdown),
-                },
-                FileRange { file_id, range: position.range_or_empty() },
-            )
-            .unwrap()
-            .unwrap();
-        expect.assert_debug_eq(&hover.info.actions)
-    }
-
-    fn check_hover_range(ra_fixture: &str, expect: Expect) {
-        let (analysis, range) = fixture::range(ra_fixture);
-        let hover = analysis
-            .hover(
-                &HoverConfig {
-                    links_in_hover: false,
-                    documentation: Some(HoverDocFormat::Markdown),
-                },
-                range,
-            )
-            .unwrap()
-            .unwrap();
-        expect.assert_eq(hover.info.markup.as_str())
-    }
-
-    fn check_hover_range_no_results(ra_fixture: &str) {
-        let (analysis, range) = fixture::range(ra_fixture);
-        let hover = analysis
-            .hover(
-                &HoverConfig {
-                    links_in_hover: false,
-                    documentation: Some(HoverDocFormat::Markdown),
-                },
-                range,
-            )
-            .unwrap();
-        assert!(hover.is_none());
-    }
-
-    #[test]
-    fn hover_shows_type_of_an_expression() {
-        check(
-            r#"
-pub fn foo() -> u32 { 1 }
-
-fn main() {
-    let foo_test = foo()$0;
-}
-"#,
-            expect![[r#"
-                *foo()*
-                ```rust
-                u32
-                ```
-            "#]],
-        );
-    }
-
-    #[test]
-    fn hover_remove_markdown_if_configured() {
-        check_hover_no_markdown(
-            r#"
-pub fn foo() -> u32 { 1 }
-
-fn main() {
-    let foo_test = foo()$0;
-}
-"#,
-            expect![[r#"
-                *foo()*
-                u32
-            "#]],
-        );
-    }
-
-    #[test]
-    fn hover_shows_long_type_of_an_expression() {
-        check(
-            r#"
-struct Scan<A, B, C> { a: A, b: B, c: C }
-struct Iter<I> { inner: I }
-enum Option<T> { Some(T), None }
-
-struct OtherStruct<T> { i: T }
-
-fn scan<A, B, C>(a: A, b: B, c: C) -> Iter<Scan<OtherStruct<A>, B, C>> {
-    Iter { inner: Scan { a, b, c } }
-}
-
-fn main() {
-    let num: i32 = 55;
-    let closure = |memo: &mut u32, value: &u32, _another: &mut u32| -> Option<u32> {
-        Option::Some(*memo + value)
-    };
-    let number = 5u32;
-    let mut iter$0 = scan(OtherStruct { i: num }, closure, number);
-}
-"#,
-            expect![[r#"
-                *iter*
-
-                ```rust
-                let mut iter: Iter<Scan<OtherStruct<OtherStruct<i32>>, |&mut u32, &u32, &mut u32| -> Option<u32>, u32>>
-                ```
-            "#]],
-        );
-    }
-
-    #[test]
-    fn hover_shows_fn_signature() {
-        // Single file with result
-        check(
-            r#"
-pub fn foo() -> u32 { 1 }
-
-fn main() { let foo_test = fo$0o(); }
-"#,
-            expect![[r#"
-                *foo*
-
-                ```rust
-                test
-                ```
-
-                ```rust
-                pub fn foo() -> u32
-                ```
-            "#]],
-        );
-
-        // Multiple candidates but results are ambiguous.
-        check(
-            r#"
-//- /a.rs
-pub fn foo() -> u32 { 1 }
-
-//- /b.rs
-pub fn foo() -> &str { "" }
-
-//- /c.rs
-pub fn foo(a: u32, b: u32) {}
-
-//- /main.rs
-mod a;
-mod b;
-mod c;
-
-fn main() { let foo_test = fo$0o(); }
-        "#,
-            expect![[r#"
-                *foo*
-                ```rust
-                {unknown}
-                ```
-            "#]],
-        );
-
-        // Use literal `crate` in path
-        check(
-            r#"
-pub struct X;
-
-fn foo() -> crate::X { X }
-
-fn main() { f$0oo(); }
-        "#,
-            expect![[r#"
-            *foo*
-
-            ```rust
-            test
-            ```
-
-            ```rust
-            fn foo() -> crate::X
-            ```
-        "#]],
-        );
-
-        // Check `super` in path
-        check(
-            r#"
-pub struct X;
-
-mod m { pub fn foo() -> super::X { super::X } }
-
-fn main() { m::f$0oo(); }
-        "#,
-            expect![[r#"
-                *foo*
-
-                ```rust
-                test::m
-                ```
-
-                ```rust
-                pub fn foo() -> super::X
-                ```
-            "#]],
-        );
-    }
-
-    #[test]
-    fn hover_shows_fn_signature_with_type_params() {
-        check(
-            r#"
-pub fn foo<'a, T: AsRef<str>>(b: &'a T) -> &'a str { }
-
-fn main() { let foo_test = fo$0o(); }
-        "#,
-            expect![[r#"
-                *foo*
-
-                ```rust
-                test
-                ```
-
-                ```rust
-                pub fn foo<'a, T>(b: &'a T) -> &'a str
-                where
-                    T: AsRef<str>,
-                ```
-            "#]],
-        );
-    }
-
-    #[test]
-    fn hover_shows_fn_signature_on_fn_name() {
-        check(
-            r#"
-pub fn foo$0(a: u32, b: u32) -> u32 {}
-
-fn main() { }
-"#,
-            expect![[r#"
-                *foo*
-
-                ```rust
-                test
-                ```
-
-                ```rust
-                pub fn foo(a: u32, b: u32) -> u32
-                ```
-            "#]],
-        );
-    }
-
-    #[test]
-    fn hover_shows_fn_doc() {
-        check(
-            r#"
-/// # Example
-/// ```
-/// # use std::path::Path;
-/// #
-/// foo(Path::new("hello, world!"))
-/// ```
-pub fn foo$0(_: &Path) {}
-
-fn main() { }
-"#,
-            expect![[r##"
-                *foo*
-
-                ```rust
-                test
-                ```
-
-                ```rust
-                pub fn foo(_: &Path)
-                ```
-
-                ---
-
-                # Example
-
-                ```
-                # use std::path::Path;
-                #
-                foo(Path::new("hello, world!"))
-                ```
-            "##]],
-        );
-    }
-
-    #[test]
-    fn hover_shows_fn_doc_attr_raw_string() {
-        check(
-            r##"
-#[doc = r#"Raw string doc attr"#]
-pub fn foo$0(_: &Path) {}
-
-fn main() { }
-"##,
-            expect![[r##"
-                *foo*
-
-                ```rust
-                test
-                ```
-
-                ```rust
-                pub fn foo(_: &Path)
-                ```
-
-                ---
-
-                Raw string doc attr
-            "##]],
-        );
-    }
-
-    #[test]
-    fn hover_shows_struct_field_info() {
-        // Hovering over the field when instantiating
-        check(
-            r#"
-struct Foo { field_a: u32 }
-
-fn main() {
-    let foo = Foo { field_a$0: 0, };
-}
-"#,
-            expect![[r#"
-                *field_a*
-
-                ```rust
-                test::Foo
-                ```
-
-                ```rust
-                field_a: u32
-                ```
-            "#]],
-        );
-
-        // Hovering over the field in the definition
-        check(
-            r#"
-struct Foo { field_a$0: u32 }
-
-fn main() {
-    let foo = Foo { field_a: 0 };
-}
-"#,
-            expect![[r#"
-                *field_a*
-
-                ```rust
-                test::Foo
-                ```
-
-                ```rust
-                field_a: u32
-                ```
-            "#]],
-        );
-    }
-
-    #[test]
-    fn hover_const_static() {
-        check(
-            r#"const foo$0: u32 = 123;"#,
-            expect![[r#"
-                *foo*
-
-                ```rust
-                test
-                ```
-
-                ```rust
-                const foo: u32
-                ```
-            "#]],
-        );
-        check(
-            r#"static foo$0: u32 = 456;"#,
-            expect![[r#"
-                *foo*
-
-                ```rust
-                test
-                ```
-
-                ```rust
-                static foo: u32
-                ```
-            "#]],
-        );
-    }
-
-    #[test]
-    fn hover_default_generic_types() {
-        check(
-            r#"
-struct Test<K, T = u8> { k: K, t: T }
-
-fn main() {
-    let zz$0 = Test { t: 23u8, k: 33 };
-}"#,
-            expect![[r#"
-                *zz*
-
-                ```rust
-                let zz: Test<i32, u8>
-                ```
-            "#]],
-        );
-    }
-
-    #[test]
-    fn hover_some() {
-        check(
-            r#"
-enum Option<T> { Some(T) }
-use Option::Some;
-
-fn main() { So$0me(12); }
-"#,
-            expect![[r#"
-                *Some*
-
-                ```rust
-                test::Option
-                ```
-
-                ```rust
-                Some(T)
-                ```
-            "#]],
-        );
-
-        check(
-            r#"
-enum Option<T> { Some(T) }
-use Option::Some;
-
-fn main() { let b$0ar = Some(12); }
-"#,
-            expect![[r#"
-                *bar*
-
-                ```rust
-                let bar: Option<i32>
-                ```
-            "#]],
-        );
-    }
-
-    #[test]
-    fn hover_enum_variant() {
-        check(
-            r#"
-enum Option<T> {
-    /// The None variant
-    Non$0e
-}
-"#,
-            expect![[r#"
-                *None*
-
-                ```rust
-                test::Option
-                ```
-
-                ```rust
-                None
-                ```
-
-                ---
-
-                The None variant
-            "#]],
-        );
-
-        check(
-            r#"
-enum Option<T> {
-    /// The Some variant
-    Some(T)
-}
-fn main() {
-    let s = Option::Som$0e(12);
-}
-"#,
-            expect![[r#"
-                *Some*
-
-                ```rust
-                test::Option
-                ```
-
-                ```rust
-                Some(T)
-                ```
-
-                ---
-
-                The Some variant
-            "#]],
-        );
-    }
-
-    #[test]
-    fn hover_for_local_variable() {
-        check(
-            r#"fn func(foo: i32) { fo$0o; }"#,
-            expect![[r#"
-                *foo*
-
-                ```rust
-                foo: i32
-                ```
-            "#]],
-        )
-    }
-
-    #[test]
-    fn hover_for_local_variable_pat() {
-        check(
-            r#"fn func(fo$0o: i32) {}"#,
-            expect![[r#"
-                *foo*
-
-                ```rust
-                foo: i32
-                ```
-            "#]],
-        )
-    }
-
-    #[test]
-    fn hover_local_var_edge() {
-        check(
-            r#"fn func(foo: i32) { if true { $0foo; }; }"#,
-            expect![[r#"
-                *foo*
-
-                ```rust
-                foo: i32
-                ```
-            "#]],
-        )
-    }
-
-    #[test]
-    fn hover_for_param_edge() {
-        check(
-            r#"fn func($0foo: i32) {}"#,
-            expect![[r#"
-                *foo*
-
-                ```rust
-                foo: i32
-                ```
-            "#]],
-        )
-    }
-
-    #[test]
-    fn hover_for_param_with_multiple_traits() {
-        check(
-            r#"
-            //- minicore: sized
-            trait Deref {
-                type Target: ?Sized;
-            }
-            trait DerefMut {
-                type Target: ?Sized;
-            }
-            fn f(_x$0: impl Deref<Target=u8> + DerefMut<Target=u8>) {}"#,
-            expect![[r#"
-                *_x*
-
-                ```rust
-                _x: impl Deref<Target = u8> + DerefMut<Target = u8>
-                ```
-            "#]],
-        )
-    }
-
-    #[test]
-    fn test_hover_infer_associated_method_result() {
-        check(
-            r#"
-struct Thing { x: u32 }
-
-impl Thing {
-    fn new() -> Thing { Thing { x: 0 } }
-}
-
-fn main() { let foo_$0test = Thing::new(); }
-"#,
-            expect![[r#"
-                *foo_test*
-
-                ```rust
-                let foo_test: Thing
-                ```
-            "#]],
-        )
-    }
-
-    #[test]
-    fn test_hover_infer_associated_method_exact() {
-        check(
-            r#"
-mod wrapper {
-    struct Thing { x: u32 }
-
-    impl Thing {
-        fn new() -> Thing { Thing { x: 0 } }
-    }
-}
-
-fn main() { let foo_test = wrapper::Thing::new$0(); }
-"#,
-            expect![[r#"
-                *new*
-
-                ```rust
-                test::wrapper::Thing
-                ```
-
-                ```rust
-                fn new() -> Thing
-                ```
-            "#]],
-        )
-    }
-
-    #[test]
-    fn test_hover_infer_associated_const_in_pattern() {
-        check(
-            r#"
-struct X;
-impl X {
-    const C: u32 = 1;
-}
-
-fn main() {
-    match 1 {
-        X::C$0 => {},
-        2 => {},
-        _ => {}
-    };
-}
-"#,
-            expect![[r#"
-                *C*
-
-                ```rust
-                test
-                ```
-
-                ```rust
-                const C: u32
-                ```
-            "#]],
-        )
-    }
-
-    #[test]
-    fn test_hover_self() {
-        check(
-            r#"
-struct Thing { x: u32 }
-impl Thing {
-    fn new() -> Self { Self$0 { x: 0 } }
-}
-"#,
-            expect![[r#"
-                *Self*
-
-                ```rust
-                test
-                ```
-
-                ```rust
-                struct Thing
-                ```
-            "#]],
-        );
-        check(
-            r#"
-struct Thing { x: u32 }
-impl Thing {
-    fn new() -> Self$0 { Self { x: 0 } }
-}
-"#,
-            expect![[r#"
-                *Self*
-
-                ```rust
-                test
-                ```
-
-                ```rust
-                struct Thing
-                ```
-            "#]],
-        );
-        check(
-            r#"
-enum Thing { A }
-impl Thing {
-    pub fn new() -> Self$0 { Thing::A }
-}
-"#,
-            expect![[r#"
-                *Self*
-
-                ```rust
-                test
-                ```
-
-                ```rust
-                enum Thing
-                ```
-            "#]],
-        );
-        check(
-            r#"
-        enum Thing { A }
-        impl Thing {
-            pub fn thing(a: Self$0) {}
-        }
-        "#,
-            expect![[r#"
-                *Self*
-
-                ```rust
-                test
-                ```
-
-                ```rust
-                enum Thing
-                ```
-            "#]],
-        );
-    }
-
-    #[test]
-    fn test_hover_shadowing_pat() {
-        check(
-            r#"
-fn x() {}
-
-fn y() {
-    let x = 0i32;
-    x$0;
-}
-"#,
-            expect![[r#"
-                *x*
-
-                ```rust
-                let x: i32
-                ```
-            "#]],
-        )
-    }
-
-    #[test]
-    fn test_hover_macro_invocation() {
-        check(
-            r#"
-macro_rules! foo { () => {} }
-
-fn f() { fo$0o!(); }
-"#,
-            expect![[r#"
-                *foo*
-
-                ```rust
-                test
-                ```
-
-                ```rust
-                macro_rules! foo
-                ```
-            "#]],
-        )
-    }
-
-    #[test]
-    fn test_hover_macro2_invocation() {
-        check(
-            r#"
-/// foo bar
-///
-/// foo bar baz
-macro foo() {}
-
-fn f() { fo$0o!(); }
-"#,
-            expect![[r#"
-                *foo*
-
-                ```rust
-                test
-                ```
-
-                ```rust
-                macro foo
-                ```
-
-                ---
-
-                foo bar
-
-                foo bar baz
-            "#]],
-        )
-    }
-
-    #[test]
-    fn test_hover_tuple_field() {
-        check(
-            r#"struct TS(String, i32$0);"#,
-            expect![[r#"
-                *i32*
-
-                ```rust
-                i32
-                ```
-            "#]],
-        )
-    }
-
-    #[test]
-    fn test_hover_through_macro() {
-        check(
-            r#"
-macro_rules! id { ($($tt:tt)*) => { $($tt)* } }
-fn foo() {}
-id! {
-    fn bar() { fo$0o(); }
-}
-"#,
-            expect![[r#"
-                *foo*
-
-                ```rust
-                test
-                ```
-
-                ```rust
-                fn foo()
-                ```
-            "#]],
-        );
-    }
-
-    #[test]
-    fn test_hover_through_expr_in_macro() {
-        check(
-            r#"
-macro_rules! id { ($($tt:tt)*) => { $($tt)* } }
-fn foo(bar:u32) { let a = id!(ba$0r); }
-"#,
-            expect![[r#"
-                *bar*
-
-                ```rust
-                bar: u32
-                ```
-            "#]],
-        );
-    }
-
-    #[test]
-    fn test_hover_through_expr_in_macro_recursive() {
-        check(
-            r#"
-macro_rules! id_deep { ($($tt:tt)*) => { $($tt)* } }
-macro_rules! id { ($($tt:tt)*) => { id_deep!($($tt)*) } }
-fn foo(bar:u32) { let a = id!(ba$0r); }
-"#,
-            expect![[r#"
-                *bar*
-
-                ```rust
-                bar: u32
-                ```
-            "#]],
-        );
-    }
-
-    #[test]
-    fn test_hover_through_func_in_macro_recursive() {
-        check(
-            r#"
-macro_rules! id_deep { ($($tt:tt)*) => { $($tt)* } }
-macro_rules! id { ($($tt:tt)*) => { id_deep!($($tt)*) } }
-fn bar() -> u32 { 0 }
-fn foo() { let a = id!([0u32, bar($0)] ); }
-"#,
-            expect![[r#"
-                *bar()*
-                ```rust
-                u32
-                ```
-            "#]],
-        );
-    }
-
-    #[test]
-    fn test_hover_through_literal_string_in_macro() {
-        check(
-            r#"
-macro_rules! arr { ($($tt:tt)*) => { [$($tt)*)] } }
-fn foo() {
-    let mastered_for_itunes = "";
-    let _ = arr!("Tr$0acks", &mastered_for_itunes);
-}
-"#,
-            expect![[r#"
-                *"Tracks"*
-                ```rust
-                &str
-                ```
-            "#]],
-        );
-    }
-
-    #[test]
-    fn test_hover_through_assert_macro() {
-        check(
-            r#"
-#[rustc_builtin_macro]
-macro_rules! assert {}
-
-fn bar() -> bool { true }
-fn foo() {
-    assert!(ba$0r());
-}
-"#,
-            expect![[r#"
-                *bar*
-
-                ```rust
-                test
-                ```
-
-                ```rust
-                fn bar() -> bool
-                ```
-            "#]],
-        );
-    }
-
-    #[test]
-    fn test_hover_through_literal_string_in_builtin_macro() {
-        check_hover_no_result(
-            r#"
-            #[rustc_builtin_macro]
-            macro_rules! format {}
-
-            fn foo() {
-                format!("hel$0lo {}", 0);
-            }
-"#,
-        );
-    }
-
-    #[test]
-    fn test_hover_non_ascii_space_doc() {
-        check(
-            "
-/// <- `\u{3000}` here
-fn foo() { }
-
-fn bar() { fo$0o(); }
-",
-            expect![[r#"
-                *foo*
-
-                ```rust
-                test
-                ```
-
-                ```rust
-                fn foo()
-                ```
-
-                ---
-
-                \<- ` ` here
-            "#]],
-        );
-    }
-
-    #[test]
-    fn test_hover_function_show_qualifiers() {
-        check(
-            r#"async fn foo$0() {}"#,
-            expect![[r#"
-                *foo*
-
-                ```rust
-                test
-                ```
-
-                ```rust
-                async fn foo()
-                ```
-            "#]],
-        );
-        check(
-            r#"pub const unsafe fn foo$0() {}"#,
-            expect![[r#"
-                *foo*
-
-                ```rust
-                test
-                ```
-
-                ```rust
-                pub const unsafe fn foo()
-                ```
-            "#]],
-        );
-        // Top level `pub(crate)` will be displayed as no visibility.
-        check(
-            r#"mod m { pub(crate) async unsafe extern "C" fn foo$0() {} }"#,
-            expect![[r#"
-                *foo*
-
-                ```rust
-                test::m
-                ```
-
-                ```rust
-                pub(crate) async unsafe extern "C" fn foo()
-                ```
-            "#]],
-        );
-    }
-
-    #[test]
-    fn test_hover_trait_show_qualifiers() {
-        check_actions(
-            r"unsafe trait foo$0() {}",
-            expect![[r#"
-                [
-                    Implementation(
-                        FilePosition {
-                            file_id: FileId(
-                                0,
-                            ),
-                            offset: 13,
-                        },
-                    ),
-                ]
-            "#]],
-        );
-    }
-
-    #[test]
-    fn test_hover_extern_crate() {
-        check(
-            r#"
-//- /main.rs crate:main deps:std
-extern crate st$0d;
-//- /std/lib.rs crate:std
-//! Standard library for this test
-//!
-//! Printed?
-//! abc123
-"#,
-            expect![[r#"
-                *std*
-
-                ```rust
-                extern crate std
-                ```
-
-                ---
-
-                Standard library for this test
-
-                Printed?
-                abc123
-            "#]],
-        );
-        check(
-            r#"
-//- /main.rs crate:main deps:std
-extern crate std as ab$0c;
-//- /std/lib.rs crate:std
-//! Standard library for this test
-//!
-//! Printed?
-//! abc123
-"#,
-            expect![[r#"
-                *abc*
-
-                ```rust
-                extern crate std
-                ```
-
-                ---
-
-                Standard library for this test
-
-                Printed?
-                abc123
-            "#]],
-        );
-    }
-
-    #[test]
-    fn test_hover_mod_with_same_name_as_function() {
-        check(
-            r#"
-use self::m$0y::Bar;
-mod my { pub struct Bar; }
-
-fn my() {}
-"#,
-            expect![[r#"
-                *my*
-
-                ```rust
-                test
-                ```
-
-                ```rust
-                mod my
-                ```
-            "#]],
-        );
-    }
-
-    #[test]
-    fn test_hover_struct_doc_comment() {
-        check(
-            r#"
-/// This is an example
-/// multiline doc
-///
-/// # Example
-///
-/// ```
-/// let five = 5;
-///
-/// assert_eq!(6, my_crate::add_one(5));
-/// ```
-struct Bar;
-
-fn foo() { let bar = Ba$0r; }
-"#,
-            expect![[r##"
-                *Bar*
-
-                ```rust
-                test
-                ```
-
-                ```rust
-                struct Bar
-                ```
-
-                ---
-
-                This is an example
-                multiline doc
-
-                # Example
-
-                ```
-                let five = 5;
-
-                assert_eq!(6, my_crate::add_one(5));
-                ```
-            "##]],
-        );
-    }
-
-    #[test]
-    fn test_hover_struct_doc_attr() {
-        check(
-            r#"
-#[doc = "bar docs"]
-struct Bar;
-
-fn foo() { let bar = Ba$0r; }
-"#,
-            expect![[r#"
-                *Bar*
-
-                ```rust
-                test
-                ```
-
-                ```rust
-                struct Bar
-                ```
-
-                ---
-
-                bar docs
-            "#]],
-        );
-    }
-
-    #[test]
-    fn test_hover_struct_doc_attr_multiple_and_mixed() {
-        check(
-            r#"
-/// bar docs 0
-#[doc = "bar docs 1"]
-#[doc = "bar docs 2"]
-struct Bar;
-
-fn foo() { let bar = Ba$0r; }
-"#,
-            expect![[r#"
-                *Bar*
-
-                ```rust
-                test
-                ```
-
-                ```rust
-                struct Bar
-                ```
-
-                ---
-
-                bar docs 0
-                bar docs 1
-                bar docs 2
-            "#]],
-        );
-    }
-
-    #[test]
-    fn test_hover_external_url() {
-        check(
-            r#"
-pub struct Foo;
-/// [external](https://www.google.com)
-pub struct B$0ar
-"#,
-            expect![[r#"
-                *Bar*
-
-                ```rust
-                test
-                ```
-
-                ```rust
-                pub struct Bar
-                ```
-
-                ---
-
-                [external](https://www.google.com)
-            "#]],
-        );
-    }
-
-    // Check that we don't rewrite links which we can't identify
-    #[test]
-    fn test_hover_unknown_target() {
-        check(
-            r#"
-pub struct Foo;
-/// [baz](Baz)
-pub struct B$0ar
-"#,
-            expect![[r#"
-                *Bar*
-
-                ```rust
-                test
-                ```
-
-                ```rust
-                pub struct Bar
-                ```
-
-                ---
-
-                [baz](Baz)
-            "#]],
-        );
-    }
-
-    #[test]
-    fn test_hover_no_links() {
-        check_hover_no_links(
-            r#"
-/// Test cases:
-/// case 1.  bare URL: https://www.example.com/
-/// case 2.  inline URL with title: [example](https://www.example.com/)
-/// case 3.  code reference: [`Result`]
-/// case 4.  code reference but miss footnote: [`String`]
-/// case 5.  autolink: <http://www.example.com/>
-/// case 6.  email address: <test@example.com>
-/// case 7.  reference: [example][example]
-/// case 8.  collapsed link: [example][]
-/// case 9.  shortcut link: [example]
-/// case 10. inline without URL: [example]()
-/// case 11. reference: [foo][foo]
-/// case 12. reference: [foo][bar]
-/// case 13. collapsed link: [foo][]
-/// case 14. shortcut link: [foo]
-/// case 15. inline without URL: [foo]()
-/// case 16. just escaped text: \[foo]
-/// case 17. inline link: [Foo](foo::Foo)
-///
-/// [`Result`]: ../../std/result/enum.Result.html
-/// [^example]: https://www.example.com/
-pub fn fo$0o() {}
-"#,
-            expect![[r#"
-                *foo*
-
-                ```rust
-                test
-                ```
-
-                ```rust
-                pub fn foo()
-                ```
-
-                ---
-
-                Test cases:
-                case 1.  bare URL: https://www.example.com/
-                case 2.  inline URL with title: [example](https://www.example.com/)
-                case 3.  code reference: `Result`
-                case 4.  code reference but miss footnote: `String`
-                case 5.  autolink: http://www.example.com/
-                case 6.  email address: test@example.com
-                case 7.  reference: example
-                case 8.  collapsed link: example
-                case 9.  shortcut link: example
-                case 10. inline without URL: example
-                case 11. reference: foo
-                case 12. reference: foo
-                case 13. collapsed link: foo
-                case 14. shortcut link: foo
-                case 15. inline without URL: foo
-                case 16. just escaped text: \[foo\]
-                case 17. inline link: Foo
-
-                [^example]: https://www.example.com/
-            "#]],
-        );
-    }
-
-    #[test]
-    fn test_hover_macro_generated_struct_fn_doc_comment() {
-        cov_mark::check!(hover_macro_generated_struct_fn_doc_comment);
-
-        check(
-            r#"
-macro_rules! bar {
-    () => {
-        struct Bar;
-        impl Bar {
-            /// Do the foo
-            fn foo(&self) {}
-        }
-    }
-}
-
-bar!();
-
-fn foo() { let bar = Bar; bar.fo$0o(); }
-"#,
-            expect![[r#"
-                *foo*
-
-                ```rust
-                test::Bar
-                ```
-
-                ```rust
-                fn foo(&self)
-                ```
-
-                ---
-
-                Do the foo
-            "#]],
-        );
-    }
-
-    #[test]
-    fn test_hover_macro_generated_struct_fn_doc_attr() {
-        cov_mark::check!(hover_macro_generated_struct_fn_doc_attr);
-
-        check(
-            r#"
-macro_rules! bar {
-    () => {
-        struct Bar;
-        impl Bar {
-            #[doc = "Do the foo"]
-            fn foo(&self) {}
-        }
-    }
-}
-
-bar!();
-
-fn foo() { let bar = Bar; bar.fo$0o(); }
-"#,
-            expect![[r#"
-                *foo*
-
-                ```rust
-                test::Bar
-                ```
-
-                ```rust
-                fn foo(&self)
-                ```
-
-                ---
-
-                Do the foo
-            "#]],
-        );
-    }
-
-    #[test]
-    fn test_hover_trait_has_impl_action() {
-        check_actions(
-            r#"trait foo$0() {}"#,
-            expect![[r#"
-                [
-                    Implementation(
-                        FilePosition {
-                            file_id: FileId(
-                                0,
-                            ),
-                            offset: 6,
-                        },
-                    ),
-                ]
-            "#]],
-        );
-    }
-
-    #[test]
-    fn test_hover_struct_has_impl_action() {
-        check_actions(
-            r"struct foo$0() {}",
-            expect![[r#"
-                [
-                    Implementation(
-                        FilePosition {
-                            file_id: FileId(
-                                0,
-                            ),
-                            offset: 7,
-                        },
-                    ),
-                ]
-            "#]],
-        );
-    }
-
-    #[test]
-    fn test_hover_union_has_impl_action() {
-        check_actions(
-            r#"union foo$0() {}"#,
-            expect![[r#"
-                [
-                    Implementation(
-                        FilePosition {
-                            file_id: FileId(
-                                0,
-                            ),
-                            offset: 6,
-                        },
-                    ),
-                ]
-            "#]],
-        );
-    }
-
-    #[test]
-    fn test_hover_enum_has_impl_action() {
-        check_actions(
-            r"enum foo$0() { A, B }",
-            expect![[r#"
-                [
-                    Implementation(
-                        FilePosition {
-                            file_id: FileId(
-                                0,
-                            ),
-                            offset: 5,
-                        },
-                    ),
-                ]
-            "#]],
-        );
-    }
-
-    #[test]
-    fn test_hover_self_has_impl_action() {
-        check_actions(
-            r#"struct foo where Self$0:;"#,
-            expect![[r#"
-                [
-                    Implementation(
-                        FilePosition {
-                            file_id: FileId(
-                                0,
-                            ),
-                            offset: 7,
-                        },
-                    ),
-                ]
-            "#]],
-        );
-    }
-
-    #[test]
-    fn test_hover_test_has_action() {
-        check_actions(
-            r#"
-#[test]
-fn foo_$0test() {}
-"#,
-            expect![[r#"
-                [
-                    Reference(
-                        FilePosition {
-                            file_id: FileId(
-                                0,
-                            ),
-                            offset: 11,
-                        },
-                    ),
-                    Runnable(
-                        Runnable {
-                            use_name_in_title: false,
-                            nav: NavigationTarget {
-                                file_id: FileId(
-                                    0,
-                                ),
-                                full_range: 0..24,
-                                focus_range: 11..19,
-                                name: "foo_test",
-                                kind: Function,
-                            },
-                            kind: Test {
-                                test_id: Path(
-                                    "foo_test",
-                                ),
-                                attr: TestAttr {
-                                    ignore: false,
-                                },
-                            },
-                            cfg: None,
-                        },
-                    ),
-                ]
-            "#]],
-        );
-    }
-
-    #[test]
-    fn test_hover_test_mod_has_action() {
-        check_actions(
-            r#"
-mod tests$0 {
-    #[test]
-    fn foo_test() {}
-}
-"#,
-            expect![[r#"
-                [
-                    Runnable(
-                        Runnable {
-                            use_name_in_title: false,
-                            nav: NavigationTarget {
-                                file_id: FileId(
-                                    0,
-                                ),
-                                full_range: 0..46,
-                                focus_range: 4..9,
-                                name: "tests",
-                                kind: Module,
-                                description: "mod tests",
-                            },
-                            kind: TestMod {
-                                path: "tests",
-                            },
-                            cfg: None,
-                        },
-                    ),
-                ]
-            "#]],
-        );
-    }
-
-    #[test]
-    fn test_hover_struct_has_goto_type_action() {
-        check_actions(
-            r#"
-struct S{ f1: u32 }
-
-fn main() { let s$0t = S{ f1:0 }; }
-"#,
-            expect![[r#"
-                [
-                    GoToType(
-                        [
-                            HoverGotoTypeData {
-                                mod_path: "test::S",
-                                nav: NavigationTarget {
-                                    file_id: FileId(
-                                        0,
-                                    ),
-                                    full_range: 0..19,
-                                    focus_range: 7..8,
-                                    name: "S",
-                                    kind: Struct,
-                                    description: "struct S",
-                                },
-                            },
-                        ],
-                    ),
-                ]
-            "#]],
-        );
-    }
-
-    #[test]
-    fn test_hover_generic_struct_has_goto_type_actions() {
-        check_actions(
-            r#"
-struct Arg(u32);
-struct S<T>{ f1: T }
-
-fn main() { let s$0t = S{ f1:Arg(0) }; }
-"#,
-            expect![[r#"
-                [
-                    GoToType(
-                        [
-                            HoverGotoTypeData {
-                                mod_path: "test::S",
-                                nav: NavigationTarget {
-                                    file_id: FileId(
-                                        0,
-                                    ),
-                                    full_range: 17..37,
-                                    focus_range: 24..25,
-                                    name: "S",
-                                    kind: Struct,
-                                    description: "struct S<T>",
-                                },
-                            },
-                            HoverGotoTypeData {
-                                mod_path: "test::Arg",
-                                nav: NavigationTarget {
-                                    file_id: FileId(
-                                        0,
-                                    ),
-                                    full_range: 0..16,
-                                    focus_range: 7..10,
-                                    name: "Arg",
-                                    kind: Struct,
-                                    description: "struct Arg",
-                                },
-                            },
-                        ],
-                    ),
-                ]
-            "#]],
-        );
-    }
-
-    #[test]
-    fn test_hover_generic_struct_has_flattened_goto_type_actions() {
-        check_actions(
-            r#"
-struct Arg(u32);
-struct S<T>{ f1: T }
-
-fn main() { let s$0t = S{ f1: S{ f1: Arg(0) } }; }
-"#,
-            expect![[r#"
-                [
-                    GoToType(
-                        [
-                            HoverGotoTypeData {
-                                mod_path: "test::S",
-                                nav: NavigationTarget {
-                                    file_id: FileId(
-                                        0,
-                                    ),
-                                    full_range: 17..37,
-                                    focus_range: 24..25,
-                                    name: "S",
-                                    kind: Struct,
-                                    description: "struct S<T>",
-                                },
-                            },
-                            HoverGotoTypeData {
-                                mod_path: "test::Arg",
-                                nav: NavigationTarget {
-                                    file_id: FileId(
-                                        0,
-                                    ),
-                                    full_range: 0..16,
-                                    focus_range: 7..10,
-                                    name: "Arg",
-                                    kind: Struct,
-                                    description: "struct Arg",
-                                },
-                            },
-                        ],
-                    ),
-                ]
-            "#]],
-        );
-    }
-
-    #[test]
-    fn test_hover_tuple_has_goto_type_actions() {
-        check_actions(
-            r#"
-struct A(u32);
-struct B(u32);
-mod M {
-    pub struct C(u32);
-}
-
-fn main() { let s$0t = (A(1), B(2), M::C(3) ); }
-"#,
-            expect![[r#"
-                [
-                    GoToType(
-                        [
-                            HoverGotoTypeData {
-                                mod_path: "test::A",
-                                nav: NavigationTarget {
-                                    file_id: FileId(
-                                        0,
-                                    ),
-                                    full_range: 0..14,
-                                    focus_range: 7..8,
-                                    name: "A",
-                                    kind: Struct,
-                                    description: "struct A",
-                                },
-                            },
-                            HoverGotoTypeData {
-                                mod_path: "test::B",
-                                nav: NavigationTarget {
-                                    file_id: FileId(
-                                        0,
-                                    ),
-                                    full_range: 15..29,
-                                    focus_range: 22..23,
-                                    name: "B",
-                                    kind: Struct,
-                                    description: "struct B",
-                                },
-                            },
-                            HoverGotoTypeData {
-                                mod_path: "test::M::C",
-                                nav: NavigationTarget {
-                                    file_id: FileId(
-                                        0,
-                                    ),
-                                    full_range: 42..60,
-                                    focus_range: 53..54,
-                                    name: "C",
-                                    kind: Struct,
-                                    description: "pub struct C",
-                                },
-                            },
-                        ],
-                    ),
-                ]
-            "#]],
-        );
-    }
-
-    #[test]
-    fn test_hover_return_impl_trait_has_goto_type_action() {
-        check_actions(
-            r#"
-trait Foo {}
-fn foo() -> impl Foo {}
-
-fn main() { let s$0t = foo(); }
-"#,
-            expect![[r#"
-                [
-                    GoToType(
-                        [
-                            HoverGotoTypeData {
-                                mod_path: "test::Foo",
-                                nav: NavigationTarget {
-                                    file_id: FileId(
-                                        0,
-                                    ),
-                                    full_range: 0..12,
-                                    focus_range: 6..9,
-                                    name: "Foo",
-                                    kind: Trait,
-                                    description: "trait Foo",
-                                },
-                            },
-                        ],
-                    ),
-                ]
-            "#]],
-        );
-    }
-
-    #[test]
-    fn test_hover_generic_return_impl_trait_has_goto_type_action() {
-        check_actions(
-            r#"
-trait Foo<T> {}
-struct S;
-fn foo() -> impl Foo<S> {}
-
-fn main() { let s$0t = foo(); }
-"#,
-            expect![[r#"
-                [
-                    GoToType(
-                        [
-                            HoverGotoTypeData {
-                                mod_path: "test::Foo",
-                                nav: NavigationTarget {
-                                    file_id: FileId(
-                                        0,
-                                    ),
-                                    full_range: 0..15,
-                                    focus_range: 6..9,
-                                    name: "Foo",
-                                    kind: Trait,
-                                    description: "trait Foo<T>",
-                                },
-                            },
-                            HoverGotoTypeData {
-                                mod_path: "test::S",
-                                nav: NavigationTarget {
-                                    file_id: FileId(
-                                        0,
-                                    ),
-                                    full_range: 16..25,
-                                    focus_range: 23..24,
-                                    name: "S",
-                                    kind: Struct,
-                                    description: "struct S",
-                                },
-                            },
-                        ],
-                    ),
-                ]
-            "#]],
-        );
-    }
-
-    #[test]
-    fn test_hover_return_impl_traits_has_goto_type_action() {
-        check_actions(
-            r#"
-trait Foo {}
-trait Bar {}
-fn foo() -> impl Foo + Bar {}
-
-fn main() { let s$0t = foo(); }
-"#,
-            expect![[r#"
-                [
-                    GoToType(
-                        [
-                            HoverGotoTypeData {
-                                mod_path: "test::Foo",
-                                nav: NavigationTarget {
-                                    file_id: FileId(
-                                        0,
-                                    ),
-                                    full_range: 0..12,
-                                    focus_range: 6..9,
-                                    name: "Foo",
-                                    kind: Trait,
-                                    description: "trait Foo",
-                                },
-                            },
-                            HoverGotoTypeData {
-                                mod_path: "test::Bar",
-                                nav: NavigationTarget {
-                                    file_id: FileId(
-                                        0,
-                                    ),
-                                    full_range: 13..25,
-                                    focus_range: 19..22,
-                                    name: "Bar",
-                                    kind: Trait,
-                                    description: "trait Bar",
-                                },
-                            },
-                        ],
-                    ),
-                ]
-            "#]],
-        );
-    }
-
-    #[test]
-    fn test_hover_generic_return_impl_traits_has_goto_type_action() {
-        check_actions(
-            r#"
-trait Foo<T> {}
-trait Bar<T> {}
-struct S1 {}
-struct S2 {}
-
-fn foo() -> impl Foo<S1> + Bar<S2> {}
-
-fn main() { let s$0t = foo(); }
-"#,
-            expect![[r#"
-                [
-                    GoToType(
-                        [
-                            HoverGotoTypeData {
-                                mod_path: "test::Foo",
-                                nav: NavigationTarget {
-                                    file_id: FileId(
-                                        0,
-                                    ),
-                                    full_range: 0..15,
-                                    focus_range: 6..9,
-                                    name: "Foo",
-                                    kind: Trait,
-                                    description: "trait Foo<T>",
-                                },
-                            },
-                            HoverGotoTypeData {
-                                mod_path: "test::Bar",
-                                nav: NavigationTarget {
-                                    file_id: FileId(
-                                        0,
-                                    ),
-                                    full_range: 16..31,
-                                    focus_range: 22..25,
-                                    name: "Bar",
-                                    kind: Trait,
-                                    description: "trait Bar<T>",
-                                },
-                            },
-                            HoverGotoTypeData {
-                                mod_path: "test::S1",
-                                nav: NavigationTarget {
-                                    file_id: FileId(
-                                        0,
-                                    ),
-                                    full_range: 32..44,
-                                    focus_range: 39..41,
-                                    name: "S1",
-                                    kind: Struct,
-                                    description: "struct S1",
-                                },
-                            },
-                            HoverGotoTypeData {
-                                mod_path: "test::S2",
-                                nav: NavigationTarget {
-                                    file_id: FileId(
-                                        0,
-                                    ),
-                                    full_range: 45..57,
-                                    focus_range: 52..54,
-                                    name: "S2",
-                                    kind: Struct,
-                                    description: "struct S2",
-                                },
-                            },
-                        ],
-                    ),
-                ]
-            "#]],
-        );
-    }
-
-    #[test]
-    fn test_hover_arg_impl_trait_has_goto_type_action() {
-        check_actions(
-            r#"
-trait Foo {}
-fn foo(ar$0g: &impl Foo) {}
-"#,
-            expect![[r#"
-                [
-                    GoToType(
-                        [
-                            HoverGotoTypeData {
-                                mod_path: "test::Foo",
-                                nav: NavigationTarget {
-                                    file_id: FileId(
-                                        0,
-                                    ),
-                                    full_range: 0..12,
-                                    focus_range: 6..9,
-                                    name: "Foo",
-                                    kind: Trait,
-                                    description: "trait Foo",
-                                },
-                            },
-                        ],
-                    ),
-                ]
-            "#]],
-        );
-    }
-
-    #[test]
-    fn test_hover_arg_impl_traits_has_goto_type_action() {
-        check_actions(
-            r#"
-trait Foo {}
-trait Bar<T> {}
-struct S{}
-
-fn foo(ar$0g: &impl Foo + Bar<S>) {}
-"#,
-            expect![[r#"
-                [
-                    GoToType(
-                        [
-                            HoverGotoTypeData {
-                                mod_path: "test::Foo",
-                                nav: NavigationTarget {
-                                    file_id: FileId(
-                                        0,
-                                    ),
-                                    full_range: 0..12,
-                                    focus_range: 6..9,
-                                    name: "Foo",
-                                    kind: Trait,
-                                    description: "trait Foo",
-                                },
-                            },
-                            HoverGotoTypeData {
-                                mod_path: "test::Bar",
-                                nav: NavigationTarget {
-                                    file_id: FileId(
-                                        0,
-                                    ),
-                                    full_range: 13..28,
-                                    focus_range: 19..22,
-                                    name: "Bar",
-                                    kind: Trait,
-                                    description: "trait Bar<T>",
-                                },
-                            },
-                            HoverGotoTypeData {
-                                mod_path: "test::S",
-                                nav: NavigationTarget {
-                                    file_id: FileId(
-                                        0,
-                                    ),
-                                    full_range: 29..39,
-                                    focus_range: 36..37,
-                                    name: "S",
-                                    kind: Struct,
-                                    description: "struct S",
-                                },
-                            },
-                        ],
-                    ),
-                ]
-            "#]],
-        );
-    }
-
-    #[test]
-    fn test_hover_async_block_impl_trait_has_goto_type_action() {
-        check_actions(
-            r#"
-//- minicore: future
-struct S;
-fn foo() {
-    let fo$0o = async { S };
-}
-"#,
-            expect![[r#"
-                [
-                    GoToType(
-                        [
-                            HoverGotoTypeData {
-                                mod_path: "core::future::Future",
-                                nav: NavigationTarget {
-                                    file_id: FileId(
-                                        1,
-                                    ),
-                                    full_range: 254..436,
-                                    focus_range: 293..299,
-                                    name: "Future",
-                                    kind: Trait,
-                                    description: "pub trait Future",
-                                },
-                            },
-                            HoverGotoTypeData {
-                                mod_path: "test::S",
-                                nav: NavigationTarget {
-                                    file_id: FileId(
-                                        0,
-                                    ),
-                                    full_range: 0..9,
-                                    focus_range: 7..8,
-                                    name: "S",
-                                    kind: Struct,
-                                    description: "struct S",
-                                },
-                            },
-                        ],
-                    ),
-                ]
-            "#]],
-        );
-    }
-
-    #[test]
-    fn test_hover_arg_generic_impl_trait_has_goto_type_action() {
-        check_actions(
-            r#"
-trait Foo<T> {}
-struct S {}
-fn foo(ar$0g: &impl Foo<S>) {}
-"#,
-            expect![[r#"
-                [
-                    GoToType(
-                        [
-                            HoverGotoTypeData {
-                                mod_path: "test::Foo",
-                                nav: NavigationTarget {
-                                    file_id: FileId(
-                                        0,
-                                    ),
-                                    full_range: 0..15,
-                                    focus_range: 6..9,
-                                    name: "Foo",
-                                    kind: Trait,
-                                    description: "trait Foo<T>",
-                                },
-                            },
-                            HoverGotoTypeData {
-                                mod_path: "test::S",
-                                nav: NavigationTarget {
-                                    file_id: FileId(
-                                        0,
-                                    ),
-                                    full_range: 16..27,
-                                    focus_range: 23..24,
-                                    name: "S",
-                                    kind: Struct,
-                                    description: "struct S",
-                                },
-                            },
-                        ],
-                    ),
-                ]
-            "#]],
-        );
-    }
-
-    #[test]
-    fn test_hover_dyn_return_has_goto_type_action() {
-        check_actions(
-            r#"
-trait Foo {}
-struct S;
-impl Foo for S {}
-
-struct B<T>{}
-fn foo() -> B<dyn Foo> {}
-
-fn main() { let s$0t = foo(); }
-"#,
-            expect![[r#"
-                [
-                    GoToType(
-                        [
-                            HoverGotoTypeData {
-                                mod_path: "test::B",
-                                nav: NavigationTarget {
-                                    file_id: FileId(
-                                        0,
-                                    ),
-                                    full_range: 42..55,
-                                    focus_range: 49..50,
-                                    name: "B",
-                                    kind: Struct,
-                                    description: "struct B<T>",
-                                },
-                            },
-                            HoverGotoTypeData {
-                                mod_path: "test::Foo",
-                                nav: NavigationTarget {
-                                    file_id: FileId(
-                                        0,
-                                    ),
-                                    full_range: 0..12,
-                                    focus_range: 6..9,
-                                    name: "Foo",
-                                    kind: Trait,
-                                    description: "trait Foo",
-                                },
-                            },
-                        ],
-                    ),
-                ]
-            "#]],
-        );
-    }
-
-    #[test]
-    fn test_hover_dyn_arg_has_goto_type_action() {
-        check_actions(
-            r#"
-trait Foo {}
-fn foo(ar$0g: &dyn Foo) {}
-"#,
-            expect![[r#"
-                [
-                    GoToType(
-                        [
-                            HoverGotoTypeData {
-                                mod_path: "test::Foo",
-                                nav: NavigationTarget {
-                                    file_id: FileId(
-                                        0,
-                                    ),
-                                    full_range: 0..12,
-                                    focus_range: 6..9,
-                                    name: "Foo",
-                                    kind: Trait,
-                                    description: "trait Foo",
-                                },
-                            },
-                        ],
-                    ),
-                ]
-            "#]],
-        );
-    }
-
-    #[test]
-    fn test_hover_generic_dyn_arg_has_goto_type_action() {
-        check_actions(
-            r#"
-trait Foo<T> {}
-struct S {}
-fn foo(ar$0g: &dyn Foo<S>) {}
-"#,
-            expect![[r#"
-                [
-                    GoToType(
-                        [
-                            HoverGotoTypeData {
-                                mod_path: "test::Foo",
-                                nav: NavigationTarget {
-                                    file_id: FileId(
-                                        0,
-                                    ),
-                                    full_range: 0..15,
-                                    focus_range: 6..9,
-                                    name: "Foo",
-                                    kind: Trait,
-                                    description: "trait Foo<T>",
-                                },
-                            },
-                            HoverGotoTypeData {
-                                mod_path: "test::S",
-                                nav: NavigationTarget {
-                                    file_id: FileId(
-                                        0,
-                                    ),
-                                    full_range: 16..27,
-                                    focus_range: 23..24,
-                                    name: "S",
-                                    kind: Struct,
-                                    description: "struct S",
-                                },
-                            },
-                        ],
-                    ),
-                ]
-            "#]],
-        );
-    }
-
-    #[test]
-    fn test_hover_goto_type_action_links_order() {
-        check_actions(
-            r#"
-trait ImplTrait<T> {}
-trait DynTrait<T> {}
-struct B<T> {}
-struct S {}
-
-fn foo(a$0rg: &impl ImplTrait<B<dyn DynTrait<B<S>>>>) {}
-"#,
-            expect![[r#"
-                [
-                    GoToType(
-                        [
-                            HoverGotoTypeData {
-                                mod_path: "test::ImplTrait",
-                                nav: NavigationTarget {
-                                    file_id: FileId(
-                                        0,
-                                    ),
-                                    full_range: 0..21,
-                                    focus_range: 6..15,
-                                    name: "ImplTrait",
-                                    kind: Trait,
-                                    description: "trait ImplTrait<T>",
-                                },
-                            },
-                            HoverGotoTypeData {
-                                mod_path: "test::B",
-                                nav: NavigationTarget {
-                                    file_id: FileId(
-                                        0,
-                                    ),
-                                    full_range: 43..57,
-                                    focus_range: 50..51,
-                                    name: "B",
-                                    kind: Struct,
-                                    description: "struct B<T>",
-                                },
-                            },
-                            HoverGotoTypeData {
-                                mod_path: "test::DynTrait",
-                                nav: NavigationTarget {
-                                    file_id: FileId(
-                                        0,
-                                    ),
-                                    full_range: 22..42,
-                                    focus_range: 28..36,
-                                    name: "DynTrait",
-                                    kind: Trait,
-                                    description: "trait DynTrait<T>",
-                                },
-                            },
-                            HoverGotoTypeData {
-                                mod_path: "test::S",
-                                nav: NavigationTarget {
-                                    file_id: FileId(
-                                        0,
-                                    ),
-                                    full_range: 58..69,
-                                    focus_range: 65..66,
-                                    name: "S",
-                                    kind: Struct,
-                                    description: "struct S",
-                                },
-                            },
-                        ],
-                    ),
-                ]
-            "#]],
-        );
-    }
-
-    #[test]
-    fn test_hover_associated_type_has_goto_type_action() {
-        check_actions(
-            r#"
-trait Foo {
-    type Item;
-    fn get(self) -> Self::Item {}
-}
-
-struct Bar{}
-struct S{}
-
-impl Foo for S { type Item = Bar; }
-
-fn test() -> impl Foo { S {} }
-
-fn main() { let s$0t = test().get(); }
-"#,
-            expect![[r#"
-                [
-                    GoToType(
-                        [
-                            HoverGotoTypeData {
-                                mod_path: "test::Foo",
-                                nav: NavigationTarget {
-                                    file_id: FileId(
-                                        0,
-                                    ),
-                                    full_range: 0..62,
-                                    focus_range: 6..9,
-                                    name: "Foo",
-                                    kind: Trait,
-                                    description: "trait Foo",
-                                },
-                            },
-                        ],
-                    ),
-                ]
-            "#]],
-        );
-    }
-
-    #[test]
-    fn test_hover_const_param_has_goto_type_action() {
-        check_actions(
-            r#"
-struct Bar;
-struct Foo<const BAR: Bar>;
-
-impl<const BAR: Bar> Foo<BAR$0> {}
-"#,
-            expect![[r#"
-                [
-                    GoToType(
-                        [
-                            HoverGotoTypeData {
-                                mod_path: "test::Bar",
-                                nav: NavigationTarget {
-                                    file_id: FileId(
-                                        0,
-                                    ),
-                                    full_range: 0..11,
-                                    focus_range: 7..10,
-                                    name: "Bar",
-                                    kind: Struct,
-                                    description: "struct Bar",
-                                },
-                            },
-                        ],
-                    ),
-                ]
-            "#]],
-        );
-    }
-
-    #[test]
-    fn test_hover_type_param_has_goto_type_action() {
-        check_actions(
-            r#"
-trait Foo {}
-
-fn foo<T: Foo>(t: T$0){}
-"#,
-            expect![[r#"
-                [
-                    GoToType(
-                        [
-                            HoverGotoTypeData {
-                                mod_path: "test::Foo",
-                                nav: NavigationTarget {
-                                    file_id: FileId(
-                                        0,
-                                    ),
-                                    full_range: 0..12,
-                                    focus_range: 6..9,
-                                    name: "Foo",
-                                    kind: Trait,
-                                    description: "trait Foo",
-                                },
-                            },
-                        ],
-                    ),
-                ]
-            "#]],
-        );
-    }
-
-    #[test]
-    fn test_hover_self_has_go_to_type() {
-        check_actions(
-            r#"
-struct Foo;
-impl Foo {
-    fn foo(&self$0) {}
-}
-"#,
-            expect![[r#"
-                [
-                    GoToType(
-                        [
-                            HoverGotoTypeData {
-                                mod_path: "test::Foo",
-                                nav: NavigationTarget {
-                                    file_id: FileId(
-                                        0,
-                                    ),
-                                    full_range: 0..11,
-                                    focus_range: 7..10,
-                                    name: "Foo",
-                                    kind: Struct,
-                                    description: "struct Foo",
-                                },
-                            },
-                        ],
-                    ),
-                ]
-            "#]],
-        );
-    }
-
-    #[test]
-    fn hover_displays_normalized_crate_names() {
-        check(
-            r#"
-//- /lib.rs crate:name-with-dashes
-pub mod wrapper {
-    pub struct Thing { x: u32 }
-
-    impl Thing {
-        pub fn new() -> Thing { Thing { x: 0 } }
-    }
-}
-
-//- /main.rs crate:main deps:name-with-dashes
-fn main() { let foo_test = name_with_dashes::wrapper::Thing::new$0(); }
-"#,
-            expect![[r#"
-            *new*
-
-            ```rust
-            name_with_dashes::wrapper::Thing
-            ```
-
-            ```rust
-            pub fn new() -> Thing
-            ```
-            "#]],
-        )
-    }
-
-    #[test]
-    fn hover_field_pat_shorthand_ref_match_ergonomics() {
-        check(
-            r#"
-struct S {
-    f: i32,
-}
-
-fn main() {
-    let s = S { f: 0 };
-    let S { f$0 } = &s;
-}
-"#,
-            expect![[r#"
-                *f*
-
-                ```rust
-                f: &i32
-                ```
-            "#]],
-        );
-    }
-
-    #[test]
-    fn hover_self_param_shows_type() {
-        check(
-            r#"
-struct Foo {}
-impl Foo {
-    fn bar(&sel$0f) {}
-}
-"#,
-            expect![[r#"
-                *self*
-
-                ```rust
-                self: &Foo
-                ```
-            "#]],
-        );
-    }
-
-    #[test]
-    fn hover_self_param_shows_type_for_arbitrary_self_type() {
-        check(
-            r#"
-struct Arc<T>(T);
-struct Foo {}
-impl Foo {
-    fn bar(sel$0f: Arc<Foo>) {}
-}
-"#,
-            expect![[r#"
-                *self*
-
-                ```rust
-                self: Arc<Foo>
-                ```
-            "#]],
-        );
-    }
-
-    #[test]
-    fn hover_doc_outer_inner() {
-        check(
-            r#"
-/// Be quick;
-mod Foo$0 {
-    //! time is mana
-
-    /// This comment belongs to the function
-    fn foo() {}
-}
-"#,
-            expect![[r#"
-                *Foo*
-
-                ```rust
-                test
-                ```
-
-                ```rust
-                mod Foo
-                ```
-
-                ---
-
-                Be quick;
-                time is mana
-            "#]],
-        );
-    }
-
-    #[test]
-    fn hover_doc_outer_inner_attribue() {
-        check(
-            r#"
-#[doc = "Be quick;"]
-mod Foo$0 {
-    #![doc = "time is mana"]
-
-    #[doc = "This comment belongs to the function"]
-    fn foo() {}
-}
-"#,
-            expect![[r#"
-                *Foo*
-
-                ```rust
-                test
-                ```
-
-                ```rust
-                mod Foo
-                ```
-
-                ---
-
-                Be quick;
-                time is mana
-            "#]],
-        );
-    }
-
-    #[test]
-    fn hover_doc_block_style_indentend() {
-        check(
-            r#"
-/**
-    foo
-    ```rust
-    let x = 3;
-    ```
-*/
-fn foo$0() {}
-"#,
-            expect![[r#"
-                *foo*
-
-                ```rust
-                test
-                ```
-
-                ```rust
-                fn foo()
-                ```
-
-                ---
-
-                foo
-
-                ```rust
-                let x = 3;
-                ```
-            "#]],
-        );
-    }
-
-    #[test]
-    fn hover_comments_dont_highlight_parent() {
-        cov_mark::check!(no_highlight_on_comment_hover);
-        check_hover_no_result(
-            r#"
-fn no_hover() {
-    // no$0hover
-}
-"#,
-        );
-    }
-
-    #[test]
-    fn hover_label() {
-        check(
-            r#"
-fn foo() {
-    'label$0: loop {}
-}
-"#,
-            expect![[r#"
-            *'label*
-
-            ```rust
-            'label
-            ```
-            "#]],
-        );
-    }
-
-    #[test]
-    fn hover_lifetime() {
-        check(
-            r#"fn foo<'lifetime>(_: &'lifetime$0 ()) {}"#,
-            expect![[r#"
-            *'lifetime*
-
-            ```rust
-            'lifetime
-            ```
-            "#]],
-        );
-    }
-
-    #[test]
-    fn hover_type_param() {
-        check(
-            r#"
-//- minicore: sized
-struct Foo<T>(T);
-trait Copy {}
-trait Clone {}
-impl<T: Copy + Clone> Foo<T$0> where T: Sized {}
-"#,
-            expect![[r#"
-                *T*
-
-                ```rust
-                T: Copy + Clone
-                ```
-            "#]],
-        );
-        check(
-            r#"
-struct Foo<T>(T);
-impl<T> Foo<T$0> {}
-"#,
-            expect![[r#"
-                *T*
-
-                ```rust
-                T
-                ```
-                "#]],
-        );
-        // lifetimes bounds arent being tracked yet
-        check(
-            r#"
-struct Foo<T>(T);
-impl<T: 'static> Foo<T$0> {}
-"#,
-            expect![[r#"
-                *T*
-
-                ```rust
-                T
-                ```
-                "#]],
-        );
-    }
-
-    #[test]
-    fn hover_type_param_not_sized() {
-        check(
-            r#"
-//- minicore: sized
-struct Foo<T>(T);
-trait Copy {}
-trait Clone {}
-impl<T: Copy + Clone> Foo<T$0> where T: ?Sized {}
-"#,
-            expect![[r#"
-                *T*
-
-                ```rust
-                T: Copy + Clone + ?Sized
-                ```
-            "#]],
-        );
-    }
-
-    #[test]
-    fn hover_const_param() {
-        check(
-            r#"
-struct Foo<const LEN: usize>;
-impl<const LEN: usize> Foo<LEN$0> {}
-"#,
-            expect![[r#"
-                *LEN*
-
-                ```rust
-                const LEN: usize
-                ```
-            "#]],
-        );
-    }
-
-    #[test]
-    fn hover_const_pat() {
-        check(
-            r#"
-/// This is a doc
-const FOO: usize = 3;
-fn foo() {
-    match 5 {
-        FOO$0 => (),
-        _ => ()
-    }
-}
-"#,
-            expect![[r#"
-                *FOO*
-
-                ```rust
-                test
-                ```
-
-                ```rust
-                const FOO: usize
-                ```
-
-                ---
-
-                This is a doc
-            "#]],
-        );
-    }
-
-    #[test]
-    fn hover_mod_def() {
-        check(
-            r#"
-//- /main.rs
-mod foo$0;
-//- /foo.rs
-//! For the horde!
-"#,
-            expect![[r#"
-                *foo*
-
-                ```rust
-                test
-                ```
-
-                ```rust
-                mod foo
-                ```
-
-                ---
-
-                For the horde!
-            "#]],
-        );
-    }
-
-    #[test]
-    fn hover_self_in_use() {
-        check(
-            r#"
-//! This should not appear
-mod foo {
-    /// But this should appear
-    pub mod bar {}
-}
-use foo::bar::{self$0};
-"#,
-            expect![[r#"
-                *self*
-
-                ```rust
-                test::foo
-                ```
-
-                ```rust
-                mod bar
-                ```
-
-                ---
-
-                But this should appear
-            "#]],
-        )
-    }
-
-    #[test]
-    fn hover_keyword() {
-        check(
-            r#"
-//- /main.rs crate:main deps:std
-fn f() { retur$0n; }
-//- /libstd.rs crate:std
-/// Docs for return_keyword
-mod return_keyword {}
-"#,
-            expect![[r#"
-                *return*
-
-                ```rust
-                return
-                ```
-
-                ---
-
-                Docs for return_keyword
-            "#]],
-        );
-    }
-
-    #[test]
-    fn hover_builtin() {
-        check(
-            r#"
-//- /main.rs crate:main deps:std
-cosnt _: &str$0 = ""; }
-
-//- /libstd.rs crate:std
-/// Docs for prim_str
-mod prim_str {}
-"#,
-            expect![[r#"
-                *str*
-
-                ```rust
-                str
-                ```
-
-                ---
-
-                Docs for prim_str
-            "#]],
-        );
-    }
-
-    #[test]
-    fn hover_macro_expanded_function() {
-        check(
-            r#"
-struct S<'a, T>(&'a T);
-trait Clone {}
-macro_rules! foo {
-    () => {
-        fn bar<'t, T: Clone + 't>(s: &mut S<'t, T>, t: u32) -> *mut u32 where
-            't: 't + 't,
-            for<'a> T: Clone + 'a
-        { 0 as _ }
-    };
-}
-
-foo!();
-
-fn main() {
-    bar$0;
-}
-"#,
-            expect![[r#"
-                *bar*
-
-                ```rust
-                test
-                ```
-
-                ```rust
-                fn bar<'t, T>(s: &mut S<'t, T>, t: u32) -> *mut u32
-                where
-                    T: Clone + 't,
-                    't: 't + 't,
-                    for<'a> T: Clone + 'a,
-                ```
-            "#]],
-        )
-    }
-
-    #[test]
-    fn hover_intra_doc_links() {
-        check(
-            r#"
-
-pub mod theitem {
-    /// This is the item. Cool!
-    pub struct TheItem;
-}
-
-/// Gives you a [`TheItem$0`].
-///
-/// [`TheItem`]: theitem::TheItem
-pub fn gimme() -> theitem::TheItem {
-    theitem::TheItem
-}
-"#,
-            expect![[r#"
-                *[`TheItem`]*
-
-                ```rust
-                test::theitem
-                ```
-
-                ```rust
-                pub struct TheItem
-                ```
-
-                ---
-
-                This is the item. Cool!
-            "#]],
-        );
-    }
-
-    #[test]
-    fn hover_generic_assoc() {
-        check(
-            r#"
-fn foo<T: A>() where T::Assoc$0: {}
-
-trait A {
-    type Assoc;
-}"#,
-            expect![[r#"
-                *Assoc*
-
-                ```rust
-                test
-                ```
-
-                ```rust
-                type Assoc
-                ```
-            "#]],
-        );
-        check(
-            r#"
-fn foo<T: A>() {
-    let _: <T>::Assoc$0;
-}
-
-trait A {
-    type Assoc;
-}"#,
-            expect![[r#"
-                *Assoc*
-
-                ```rust
-                test
-                ```
-
-                ```rust
-                type Assoc
-                ```
-            "#]],
-        );
-        check(
-            r#"
-trait A where
-    Self::Assoc$0: ,
-{
-    type Assoc;
-}"#,
-            expect![[r#"
-                *Assoc*
-
-                ```rust
-                test
-                ```
-
-                ```rust
-                type Assoc
-                ```
-            "#]],
-        );
-    }
-
-    #[test]
-    fn string_shadowed_with_inner_items() {
-        check(
-            r#"
-//- /main.rs crate:main deps:alloc
-
-/// Custom `String` type.
-struct String;
-
-fn f() {
-    let _: String$0;
-
-    fn inner() {}
-}
-
-//- /alloc.rs crate:alloc
-#[prelude_import]
-pub use string::*;
-
-mod string {
-    /// This is `alloc::String`.
-    pub struct String;
-}
-"#,
-            expect![[r#"
-                *String*
-
-                ```rust
-                main
-                ```
-
-                ```rust
-                struct String
-                ```
-
-                ---
-
-                Custom `String` type.
-            "#]],
-        )
-    }
-
-    #[test]
-    fn function_doesnt_shadow_crate_in_use_tree() {
-        check(
-            r#"
-//- /main.rs crate:main deps:foo
-use foo$0::{foo};
-
-//- /foo.rs crate:foo
-pub fn foo() {}
-"#,
-            expect![[r#"
-                *foo*
-
-                ```rust
-                extern crate foo
-                ```
-            "#]],
-        )
-    }
-
-    #[test]
-    fn hover_feature() {
-        check(
-            r#"#![feature(box_syntax$0)]"#,
-            expect![[r##"
-                *box_syntax*
-                ```
-                box_syntax
-                ```
-                ___
-
-                # `box_syntax`
-
-                The tracking issue for this feature is: [#49733]
-
-                [#49733]: https://github.com/rust-lang/rust/issues/49733
-
-                See also [`box_patterns`](box-patterns.md)
-
-                ------------------------
-
-                Currently the only stable way to create a `Box` is via the `Box::new` method.
-                Also it is not possible in stable Rust to destructure a `Box` in a match
-                pattern. The unstable `box` keyword can be used to create a `Box`. An example
-                usage would be:
-
-                ```rust
-                #![feature(box_syntax)]
-
-                fn main() {
-                    let b = box 5;
-                }
-                ```
-
-            "##]],
-        )
-    }
-
-    #[test]
-    fn hover_lint() {
-        check(
-            r#"#![allow(arithmetic_overflow$0)]"#,
-            expect![[r#"
-                *arithmetic_overflow*
-                ```
-                arithmetic_overflow
-                ```
-                ___
-
-                arithmetic operation overflows
-            "#]],
-        )
-    }
-
-    #[test]
-    fn hover_clippy_lint() {
-        check(
-            r#"#![allow(clippy::almost_swapped$0)]"#,
-            expect![[r#"
-                *almost_swapped*
-                ```
-                clippy::almost_swapped
-                ```
-                ___
-
-                Checks for `foo = bar; bar = foo` sequences.
-            "#]],
-        )
-    }
-
-    #[test]
-    fn hover_attr_path_qualifier() {
-        cov_mark::check!(name_ref_classify_attr_path_qualifier);
-        check(
-            r#"
-//- /foo.rs crate:foo
-
-//- /lib.rs crate:main.rs deps:foo
-#[fo$0o::bar()]
-struct Foo;
-"#,
-            expect![[r#"
-                *foo*
-
-                ```rust
-                extern crate foo
-                ```
-            "#]],
-        )
-    }
-
-    #[test]
-    fn hover_rename() {
-        check(
-            r#"
-use self as foo$0;
-"#,
-            expect![[r#"
-                *foo*
-
-                ```rust
-                extern crate test
-                ```
-            "#]],
-        );
-        check(
-            r#"
-mod bar {}
-use bar::{self as foo$0};
-"#,
-            expect![[r#"
-                *foo*
-
-                ```rust
-                test
-                ```
-
-                ```rust
-                mod bar
-                ```
-            "#]],
-        );
-        check(
-            r#"
-mod bar {
-    use super as foo$0;
-}
-"#,
-            expect![[r#"
-                *foo*
-
-                ```rust
-                extern crate test
-                ```
-            "#]],
-        );
-        check(
-            r#"
-use crate as foo$0;
-"#,
-            expect![[r#"
-                *foo*
-
-                ```rust
-                extern crate test
-                ```
-            "#]],
-        );
-    }
-
-    #[test]
-    fn hover_derive_input() {
-        check(
-            r#"
-#[rustc_builtin_macro]
-pub macro Copy {}
-#[derive(Copy$0)]
-struct Foo;
-"#,
-            expect![[r#"
-                *Copy*
-
-                ```rust
-                test
-                ```
-
-                ```rust
-                pub macro Copy
-                ```
-            "#]],
-        );
-        check(
-            r#"
-mod foo {
-    #[rustc_builtin_macro]
-    pub macro Copy {}
-}
-#[derive(foo::Copy$0)]
-struct Foo;
-"#,
-            expect![[r#"
-                *Copy*
-
-                ```rust
-                test
-                ```
-
-                ```rust
-                pub macro Copy
-                ```
-            "#]],
-        );
-    }
-
-    #[test]
-    fn hover_range_math() {
-        check_hover_range(
-            r#"
-fn f() { let expr = $01 + 2 * 3$0 }
-"#,
-            expect![[r#"
-            ```rust
-            i32
-            ```"#]],
-        );
-
-        check_hover_range(
-            r#"
-fn f() { let expr = 1 $0+ 2 * $03 }
-"#,
-            expect![[r#"
-            ```rust
-            i32
-            ```"#]],
-        );
-
-        check_hover_range(
-            r#"
-fn f() { let expr = 1 + $02 * 3$0 }
-"#,
-            expect![[r#"
-            ```rust
-            i32
-            ```"#]],
-        );
-    }
-
-    #[test]
-    fn hover_range_arrays() {
-        check_hover_range(
-            r#"
-fn f() { let expr = $0[1, 2, 3, 4]$0 }
-"#,
-            expect![[r#"
-            ```rust
-            [i32; 4]
-            ```"#]],
-        );
-
-        check_hover_range(
-            r#"
-fn f() { let expr = [1, 2, $03, 4]$0 }
-"#,
-            expect![[r#"
-            ```rust
-            [i32; 4]
-            ```"#]],
-        );
-
-        check_hover_range(
-            r#"
-fn f() { let expr = [1, 2, $03$0, 4] }
-"#,
-            expect![[r#"
-            ```rust
-            i32
-            ```"#]],
-        );
-    }
-
-    #[test]
-    fn hover_range_functions() {
-        check_hover_range(
-            r#"
-fn f<T>(a: &[T]) { }
-fn b() { $0f$0(&[1, 2, 3, 4, 5]); }
-"#,
-            expect![[r#"
-            ```rust
-            fn f<i32>(&[i32])
-            ```"#]],
-        );
-
-        check_hover_range(
-            r#"
-fn f<T>(a: &[T]) { }
-fn b() { f($0&[1, 2, 3, 4, 5]$0); }
-"#,
-            expect![[r#"
-            ```rust
-            &[i32; 5]
-            ```"#]],
-        );
-    }
-
-    #[test]
-    fn hover_range_shows_nothing_when_invalid() {
-        check_hover_range_no_results(
-            r#"
-fn f<T>(a: &[T]) { }
-fn b()$0 { f(&[1, 2, 3, 4, 5]); }$0
-"#,
-        );
-
-        check_hover_range_no_results(
-            r#"
-fn f<T>$0(a: &[T]) { }
-fn b() { f(&[1, 2, 3,$0 4, 5]); }
-"#,
-        );
-
-        check_hover_range_no_results(
-            r#"
-fn $0f() { let expr = [1, 2, 3, 4]$0 }
-"#,
-        );
-    }
-
-    #[test]
-    fn hover_range_shows_unit_for_statements() {
-        check_hover_range(
-            r#"
-fn f<T>(a: &[T]) { }
-fn b() { $0f(&[1, 2, 3, 4, 5]); }$0
-"#,
-            expect![[r#"
-            ```rust
-            ()
-            ```"#]],
-        );
-
-        check_hover_range(
-            r#"
-fn f() { let expr$0 = $0[1, 2, 3, 4] }
-"#,
-            expect![[r#"
-            ```rust
-            ()
-            ```"#]],
-        );
-    }
-
-    #[test]
-    fn hover_range_for_pat() {
-        check_hover_range(
-            r#"
-fn foo() {
-    let $0x$0 = 0;
-}
-"#,
-            expect![[r#"
-                ```rust
-                i32
-                ```"#]],
-        );
-
-        check_hover_range(
-            r#"
-fn foo() {
-    let $0x$0 = "";
-}
-"#,
-            expect![[r#"
-                ```rust
-                &str
-                ```"#]],
-        );
-    }
-
-    #[test]
-    fn hover_range_shows_coercions_if_applicable_expr() {
-        check_hover_range(
-            r#"
-fn foo() {
-    let x: &u32 = $0&&&&&0$0;
-}
-"#,
-            expect![[r#"
-                ```text
-                Type:       &&&&&u32
-                Coerced to:     &u32
-                ```
-            "#]],
-        );
-        check_hover_range(
-            r#"
-fn foo() {
-    let x: *const u32 = $0&0$0;
-}
-"#,
-            expect![[r#"
-                ```text
-                Type:             &u32
-                Coerced to: *const u32
-                ```
-            "#]],
-        );
-    }
-
-    #[test]
-    fn hover_range_shows_type_actions() {
-        check_actions(
-            r#"
-struct Foo;
-fn foo() {
-    let x: &Foo = $0&&&&&Foo$0;
-}
-"#,
-            expect![[r#"
-                [
-                    GoToType(
-                        [
-                            HoverGotoTypeData {
-                                mod_path: "test::Foo",
-                                nav: NavigationTarget {
-                                    file_id: FileId(
-                                        0,
-                                    ),
-                                    full_range: 0..11,
-                                    focus_range: 7..10,
-                                    name: "Foo",
-                                    kind: Struct,
-                                    description: "struct Foo",
-                                },
-                            },
-                        ],
-                    ),
-                ]
-            "#]],
-        );
-    }
-
-    #[test]
-    fn hover_try_expr_res() {
-        check_hover_range(
-            r#"
-//- minicore:result
-struct FooError;
-
-fn foo() -> Result<(), FooError> {
-    Ok($0Result::<(), FooError>::Ok(())?$0)
-}
-"#,
-            expect![[r#"
-                ```rust
-                ()
-                ```"#]],
-        );
-        check_hover_range(
-            r#"
-//- minicore:result
-struct FooError;
-struct BarError;
-
-fn foo() -> Result<(), FooError> {
-    Ok($0Result::<(), BarError>::Ok(())?$0)
-}
-"#,
-            expect![[r#"
-                ```text
-                Try Error Type: BarError
-                Propagated as:  FooError
-                ```
-            "#]],
-        );
-    }
-
-    #[test]
-    fn hover_try_expr() {
-        check_hover_range(
-            r#"
-struct NotResult<T, U>(T, U);
-struct Short;
-struct Looooong;
-
-fn foo() -> NotResult<(), Looooong> {
-    $0NotResult((), Short)?$0;
-}
-"#,
-            expect![[r#"
-                ```text
-                Try Target Type:    NotResult<(), Short>
-                Propagated as:   NotResult<(), Looooong>
-                ```
-            "#]],
-        );
-        check_hover_range(
-            r#"
-struct NotResult<T, U>(T, U);
-struct Short;
-struct Looooong;
-
-fn foo() -> NotResult<(), Short> {
-    $0NotResult((), Looooong)?$0;
-}
-"#,
-            expect![[r#"
-                ```text
-                Try Target Type: NotResult<(), Looooong>
-                Propagated as:      NotResult<(), Short>
-                ```
-            "#]],
-        );
-    }
-
-    #[test]
-    fn hover_try_expr_option() {
-        cov_mark::check!(hover_try_expr_opt_opt);
-        check_hover_range(
-            r#"
-//- minicore: option, try
-
-fn foo() -> Option<()> {
-    $0Some(0)?$0;
-    None
-}
-"#,
-            expect![[r#"
-                ```rust
-                <Option<i32> as Try>::Output
-                ```"#]],
-        );
-    }
-}
diff --git a/crates/ide/src/hover/render.rs b/crates/ide/src/hover/render.rs
new file mode 100644 (file)
index 0000000..5b516de
--- /dev/null
@@ -0,0 +1,450 @@
+//! Logic for rendering the different hover messages
+use either::Either;
+use hir::{AsAssocItem, HasAttrs, HasSource, HirDisplay, Semantics, TypeInfo};
+use ide_db::{
+    base_db::SourceDatabase,
+    defs::Definition,
+    helpers::{
+        generated_lints::{CLIPPY_LINTS, DEFAULT_LINTS, FEATURES},
+        FamousDefs,
+    },
+    RootDatabase,
+};
+use itertools::Itertools;
+use stdx::format_to;
+use syntax::{
+    algo, ast,
+    display::{fn_as_proc_macro_label, macro_label},
+    match_ast, AstNode, Direction,
+    SyntaxKind::{CONDITION, LET_STMT},
+    SyntaxToken, T,
+};
+
+use crate::{
+    doc_links::{remove_links, rewrite_links},
+    hover::walk_and_push_ty,
+    markdown_remove::remove_markdown,
+    HoverAction, HoverConfig, HoverResult, Markup,
+};
+
+pub(super) fn type_info(
+    sema: &Semantics<RootDatabase>,
+    config: &HoverConfig,
+    expr_or_pat: &Either<ast::Expr, ast::Pat>,
+) -> Option<HoverResult> {
+    let TypeInfo { original, adjusted } = match expr_or_pat {
+        Either::Left(expr) => sema.type_of_expr(expr)?,
+        Either::Right(pat) => sema.type_of_pat(pat)?,
+    };
+
+    let mut res = HoverResult::default();
+    let mut targets: Vec<hir::ModuleDef> = Vec::new();
+    let mut push_new_def = |item: hir::ModuleDef| {
+        if !targets.contains(&item) {
+            targets.push(item);
+        }
+    };
+    walk_and_push_ty(sema.db, &original, &mut push_new_def);
+
+    res.markup = if let Some(adjusted_ty) = adjusted {
+        walk_and_push_ty(sema.db, &adjusted_ty, &mut push_new_def);
+        let original = original.display(sema.db).to_string();
+        let adjusted = adjusted_ty.display(sema.db).to_string();
+        let static_text_diff_len = "Coerced to: ".len() - "Type: ".len();
+        format!(
+            "{bt_start}Type: {:>apad$}\nCoerced to: {:>opad$}\n{bt_end}",
+            original,
+            adjusted,
+            apad = static_text_diff_len + adjusted.len().max(original.len()),
+            opad = original.len(),
+            bt_start = if config.markdown() { "```text\n" } else { "" },
+            bt_end = if config.markdown() { "```\n" } else { "" }
+        )
+        .into()
+    } else {
+        if config.markdown() {
+            Markup::fenced_block(&original.display(sema.db))
+        } else {
+            original.display(sema.db).to_string().into()
+        }
+    };
+    res.actions.push(HoverAction::goto_type_from_targets(sema.db, targets));
+    Some(res)
+}
+
+pub(super) fn try_expr(
+    sema: &Semantics<RootDatabase>,
+    config: &HoverConfig,
+    try_expr: &ast::TryExpr,
+) -> Option<HoverResult> {
+    let inner_ty = sema.type_of_expr(&try_expr.expr()?)?.original;
+    let mut ancestors = try_expr.syntax().ancestors();
+    let mut body_ty = loop {
+        let next = ancestors.next()?;
+        break match_ast! {
+            match next {
+                ast::Fn(fn_) => sema.to_def(&fn_)?.ret_type(sema.db),
+                ast::Item(__) => return None,
+                ast::ClosureExpr(closure) => sema.type_of_expr(&closure.body()?)?.original,
+                ast::EffectExpr(effect) => if matches!(effect.effect(), ast::Effect::Async(_) | ast::Effect::Try(_)| ast::Effect::Const(_)) {
+                    sema.type_of_expr(&effect.block_expr()?.into())?.original
+                } else {
+                    continue;
+                },
+                _ => continue,
+            }
+        };
+    };
+
+    if inner_ty == body_ty {
+        return None;
+    }
+
+    let mut inner_ty = inner_ty;
+    let mut s = "Try Target".to_owned();
+
+    let adts = inner_ty.as_adt().zip(body_ty.as_adt());
+    if let Some((hir::Adt::Enum(inner), hir::Adt::Enum(body))) = adts {
+        let famous_defs = FamousDefs(sema, sema.scope(&try_expr.syntax()).krate());
+        // special case for two options, there is no value in showing them
+        if let Some(option_enum) = famous_defs.core_option_Option() {
+            if inner == option_enum && body == option_enum {
+                cov_mark::hit!(hover_try_expr_opt_opt);
+                return None;
+            }
+        }
+
+        // special case two results to show the error variants only
+        if let Some(result_enum) = famous_defs.core_result_Result() {
+            if inner == result_enum && body == result_enum {
+                let error_type_args =
+                    inner_ty.type_arguments().nth(1).zip(body_ty.type_arguments().nth(1));
+                if let Some((inner, body)) = error_type_args {
+                    inner_ty = inner;
+                    body_ty = body;
+                    s = "Try Error".to_owned();
+                }
+            }
+        }
+    }
+
+    let mut res = HoverResult::default();
+
+    let mut targets: Vec<hir::ModuleDef> = Vec::new();
+    let mut push_new_def = |item: hir::ModuleDef| {
+        if !targets.contains(&item) {
+            targets.push(item);
+        }
+    };
+    walk_and_push_ty(sema.db, &inner_ty, &mut push_new_def);
+    walk_and_push_ty(sema.db, &body_ty, &mut push_new_def);
+    res.actions.push(HoverAction::goto_type_from_targets(sema.db, targets));
+
+    let inner_ty = inner_ty.display(sema.db).to_string();
+    let body_ty = body_ty.display(sema.db).to_string();
+    let ty_len_max = inner_ty.len().max(body_ty.len());
+
+    let l = "Propagated as: ".len() - " Type: ".len();
+    let static_text_len_diff = l as isize - s.len() as isize;
+    let tpad = static_text_len_diff.max(0) as usize;
+    let ppad = static_text_len_diff.min(0).abs() as usize;
+
+    res.markup = format!(
+        "{bt_start}{} Type: {:>pad0$}\nPropagated as: {:>pad1$}\n{bt_end}",
+        s,
+        inner_ty,
+        body_ty,
+        pad0 = ty_len_max + tpad,
+        pad1 = ty_len_max + ppad,
+        bt_start = if config.markdown() { "```text\n" } else { "" },
+        bt_end = if config.markdown() { "```\n" } else { "" }
+    )
+    .into();
+    Some(res)
+}
+
+pub(super) fn deref_expr(
+    sema: &Semantics<RootDatabase>,
+    config: &HoverConfig,
+    deref_expr: &ast::PrefixExpr,
+) -> Option<HoverResult> {
+    let inner_ty = sema.type_of_expr(&deref_expr.expr()?)?.original;
+    let TypeInfo { original, adjusted } =
+        sema.type_of_expr(&ast::Expr::from(deref_expr.clone()))?;
+
+    let mut res = HoverResult::default();
+    let mut targets: Vec<hir::ModuleDef> = Vec::new();
+    let mut push_new_def = |item: hir::ModuleDef| {
+        if !targets.contains(&item) {
+            targets.push(item);
+        }
+    };
+    walk_and_push_ty(sema.db, &inner_ty, &mut push_new_def);
+    walk_and_push_ty(sema.db, &original, &mut push_new_def);
+
+    res.markup = if let Some(adjusted_ty) = adjusted {
+        walk_and_push_ty(sema.db, &adjusted_ty, &mut push_new_def);
+        let original = original.display(sema.db).to_string();
+        let adjusted = adjusted_ty.display(sema.db).to_string();
+        let inner = inner_ty.display(sema.db).to_string();
+        let type_len = "To type: ".len();
+        let coerced_len = "Coerced to: ".len();
+        let deref_len = "Dereferenced from: ".len();
+        let max_len = (original.len() + type_len)
+            .max(adjusted.len() + coerced_len)
+            .max(inner.len() + deref_len);
+        format!(
+            "{bt_start}Dereferenced from: {:>ipad$}\nTo type: {:>apad$}\nCoerced to: {:>opad$}\n{bt_end}",
+            inner,
+            original,
+            adjusted,
+            ipad = max_len - deref_len,
+            apad = max_len - type_len,
+            opad = max_len - coerced_len,
+            bt_start = if config.markdown() { "```text\n" } else { "" },
+            bt_end = if config.markdown() { "```\n" } else { "" }
+        )
+        .into()
+    } else {
+        let original = original.display(sema.db).to_string();
+        let inner = inner_ty.display(sema.db).to_string();
+        let type_len = "To type: ".len();
+        let deref_len = "Dereferenced from: ".len();
+        let max_len = (original.len() + type_len).max(inner.len() + deref_len);
+        format!(
+            "{bt_start}Dereferenced from: {:>ipad$}\nTo type: {:>apad$}\n{bt_end}",
+            inner,
+            original,
+            ipad = max_len - deref_len,
+            apad = max_len - type_len,
+            bt_start = if config.markdown() { "```text\n" } else { "" },
+            bt_end = if config.markdown() { "```\n" } else { "" }
+        )
+        .into()
+    };
+    res.actions.push(HoverAction::goto_type_from_targets(sema.db, targets));
+
+    Some(res)
+}
+
+pub(super) fn keyword(
+    sema: &Semantics<RootDatabase>,
+    config: &HoverConfig,
+    token: &SyntaxToken,
+) -> Option<HoverResult> {
+    if !token.kind().is_keyword() || !config.documentation.is_some() {
+        return None;
+    }
+    let famous_defs = FamousDefs(sema, sema.scope(&token.parent()?).krate());
+    // std exposes {}_keyword modules with docstrings on the root to document keywords
+    let keyword_mod = format!("{}_keyword", token.text());
+    let doc_owner = find_std_module(&famous_defs, &keyword_mod)?;
+    let docs = doc_owner.attrs(sema.db).docs()?;
+    let markup = process_markup(
+        sema.db,
+        Definition::ModuleDef(doc_owner.into()),
+        &markup(Some(docs.into()), token.text().into(), None)?,
+        config,
+    );
+    Some(HoverResult { markup, actions: Default::default() })
+}
+
+pub(super) fn try_for_lint(attr: &ast::Attr, token: &SyntaxToken) -> Option<HoverResult> {
+    let (path, tt) = attr.as_simple_call()?;
+    if !tt.syntax().text_range().contains(token.text_range().start()) {
+        return None;
+    }
+    let (is_clippy, lints) = match &*path {
+        "feature" => (false, FEATURES),
+        "allow" | "deny" | "forbid" | "warn" => {
+            let is_clippy = algo::non_trivia_sibling(token.clone().into(), Direction::Prev)
+                .filter(|t| t.kind() == T![:])
+                .and_then(|t| algo::non_trivia_sibling(t, Direction::Prev))
+                .filter(|t| t.kind() == T![:])
+                .and_then(|t| algo::non_trivia_sibling(t, Direction::Prev))
+                .map_or(false, |t| {
+                    t.kind() == T![ident] && t.into_token().map_or(false, |t| t.text() == "clippy")
+                });
+            if is_clippy {
+                (true, CLIPPY_LINTS)
+            } else {
+                (false, DEFAULT_LINTS)
+            }
+        }
+        _ => return None,
+    };
+
+    let tmp;
+    let needle = if is_clippy {
+        tmp = format!("clippy::{}", token.text());
+        &tmp
+    } else {
+        &*token.text()
+    };
+
+    let lint =
+        lints.binary_search_by_key(&needle, |lint| lint.label).ok().map(|idx| &lints[idx])?;
+    Some(HoverResult {
+        markup: Markup::from(format!("```\n{}\n```\n___\n\n{}", lint.label, lint.description)),
+        ..Default::default()
+    })
+}
+
+pub(super) fn process_markup(
+    db: &RootDatabase,
+    def: Definition,
+    markup: &Markup,
+    config: &HoverConfig,
+) -> Markup {
+    let markup = markup.as_str();
+    let markup = if !config.markdown() {
+        remove_markdown(markup)
+    } else if config.links_in_hover {
+        rewrite_links(db, markup, def)
+    } else {
+        remove_links(markup)
+    };
+    Markup::from(markup)
+}
+
+fn definition_owner_name(db: &RootDatabase, def: &Definition) -> Option<String> {
+    match def {
+        Definition::Field(f) => Some(f.parent_def(db).name(db)),
+        Definition::Local(l) => l.parent(db).name(db),
+        Definition::ModuleDef(md) => match md {
+            hir::ModuleDef::Function(f) => match f.as_assoc_item(db)?.container(db) {
+                hir::AssocItemContainer::Trait(t) => Some(t.name(db)),
+                hir::AssocItemContainer::Impl(i) => i.self_ty(db).as_adt().map(|adt| adt.name(db)),
+            },
+            hir::ModuleDef::Variant(e) => Some(e.parent_enum(db).name(db)),
+            _ => None,
+        },
+        _ => None,
+    }
+    .map(|name| name.to_string())
+}
+
+pub(super) fn path(db: &RootDatabase, module: hir::Module, item_name: Option<String>) -> String {
+    let crate_name =
+        db.crate_graph()[module.krate().into()].display_name.as_ref().map(|it| it.to_string());
+    let module_path = module
+        .path_to_root(db)
+        .into_iter()
+        .rev()
+        .flat_map(|it| it.name(db).map(|name| name.to_string()));
+    crate_name.into_iter().chain(module_path).chain(item_name).join("::")
+}
+
+pub(super) fn definition(
+    db: &RootDatabase,
+    def: Definition,
+    famous_defs: Option<&FamousDefs>,
+    config: &HoverConfig,
+) -> Option<Markup> {
+    let mod_path = definition_mod_path(db, &def);
+    let (label, docs) = match def {
+        Definition::Macro(it) => (
+            match &it.source(db)?.value {
+                Either::Left(mac) => macro_label(mac),
+                Either::Right(mac_fn) => fn_as_proc_macro_label(mac_fn),
+            },
+            it.attrs(db).docs(),
+        ),
+        Definition::Field(def) => label_and_docs(db, def),
+        Definition::ModuleDef(it) => match it {
+            hir::ModuleDef::Module(it) => label_and_docs(db, it),
+            hir::ModuleDef::Function(it) => label_and_docs(db, it),
+            hir::ModuleDef::Adt(it) => label_and_docs(db, it),
+            hir::ModuleDef::Variant(it) => label_and_docs(db, it),
+            hir::ModuleDef::Const(it) => label_and_docs(db, it),
+            hir::ModuleDef::Static(it) => label_and_docs(db, it),
+            hir::ModuleDef::Trait(it) => label_and_docs(db, it),
+            hir::ModuleDef::TypeAlias(it) => label_and_docs(db, it),
+            hir::ModuleDef::BuiltinType(it) => {
+                return famous_defs
+                    .and_then(|fd| builtin(fd, it))
+                    .or_else(|| Some(Markup::fenced_block(&it.name())))
+            }
+        },
+        Definition::Local(it) => return local(db, it),
+        Definition::SelfType(impl_def) => {
+            impl_def.self_ty(db).as_adt().map(|adt| label_and_docs(db, adt))?
+        }
+        Definition::GenericParam(it) => label_and_docs(db, it),
+        Definition::Label(it) => return Some(Markup::fenced_block(&it.name(db))),
+    };
+
+    markup(docs.filter(|_| config.documentation.is_some()).map(Into::into), label, mod_path)
+}
+
+fn label_and_docs<D>(db: &RootDatabase, def: D) -> (String, Option<hir::Documentation>)
+where
+    D: HasAttrs + HirDisplay,
+{
+    let label = def.display(db).to_string();
+    let docs = def.attrs(db).docs();
+    (label, docs)
+}
+
+fn definition_mod_path(db: &RootDatabase, def: &Definition) -> Option<String> {
+    if let Definition::GenericParam(_) = def {
+        return None;
+    }
+    def.module(db).map(|module| path(db, module, definition_owner_name(db, def)))
+}
+
+fn markup(docs: Option<String>, desc: String, mod_path: Option<String>) -> Option<Markup> {
+    let mut buf = String::new();
+
+    if let Some(mod_path) = mod_path {
+        if !mod_path.is_empty() {
+            format_to!(buf, "```rust\n{}\n```\n\n", mod_path);
+        }
+    }
+    format_to!(buf, "```rust\n{}\n```", desc);
+
+    if let Some(doc) = docs {
+        format_to!(buf, "\n___\n\n{}", doc);
+    }
+    Some(buf.into())
+}
+
+fn builtin(famous_defs: &FamousDefs, builtin: hir::BuiltinType) -> Option<Markup> {
+    // std exposes prim_{} modules with docstrings on the root to document the builtins
+    let primitive_mod = format!("prim_{}", builtin.name());
+    let doc_owner = find_std_module(famous_defs, &primitive_mod)?;
+    let docs = doc_owner.attrs(famous_defs.0.db).docs()?;
+    markup(Some(docs.into()), builtin.name().to_string(), None)
+}
+
+fn find_std_module(famous_defs: &FamousDefs, name: &str) -> Option<hir::Module> {
+    let db = famous_defs.0.db;
+    let std_crate = famous_defs.std()?;
+    let std_root_module = std_crate.root_module(db);
+    std_root_module
+        .children(db)
+        .find(|module| module.name(db).map_or(false, |module| module.to_string() == name))
+}
+
+fn local(db: &RootDatabase, it: hir::Local) -> Option<Markup> {
+    let ty = it.ty(db);
+    let ty = ty.display(db);
+    let is_mut = if it.is_mut(db) { "mut " } else { "" };
+    let desc = match it.source(db).value {
+        Either::Left(ident) => {
+            let name = it.name(db).unwrap();
+            let let_kw = if ident
+                .syntax()
+                .parent()
+                .map_or(false, |p| p.kind() == LET_STMT || p.kind() == CONDITION)
+            {
+                "let "
+            } else {
+                ""
+            };
+            format!("{}{}{}: {}", let_kw, is_mut, name, ty)
+        }
+        Either::Right(_) => format!("{}self: {}", is_mut, ty),
+    };
+    markup(None, desc, None)
+}
diff --git a/crates/ide/src/hover/tests.rs b/crates/ide/src/hover/tests.rs
new file mode 100644 (file)
index 0000000..463cd58
--- /dev/null
@@ -0,0 +1,4168 @@
+use expect_test::{expect, Expect};
+use ide_db::base_db::{FileLoader, FileRange};
+use syntax::TextRange;
+
+use crate::{fixture, hover::HoverDocFormat, HoverConfig};
+
+fn check_hover_no_result(ra_fixture: &str) {
+    let (analysis, position) = fixture::position(ra_fixture);
+    let hover = analysis
+        .hover(
+            &HoverConfig { links_in_hover: true, documentation: Some(HoverDocFormat::Markdown) },
+            FileRange { file_id: position.file_id, range: TextRange::empty(position.offset) },
+        )
+        .unwrap();
+    assert!(hover.is_none(), "hover not expected but found: {:?}", hover.unwrap());
+}
+
+fn check(ra_fixture: &str, expect: Expect) {
+    let (analysis, position) = fixture::position(ra_fixture);
+    let hover = analysis
+        .hover(
+            &HoverConfig { links_in_hover: true, documentation: Some(HoverDocFormat::Markdown) },
+            FileRange { file_id: position.file_id, range: TextRange::empty(position.offset) },
+        )
+        .unwrap()
+        .unwrap();
+
+    let content = analysis.db.file_text(position.file_id);
+    let hovered_element = &content[hover.range];
+
+    let actual = format!("*{}*\n{}\n", hovered_element, hover.info.markup);
+    expect.assert_eq(&actual)
+}
+
+fn check_hover_no_links(ra_fixture: &str, expect: Expect) {
+    let (analysis, position) = fixture::position(ra_fixture);
+    let hover = analysis
+        .hover(
+            &HoverConfig { links_in_hover: false, documentation: Some(HoverDocFormat::Markdown) },
+            FileRange { file_id: position.file_id, range: TextRange::empty(position.offset) },
+        )
+        .unwrap()
+        .unwrap();
+
+    let content = analysis.db.file_text(position.file_id);
+    let hovered_element = &content[hover.range];
+
+    let actual = format!("*{}*\n{}\n", hovered_element, hover.info.markup);
+    expect.assert_eq(&actual)
+}
+
+fn check_hover_no_markdown(ra_fixture: &str, expect: Expect) {
+    let (analysis, position) = fixture::position(ra_fixture);
+    let hover = analysis
+        .hover(
+            &HoverConfig { links_in_hover: true, documentation: Some(HoverDocFormat::PlainText) },
+            FileRange { file_id: position.file_id, range: TextRange::empty(position.offset) },
+        )
+        .unwrap()
+        .unwrap();
+
+    let content = analysis.db.file_text(position.file_id);
+    let hovered_element = &content[hover.range];
+
+    let actual = format!("*{}*\n{}\n", hovered_element, hover.info.markup);
+    expect.assert_eq(&actual)
+}
+
+fn check_actions(ra_fixture: &str, expect: Expect) {
+    let (analysis, file_id, position) = fixture::range_or_position(ra_fixture);
+    let hover = analysis
+        .hover(
+            &HoverConfig { links_in_hover: true, documentation: Some(HoverDocFormat::Markdown) },
+            FileRange { file_id, range: position.range_or_empty() },
+        )
+        .unwrap()
+        .unwrap();
+    expect.assert_debug_eq(&hover.info.actions)
+}
+
+fn check_hover_range(ra_fixture: &str, expect: Expect) {
+    let (analysis, range) = fixture::range(ra_fixture);
+    let hover = analysis
+        .hover(
+            &HoverConfig { links_in_hover: false, documentation: Some(HoverDocFormat::Markdown) },
+            range,
+        )
+        .unwrap()
+        .unwrap();
+    expect.assert_eq(hover.info.markup.as_str())
+}
+
+fn check_hover_range_no_results(ra_fixture: &str) {
+    let (analysis, range) = fixture::range(ra_fixture);
+    let hover = analysis
+        .hover(
+            &HoverConfig { links_in_hover: false, documentation: Some(HoverDocFormat::Markdown) },
+            range,
+        )
+        .unwrap();
+    assert!(hover.is_none());
+}
+
+#[test]
+fn hover_descend_macros_avoids_duplicates() {
+    check(
+        r#"
+macro_rules! dupe_use {
+    ($local:ident) => {
+        {
+            $local;
+            $local;
+        }
+    }
+}
+fn foo() {
+    let local = 0;
+    dupe_use!(local$0);
+}
+"#,
+        expect![[r#"
+            *local*
+
+            ```rust
+            let local: i32
+            ```
+        "#]],
+    );
+}
+
+#[test]
+fn hover_shows_all_macro_descends() {
+    check(
+        r#"
+macro_rules! m {
+    ($name:ident) => {
+        /// Outer
+        fn $name() {}
+
+        mod module {
+            /// Inner
+            fn $name() {}
+        }
+    };
+}
+
+m!(ab$0c);
+            "#,
+        expect![[r#"
+            *abc*
+
+            ```rust
+            test::module
+            ```
+
+            ```rust
+            fn abc()
+            ```
+
+            ---
+
+            Inner
+            ---
+
+            ```rust
+            test
+            ```
+
+            ```rust
+            fn abc()
+            ```
+
+            ---
+
+            Outer
+            "#]],
+    );
+}
+
+#[test]
+fn hover_shows_type_of_an_expression() {
+    check(
+        r#"
+pub fn foo() -> u32 { 1 }
+
+fn main() {
+    let foo_test = foo()$0;
+}
+"#,
+        expect![[r#"
+            *foo()*
+            ```rust
+            u32
+            ```
+        "#]],
+    );
+}
+
+#[test]
+fn hover_remove_markdown_if_configured() {
+    check_hover_no_markdown(
+        r#"
+pub fn foo() -> u32 { 1 }
+
+fn main() {
+    let foo_test = foo()$0;
+}
+"#,
+        expect![[r#"
+            *foo()*
+            u32
+        "#]],
+    );
+}
+
+#[test]
+fn hover_shows_long_type_of_an_expression() {
+    check(
+        r#"
+struct Scan<A, B, C> { a: A, b: B, c: C }
+struct Iter<I> { inner: I }
+enum Option<T> { Some(T), None }
+
+struct OtherStruct<T> { i: T }
+
+fn scan<A, B, C>(a: A, b: B, c: C) -> Iter<Scan<OtherStruct<A>, B, C>> {
+    Iter { inner: Scan { a, b, c } }
+}
+
+fn main() {
+    let num: i32 = 55;
+    let closure = |memo: &mut u32, value: &u32, _another: &mut u32| -> Option<u32> {
+        Option::Some(*memo + value)
+    };
+    let number = 5u32;
+    let mut iter$0 = scan(OtherStruct { i: num }, closure, number);
+}
+"#,
+        expect![[r#"
+                *iter*
+
+                ```rust
+                let mut iter: Iter<Scan<OtherStruct<OtherStruct<i32>>, |&mut u32, &u32, &mut u32| -> Option<u32>, u32>>
+                ```
+            "#]],
+    );
+}
+
+#[test]
+fn hover_shows_fn_signature() {
+    // Single file with result
+    check(
+        r#"
+pub fn foo() -> u32 { 1 }
+
+fn main() { let foo_test = fo$0o(); }
+"#,
+        expect![[r#"
+                *foo*
+
+                ```rust
+                test
+                ```
+
+                ```rust
+                pub fn foo() -> u32
+                ```
+            "#]],
+    );
+
+    // Multiple candidates but results are ambiguous.
+    check(
+        r#"
+//- /a.rs
+pub fn foo() -> u32 { 1 }
+
+//- /b.rs
+pub fn foo() -> &str { "" }
+
+//- /c.rs
+pub fn foo(a: u32, b: u32) {}
+
+//- /main.rs
+mod a;
+mod b;
+mod c;
+
+fn main() { let foo_test = fo$0o(); }
+        "#,
+        expect![[r#"
+                *foo*
+                ```rust
+                {unknown}
+                ```
+            "#]],
+    );
+
+    // Use literal `crate` in path
+    check(
+        r#"
+pub struct X;
+
+fn foo() -> crate::X { X }
+
+fn main() { f$0oo(); }
+        "#,
+        expect![[r#"
+            *foo*
+
+            ```rust
+            test
+            ```
+
+            ```rust
+            fn foo() -> crate::X
+            ```
+        "#]],
+    );
+
+    // Check `super` in path
+    check(
+        r#"
+pub struct X;
+
+mod m { pub fn foo() -> super::X { super::X } }
+
+fn main() { m::f$0oo(); }
+        "#,
+        expect![[r#"
+                *foo*
+
+                ```rust
+                test::m
+                ```
+
+                ```rust
+                pub fn foo() -> super::X
+                ```
+            "#]],
+    );
+}
+
+#[test]
+fn hover_shows_fn_signature_with_type_params() {
+    check(
+        r#"
+pub fn foo<'a, T: AsRef<str>>(b: &'a T) -> &'a str { }
+
+fn main() { let foo_test = fo$0o(); }
+        "#,
+        expect![[r#"
+                *foo*
+
+                ```rust
+                test
+                ```
+
+                ```rust
+                pub fn foo<'a, T>(b: &'a T) -> &'a str
+                where
+                    T: AsRef<str>,
+                ```
+            "#]],
+    );
+}
+
+#[test]
+fn hover_shows_fn_signature_on_fn_name() {
+    check(
+        r#"
+pub fn foo$0(a: u32, b: u32) -> u32 {}
+
+fn main() { }
+"#,
+        expect![[r#"
+                *foo*
+
+                ```rust
+                test
+                ```
+
+                ```rust
+                pub fn foo(a: u32, b: u32) -> u32
+                ```
+            "#]],
+    );
+}
+
+#[test]
+fn hover_shows_fn_doc() {
+    check(
+        r#"
+/// # Example
+/// ```
+/// # use std::path::Path;
+/// #
+/// foo(Path::new("hello, world!"))
+/// ```
+pub fn foo$0(_: &Path) {}
+
+fn main() { }
+"#,
+        expect![[r##"
+                *foo*
+
+                ```rust
+                test
+                ```
+
+                ```rust
+                pub fn foo(_: &Path)
+                ```
+
+                ---
+
+                # Example
+
+                ```
+                # use std::path::Path;
+                #
+                foo(Path::new("hello, world!"))
+                ```
+            "##]],
+    );
+}
+
+#[test]
+fn hover_shows_fn_doc_attr_raw_string() {
+    check(
+        r##"
+#[doc = r#"Raw string doc attr"#]
+pub fn foo$0(_: &Path) {}
+
+fn main() { }
+"##,
+        expect![[r##"
+                *foo*
+
+                ```rust
+                test
+                ```
+
+                ```rust
+                pub fn foo(_: &Path)
+                ```
+
+                ---
+
+                Raw string doc attr
+            "##]],
+    );
+}
+
+#[test]
+fn hover_shows_struct_field_info() {
+    // Hovering over the field when instantiating
+    check(
+        r#"
+struct Foo { field_a: u32 }
+
+fn main() {
+    let foo = Foo { field_a$0: 0, };
+}
+"#,
+        expect![[r#"
+                *field_a*
+
+                ```rust
+                test::Foo
+                ```
+
+                ```rust
+                field_a: u32
+                ```
+            "#]],
+    );
+
+    // Hovering over the field in the definition
+    check(
+        r#"
+struct Foo { field_a$0: u32 }
+
+fn main() {
+    let foo = Foo { field_a: 0 };
+}
+"#,
+        expect![[r#"
+                *field_a*
+
+                ```rust
+                test::Foo
+                ```
+
+                ```rust
+                field_a: u32
+                ```
+            "#]],
+    );
+}
+
+#[test]
+fn hover_const_static() {
+    check(
+        r#"const foo$0: u32 = 123;"#,
+        expect![[r#"
+                *foo*
+
+                ```rust
+                test
+                ```
+
+                ```rust
+                const foo: u32
+                ```
+            "#]],
+    );
+    check(
+        r#"static foo$0: u32 = 456;"#,
+        expect![[r#"
+                *foo*
+
+                ```rust
+                test
+                ```
+
+                ```rust
+                static foo: u32
+                ```
+            "#]],
+    );
+}
+
+#[test]
+fn hover_default_generic_types() {
+    check(
+        r#"
+struct Test<K, T = u8> { k: K, t: T }
+
+fn main() {
+    let zz$0 = Test { t: 23u8, k: 33 };
+}"#,
+        expect![[r#"
+                *zz*
+
+                ```rust
+                let zz: Test<i32, u8>
+                ```
+            "#]],
+    );
+}
+
+#[test]
+fn hover_some() {
+    check(
+        r#"
+enum Option<T> { Some(T) }
+use Option::Some;
+
+fn main() { So$0me(12); }
+"#,
+        expect![[r#"
+                *Some*
+
+                ```rust
+                test::Option
+                ```
+
+                ```rust
+                Some(T)
+                ```
+            "#]],
+    );
+
+    check(
+        r#"
+enum Option<T> { Some(T) }
+use Option::Some;
+
+fn main() { let b$0ar = Some(12); }
+"#,
+        expect![[r#"
+                *bar*
+
+                ```rust
+                let bar: Option<i32>
+                ```
+            "#]],
+    );
+}
+
+#[test]
+fn hover_enum_variant() {
+    check(
+        r#"
+enum Option<T> {
+    /// The None variant
+    Non$0e
+}
+"#,
+        expect![[r#"
+                *None*
+
+                ```rust
+                test::Option
+                ```
+
+                ```rust
+                None
+                ```
+
+                ---
+
+                The None variant
+            "#]],
+    );
+
+    check(
+        r#"
+enum Option<T> {
+    /// The Some variant
+    Some(T)
+}
+fn main() {
+    let s = Option::Som$0e(12);
+}
+"#,
+        expect![[r#"
+                *Some*
+
+                ```rust
+                test::Option
+                ```
+
+                ```rust
+                Some(T)
+                ```
+
+                ---
+
+                The Some variant
+            "#]],
+    );
+}
+
+#[test]
+fn hover_for_local_variable() {
+    check(
+        r#"fn func(foo: i32) { fo$0o; }"#,
+        expect![[r#"
+                *foo*
+
+                ```rust
+                foo: i32
+                ```
+            "#]],
+    )
+}
+
+#[test]
+fn hover_for_local_variable_pat() {
+    check(
+        r#"fn func(fo$0o: i32) {}"#,
+        expect![[r#"
+                *foo*
+
+                ```rust
+                foo: i32
+                ```
+            "#]],
+    )
+}
+
+#[test]
+fn hover_local_var_edge() {
+    check(
+        r#"fn func(foo: i32) { if true { $0foo; }; }"#,
+        expect![[r#"
+                *foo*
+
+                ```rust
+                foo: i32
+                ```
+            "#]],
+    )
+}
+
+#[test]
+fn hover_for_param_edge() {
+    check(
+        r#"fn func($0foo: i32) {}"#,
+        expect![[r#"
+                *foo*
+
+                ```rust
+                foo: i32
+                ```
+            "#]],
+    )
+}
+
+#[test]
+fn hover_for_param_with_multiple_traits() {
+    check(
+        r#"
+            //- minicore: sized
+            trait Deref {
+                type Target: ?Sized;
+            }
+            trait DerefMut {
+                type Target: ?Sized;
+            }
+            fn f(_x$0: impl Deref<Target=u8> + DerefMut<Target=u8>) {}"#,
+        expect![[r#"
+                *_x*
+
+                ```rust
+                _x: impl Deref<Target = u8> + DerefMut<Target = u8>
+                ```
+            "#]],
+    )
+}
+
+#[test]
+fn test_hover_infer_associated_method_result() {
+    check(
+        r#"
+struct Thing { x: u32 }
+
+impl Thing {
+    fn new() -> Thing { Thing { x: 0 } }
+}
+
+fn main() { let foo_$0test = Thing::new(); }
+"#,
+        expect![[r#"
+                *foo_test*
+
+                ```rust
+                let foo_test: Thing
+                ```
+            "#]],
+    )
+}
+
+#[test]
+fn test_hover_infer_associated_method_exact() {
+    check(
+        r#"
+mod wrapper {
+    struct Thing { x: u32 }
+
+    impl Thing {
+        fn new() -> Thing { Thing { x: 0 } }
+    }
+}
+
+fn main() { let foo_test = wrapper::Thing::new$0(); }
+"#,
+        expect![[r#"
+                *new*
+
+                ```rust
+                test::wrapper::Thing
+                ```
+
+                ```rust
+                fn new() -> Thing
+                ```
+            "#]],
+    )
+}
+
+#[test]
+fn test_hover_infer_associated_const_in_pattern() {
+    check(
+        r#"
+struct X;
+impl X {
+    const C: u32 = 1;
+}
+
+fn main() {
+    match 1 {
+        X::C$0 => {},
+        2 => {},
+        _ => {}
+    };
+}
+"#,
+        expect![[r#"
+                *C*
+
+                ```rust
+                test
+                ```
+
+                ```rust
+                const C: u32
+                ```
+            "#]],
+    )
+}
+
+#[test]
+fn test_hover_self() {
+    check(
+        r#"
+struct Thing { x: u32 }
+impl Thing {
+    fn new() -> Self { Self$0 { x: 0 } }
+}
+"#,
+        expect![[r#"
+                *Self*
+
+                ```rust
+                test
+                ```
+
+                ```rust
+                struct Thing
+                ```
+            "#]],
+    );
+    check(
+        r#"
+struct Thing { x: u32 }
+impl Thing {
+    fn new() -> Self$0 { Self { x: 0 } }
+}
+"#,
+        expect![[r#"
+                *Self*
+
+                ```rust
+                test
+                ```
+
+                ```rust
+                struct Thing
+                ```
+            "#]],
+    );
+    check(
+        r#"
+enum Thing { A }
+impl Thing {
+    pub fn new() -> Self$0 { Thing::A }
+}
+"#,
+        expect![[r#"
+                *Self*
+
+                ```rust
+                test
+                ```
+
+                ```rust
+                enum Thing
+                ```
+            "#]],
+    );
+    check(
+        r#"
+        enum Thing { A }
+        impl Thing {
+            pub fn thing(a: Self$0) {}
+        }
+        "#,
+        expect![[r#"
+                *Self*
+
+                ```rust
+                test
+                ```
+
+                ```rust
+                enum Thing
+                ```
+            "#]],
+    );
+}
+
+#[test]
+fn test_hover_shadowing_pat() {
+    check(
+        r#"
+fn x() {}
+
+fn y() {
+    let x = 0i32;
+    x$0;
+}
+"#,
+        expect![[r#"
+                *x*
+
+                ```rust
+                let x: i32
+                ```
+            "#]],
+    )
+}
+
+#[test]
+fn test_hover_macro_invocation() {
+    check(
+        r#"
+macro_rules! foo { () => {} }
+
+fn f() { fo$0o!(); }
+"#,
+        expect![[r#"
+                *foo*
+
+                ```rust
+                test
+                ```
+
+                ```rust
+                macro_rules! foo
+                ```
+            "#]],
+    )
+}
+
+#[test]
+fn test_hover_macro2_invocation() {
+    check(
+        r#"
+/// foo bar
+///
+/// foo bar baz
+macro foo() {}
+
+fn f() { fo$0o!(); }
+"#,
+        expect![[r#"
+                *foo*
+
+                ```rust
+                test
+                ```
+
+                ```rust
+                macro foo
+                ```
+
+                ---
+
+                foo bar
+
+                foo bar baz
+            "#]],
+    )
+}
+
+#[test]
+fn test_hover_tuple_field() {
+    check(
+        r#"struct TS(String, i32$0);"#,
+        expect![[r#"
+                *i32*
+
+                ```rust
+                i32
+                ```
+            "#]],
+    )
+}
+
+#[test]
+fn test_hover_through_macro() {
+    check(
+        r#"
+macro_rules! id { ($($tt:tt)*) => { $($tt)* } }
+fn foo() {}
+id! {
+    fn bar() { fo$0o(); }
+}
+"#,
+        expect![[r#"
+                *foo*
+
+                ```rust
+                test
+                ```
+
+                ```rust
+                fn foo()
+                ```
+            "#]],
+    );
+}
+
+#[test]
+fn test_hover_through_attr() {
+    check(
+        r#"
+//- proc_macros: identity
+#[proc_macros::identity]
+fn foo$0() {}
+"#,
+        expect![[r#"
+                *foo*
+
+                ```rust
+                test
+                ```
+
+                ```rust
+                fn foo()
+                ```
+            "#]],
+    );
+}
+
+#[test]
+fn test_hover_through_expr_in_macro() {
+    check(
+        r#"
+macro_rules! id { ($($tt:tt)*) => { $($tt)* } }
+fn foo(bar:u32) { let a = id!(ba$0r); }
+"#,
+        expect![[r#"
+                *bar*
+
+                ```rust
+                bar: u32
+                ```
+            "#]],
+    );
+}
+
+#[test]
+fn test_hover_through_expr_in_macro_recursive() {
+    check(
+        r#"
+macro_rules! id_deep { ($($tt:tt)*) => { $($tt)* } }
+macro_rules! id { ($($tt:tt)*) => { id_deep!($($tt)*) } }
+fn foo(bar:u32) { let a = id!(ba$0r); }
+"#,
+        expect![[r#"
+                *bar*
+
+                ```rust
+                bar: u32
+                ```
+            "#]],
+    );
+}
+
+#[test]
+fn test_hover_through_func_in_macro_recursive() {
+    check(
+        r#"
+macro_rules! id_deep { ($($tt:tt)*) => { $($tt)* } }
+macro_rules! id { ($($tt:tt)*) => { id_deep!($($tt)*) } }
+fn bar() -> u32 { 0 }
+fn foo() { let a = id!([0u32, bar($0)] ); }
+"#,
+        expect![[r#"
+                *bar()*
+                ```rust
+                u32
+                ```
+            "#]],
+    );
+}
+
+#[test]
+fn test_hover_through_literal_string_in_macro() {
+    check(
+        r#"
+macro_rules! arr { ($($tt:tt)*) => { [$($tt)*)] } }
+fn foo() {
+    let mastered_for_itunes = "";
+    let _ = arr!("Tr$0acks", &mastered_for_itunes);
+}
+"#,
+        expect![[r#"
+                *"Tracks"*
+                ```rust
+                &str
+                ```
+            "#]],
+    );
+}
+
+#[test]
+fn test_hover_through_assert_macro() {
+    check(
+        r#"
+#[rustc_builtin_macro]
+macro_rules! assert {}
+
+fn bar() -> bool { true }
+fn foo() {
+    assert!(ba$0r());
+}
+"#,
+        expect![[r#"
+                *bar*
+
+                ```rust
+                test
+                ```
+
+                ```rust
+                fn bar() -> bool
+                ```
+            "#]],
+    );
+}
+
+#[test]
+fn test_hover_through_literal_string_in_builtin_macro() {
+    check_hover_no_result(
+        r#"
+            #[rustc_builtin_macro]
+            macro_rules! format {}
+
+            fn foo() {
+                format!("hel$0lo {}", 0);
+            }
+"#,
+    );
+}
+
+#[test]
+fn test_hover_non_ascii_space_doc() {
+    check(
+        "
+/// <- `\u{3000}` here
+fn foo() { }
+
+fn bar() { fo$0o(); }
+",
+        expect![[r#"
+                *foo*
+
+                ```rust
+                test
+                ```
+
+                ```rust
+                fn foo()
+                ```
+
+                ---
+
+                \<- ` ` here
+            "#]],
+    );
+}
+
+#[test]
+fn test_hover_function_show_qualifiers() {
+    check(
+        r#"async fn foo$0() {}"#,
+        expect![[r#"
+                *foo*
+
+                ```rust
+                test
+                ```
+
+                ```rust
+                async fn foo()
+                ```
+            "#]],
+    );
+    check(
+        r#"pub const unsafe fn foo$0() {}"#,
+        expect![[r#"
+                *foo*
+
+                ```rust
+                test
+                ```
+
+                ```rust
+                pub const unsafe fn foo()
+                ```
+            "#]],
+    );
+    // Top level `pub(crate)` will be displayed as no visibility.
+    check(
+        r#"mod m { pub(crate) async unsafe extern "C" fn foo$0() {} }"#,
+        expect![[r#"
+                *foo*
+
+                ```rust
+                test::m
+                ```
+
+                ```rust
+                pub(crate) async unsafe extern "C" fn foo()
+                ```
+            "#]],
+    );
+}
+
+#[test]
+fn test_hover_trait_show_qualifiers() {
+    check_actions(
+        r"unsafe trait foo$0() {}",
+        expect![[r#"
+                [
+                    Implementation(
+                        FilePosition {
+                            file_id: FileId(
+                                0,
+                            ),
+                            offset: 13,
+                        },
+                    ),
+                ]
+            "#]],
+    );
+}
+
+#[test]
+fn test_hover_extern_crate() {
+    check(
+        r#"
+//- /main.rs crate:main deps:std
+extern crate st$0d;
+//- /std/lib.rs crate:std
+//! Standard library for this test
+//!
+//! Printed?
+//! abc123
+"#,
+        expect![[r#"
+                *std*
+
+                ```rust
+                extern crate std
+                ```
+
+                ---
+
+                Standard library for this test
+
+                Printed?
+                abc123
+            "#]],
+    );
+    check(
+        r#"
+//- /main.rs crate:main deps:std
+extern crate std as ab$0c;
+//- /std/lib.rs crate:std
+//! Standard library for this test
+//!
+//! Printed?
+//! abc123
+"#,
+        expect![[r#"
+                *abc*
+
+                ```rust
+                extern crate std
+                ```
+
+                ---
+
+                Standard library for this test
+
+                Printed?
+                abc123
+            "#]],
+    );
+}
+
+#[test]
+fn test_hover_mod_with_same_name_as_function() {
+    check(
+        r#"
+use self::m$0y::Bar;
+mod my { pub struct Bar; }
+
+fn my() {}
+"#,
+        expect![[r#"
+                *my*
+
+                ```rust
+                test
+                ```
+
+                ```rust
+                mod my
+                ```
+            "#]],
+    );
+}
+
+#[test]
+fn test_hover_struct_doc_comment() {
+    check(
+        r#"
+/// This is an example
+/// multiline doc
+///
+/// # Example
+///
+/// ```
+/// let five = 5;
+///
+/// assert_eq!(6, my_crate::add_one(5));
+/// ```
+struct Bar;
+
+fn foo() { let bar = Ba$0r; }
+"#,
+        expect![[r##"
+                *Bar*
+
+                ```rust
+                test
+                ```
+
+                ```rust
+                struct Bar
+                ```
+
+                ---
+
+                This is an example
+                multiline doc
+
+                # Example
+
+                ```
+                let five = 5;
+
+                assert_eq!(6, my_crate::add_one(5));
+                ```
+            "##]],
+    );
+}
+
+#[test]
+fn test_hover_struct_doc_attr() {
+    check(
+        r#"
+#[doc = "bar docs"]
+struct Bar;
+
+fn foo() { let bar = Ba$0r; }
+"#,
+        expect![[r#"
+                *Bar*
+
+                ```rust
+                test
+                ```
+
+                ```rust
+                struct Bar
+                ```
+
+                ---
+
+                bar docs
+            "#]],
+    );
+}
+
+#[test]
+fn test_hover_struct_doc_attr_multiple_and_mixed() {
+    check(
+        r#"
+/// bar docs 0
+#[doc = "bar docs 1"]
+#[doc = "bar docs 2"]
+struct Bar;
+
+fn foo() { let bar = Ba$0r; }
+"#,
+        expect![[r#"
+                *Bar*
+
+                ```rust
+                test
+                ```
+
+                ```rust
+                struct Bar
+                ```
+
+                ---
+
+                bar docs 0
+                bar docs 1
+                bar docs 2
+            "#]],
+    );
+}
+
+#[test]
+fn test_hover_external_url() {
+    check(
+        r#"
+pub struct Foo;
+/// [external](https://www.google.com)
+pub struct B$0ar
+"#,
+        expect![[r#"
+                *Bar*
+
+                ```rust
+                test
+                ```
+
+                ```rust
+                pub struct Bar
+                ```
+
+                ---
+
+                [external](https://www.google.com)
+            "#]],
+    );
+}
+
+// Check that we don't rewrite links which we can't identify
+#[test]
+fn test_hover_unknown_target() {
+    check(
+        r#"
+pub struct Foo;
+/// [baz](Baz)
+pub struct B$0ar
+"#,
+        expect![[r#"
+                *Bar*
+
+                ```rust
+                test
+                ```
+
+                ```rust
+                pub struct Bar
+                ```
+
+                ---
+
+                [baz](Baz)
+            "#]],
+    );
+}
+
+#[test]
+fn test_hover_no_links() {
+    check_hover_no_links(
+        r#"
+/// Test cases:
+/// case 1.  bare URL: https://www.example.com/
+/// case 2.  inline URL with title: [example](https://www.example.com/)
+/// case 3.  code reference: [`Result`]
+/// case 4.  code reference but miss footnote: [`String`]
+/// case 5.  autolink: <http://www.example.com/>
+/// case 6.  email address: <test@example.com>
+/// case 7.  reference: [example][example]
+/// case 8.  collapsed link: [example][]
+/// case 9.  shortcut link: [example]
+/// case 10. inline without URL: [example]()
+/// case 11. reference: [foo][foo]
+/// case 12. reference: [foo][bar]
+/// case 13. collapsed link: [foo][]
+/// case 14. shortcut link: [foo]
+/// case 15. inline without URL: [foo]()
+/// case 16. just escaped text: \[foo]
+/// case 17. inline link: [Foo](foo::Foo)
+///
+/// [`Result`]: ../../std/result/enum.Result.html
+/// [^example]: https://www.example.com/
+pub fn fo$0o() {}
+"#,
+        expect![[r#"
+                *foo*
+
+                ```rust
+                test
+                ```
+
+                ```rust
+                pub fn foo()
+                ```
+
+                ---
+
+                Test cases:
+                case 1.  bare URL: https://www.example.com/
+                case 2.  inline URL with title: [example](https://www.example.com/)
+                case 3.  code reference: `Result`
+                case 4.  code reference but miss footnote: `String`
+                case 5.  autolink: http://www.example.com/
+                case 6.  email address: test@example.com
+                case 7.  reference: example
+                case 8.  collapsed link: example
+                case 9.  shortcut link: example
+                case 10. inline without URL: example
+                case 11. reference: foo
+                case 12. reference: foo
+                case 13. collapsed link: foo
+                case 14. shortcut link: foo
+                case 15. inline without URL: foo
+                case 16. just escaped text: \[foo\]
+                case 17. inline link: Foo
+
+                [^example]: https://www.example.com/
+            "#]],
+    );
+}
+
+#[test]
+fn test_hover_macro_generated_struct_fn_doc_comment() {
+    cov_mark::check!(hover_macro_generated_struct_fn_doc_comment);
+
+    check(
+        r#"
+macro_rules! bar {
+    () => {
+        struct Bar;
+        impl Bar {
+            /// Do the foo
+            fn foo(&self) {}
+        }
+    }
+}
+
+bar!();
+
+fn foo() { let bar = Bar; bar.fo$0o(); }
+"#,
+        expect![[r#"
+                *foo*
+
+                ```rust
+                test::Bar
+                ```
+
+                ```rust
+                fn foo(&self)
+                ```
+
+                ---
+
+                Do the foo
+            "#]],
+    );
+}
+
+#[test]
+fn test_hover_macro_generated_struct_fn_doc_attr() {
+    cov_mark::check!(hover_macro_generated_struct_fn_doc_attr);
+
+    check(
+        r#"
+macro_rules! bar {
+    () => {
+        struct Bar;
+        impl Bar {
+            #[doc = "Do the foo"]
+            fn foo(&self) {}
+        }
+    }
+}
+
+bar!();
+
+fn foo() { let bar = Bar; bar.fo$0o(); }
+"#,
+        expect![[r#"
+                *foo*
+
+                ```rust
+                test::Bar
+                ```
+
+                ```rust
+                fn foo(&self)
+                ```
+
+                ---
+
+                Do the foo
+            "#]],
+    );
+}
+
+#[test]
+fn test_hover_trait_has_impl_action() {
+    check_actions(
+        r#"trait foo$0() {}"#,
+        expect![[r#"
+                [
+                    Implementation(
+                        FilePosition {
+                            file_id: FileId(
+                                0,
+                            ),
+                            offset: 6,
+                        },
+                    ),
+                ]
+            "#]],
+    );
+}
+
+#[test]
+fn test_hover_struct_has_impl_action() {
+    check_actions(
+        r"struct foo$0() {}",
+        expect![[r#"
+                [
+                    Implementation(
+                        FilePosition {
+                            file_id: FileId(
+                                0,
+                            ),
+                            offset: 7,
+                        },
+                    ),
+                ]
+            "#]],
+    );
+}
+
+#[test]
+fn test_hover_union_has_impl_action() {
+    check_actions(
+        r#"union foo$0() {}"#,
+        expect![[r#"
+                [
+                    Implementation(
+                        FilePosition {
+                            file_id: FileId(
+                                0,
+                            ),
+                            offset: 6,
+                        },
+                    ),
+                ]
+            "#]],
+    );
+}
+
+#[test]
+fn test_hover_enum_has_impl_action() {
+    check_actions(
+        r"enum foo$0() { A, B }",
+        expect![[r#"
+                [
+                    Implementation(
+                        FilePosition {
+                            file_id: FileId(
+                                0,
+                            ),
+                            offset: 5,
+                        },
+                    ),
+                ]
+            "#]],
+    );
+}
+
+#[test]
+fn test_hover_self_has_impl_action() {
+    check_actions(
+        r#"struct foo where Self$0:;"#,
+        expect![[r#"
+                [
+                    Implementation(
+                        FilePosition {
+                            file_id: FileId(
+                                0,
+                            ),
+                            offset: 7,
+                        },
+                    ),
+                ]
+            "#]],
+    );
+}
+
+#[test]
+fn test_hover_test_has_action() {
+    check_actions(
+        r#"
+#[test]
+fn foo_$0test() {}
+"#,
+        expect![[r#"
+                [
+                    Reference(
+                        FilePosition {
+                            file_id: FileId(
+                                0,
+                            ),
+                            offset: 11,
+                        },
+                    ),
+                    Runnable(
+                        Runnable {
+                            use_name_in_title: false,
+                            nav: NavigationTarget {
+                                file_id: FileId(
+                                    0,
+                                ),
+                                full_range: 0..24,
+                                focus_range: 11..19,
+                                name: "foo_test",
+                                kind: Function,
+                            },
+                            kind: Test {
+                                test_id: Path(
+                                    "foo_test",
+                                ),
+                                attr: TestAttr {
+                                    ignore: false,
+                                },
+                            },
+                            cfg: None,
+                        },
+                    ),
+                ]
+            "#]],
+    );
+}
+
+#[test]
+fn test_hover_test_mod_has_action() {
+    check_actions(
+        r#"
+mod tests$0 {
+    #[test]
+    fn foo_test() {}
+}
+"#,
+        expect![[r#"
+                [
+                    Runnable(
+                        Runnable {
+                            use_name_in_title: false,
+                            nav: NavigationTarget {
+                                file_id: FileId(
+                                    0,
+                                ),
+                                full_range: 0..46,
+                                focus_range: 4..9,
+                                name: "tests",
+                                kind: Module,
+                                description: "mod tests",
+                            },
+                            kind: TestMod {
+                                path: "tests",
+                            },
+                            cfg: None,
+                        },
+                    ),
+                ]
+            "#]],
+    );
+}
+
+#[test]
+fn test_hover_struct_has_goto_type_action() {
+    check_actions(
+        r#"
+struct S{ f1: u32 }
+
+fn main() { let s$0t = S{ f1:0 }; }
+"#,
+        expect![[r#"
+                [
+                    GoToType(
+                        [
+                            HoverGotoTypeData {
+                                mod_path: "test::S",
+                                nav: NavigationTarget {
+                                    file_id: FileId(
+                                        0,
+                                    ),
+                                    full_range: 0..19,
+                                    focus_range: 7..8,
+                                    name: "S",
+                                    kind: Struct,
+                                    description: "struct S",
+                                },
+                            },
+                        ],
+                    ),
+                ]
+            "#]],
+    );
+}
+
+#[test]
+fn test_hover_generic_struct_has_goto_type_actions() {
+    check_actions(
+        r#"
+struct Arg(u32);
+struct S<T>{ f1: T }
+
+fn main() { let s$0t = S{ f1:Arg(0) }; }
+"#,
+        expect![[r#"
+                [
+                    GoToType(
+                        [
+                            HoverGotoTypeData {
+                                mod_path: "test::S",
+                                nav: NavigationTarget {
+                                    file_id: FileId(
+                                        0,
+                                    ),
+                                    full_range: 17..37,
+                                    focus_range: 24..25,
+                                    name: "S",
+                                    kind: Struct,
+                                    description: "struct S<T>",
+                                },
+                            },
+                            HoverGotoTypeData {
+                                mod_path: "test::Arg",
+                                nav: NavigationTarget {
+                                    file_id: FileId(
+                                        0,
+                                    ),
+                                    full_range: 0..16,
+                                    focus_range: 7..10,
+                                    name: "Arg",
+                                    kind: Struct,
+                                    description: "struct Arg",
+                                },
+                            },
+                        ],
+                    ),
+                ]
+            "#]],
+    );
+}
+
+#[test]
+fn test_hover_generic_struct_has_flattened_goto_type_actions() {
+    check_actions(
+        r#"
+struct Arg(u32);
+struct S<T>{ f1: T }
+
+fn main() { let s$0t = S{ f1: S{ f1: Arg(0) } }; }
+"#,
+        expect![[r#"
+                [
+                    GoToType(
+                        [
+                            HoverGotoTypeData {
+                                mod_path: "test::S",
+                                nav: NavigationTarget {
+                                    file_id: FileId(
+                                        0,
+                                    ),
+                                    full_range: 17..37,
+                                    focus_range: 24..25,
+                                    name: "S",
+                                    kind: Struct,
+                                    description: "struct S<T>",
+                                },
+                            },
+                            HoverGotoTypeData {
+                                mod_path: "test::Arg",
+                                nav: NavigationTarget {
+                                    file_id: FileId(
+                                        0,
+                                    ),
+                                    full_range: 0..16,
+                                    focus_range: 7..10,
+                                    name: "Arg",
+                                    kind: Struct,
+                                    description: "struct Arg",
+                                },
+                            },
+                        ],
+                    ),
+                ]
+            "#]],
+    );
+}
+
+#[test]
+fn test_hover_tuple_has_goto_type_actions() {
+    check_actions(
+        r#"
+struct A(u32);
+struct B(u32);
+mod M {
+    pub struct C(u32);
+}
+
+fn main() { let s$0t = (A(1), B(2), M::C(3) ); }
+"#,
+        expect![[r#"
+                [
+                    GoToType(
+                        [
+                            HoverGotoTypeData {
+                                mod_path: "test::A",
+                                nav: NavigationTarget {
+                                    file_id: FileId(
+                                        0,
+                                    ),
+                                    full_range: 0..14,
+                                    focus_range: 7..8,
+                                    name: "A",
+                                    kind: Struct,
+                                    description: "struct A",
+                                },
+                            },
+                            HoverGotoTypeData {
+                                mod_path: "test::B",
+                                nav: NavigationTarget {
+                                    file_id: FileId(
+                                        0,
+                                    ),
+                                    full_range: 15..29,
+                                    focus_range: 22..23,
+                                    name: "B",
+                                    kind: Struct,
+                                    description: "struct B",
+                                },
+                            },
+                            HoverGotoTypeData {
+                                mod_path: "test::M::C",
+                                nav: NavigationTarget {
+                                    file_id: FileId(
+                                        0,
+                                    ),
+                                    full_range: 42..60,
+                                    focus_range: 53..54,
+                                    name: "C",
+                                    kind: Struct,
+                                    description: "pub struct C",
+                                },
+                            },
+                        ],
+                    ),
+                ]
+            "#]],
+    );
+}
+
+#[test]
+fn test_hover_return_impl_trait_has_goto_type_action() {
+    check_actions(
+        r#"
+trait Foo {}
+fn foo() -> impl Foo {}
+
+fn main() { let s$0t = foo(); }
+"#,
+        expect![[r#"
+                [
+                    GoToType(
+                        [
+                            HoverGotoTypeData {
+                                mod_path: "test::Foo",
+                                nav: NavigationTarget {
+                                    file_id: FileId(
+                                        0,
+                                    ),
+                                    full_range: 0..12,
+                                    focus_range: 6..9,
+                                    name: "Foo",
+                                    kind: Trait,
+                                    description: "trait Foo",
+                                },
+                            },
+                        ],
+                    ),
+                ]
+            "#]],
+    );
+}
+
+#[test]
+fn test_hover_generic_return_impl_trait_has_goto_type_action() {
+    check_actions(
+        r#"
+trait Foo<T> {}
+struct S;
+fn foo() -> impl Foo<S> {}
+
+fn main() { let s$0t = foo(); }
+"#,
+        expect![[r#"
+                [
+                    GoToType(
+                        [
+                            HoverGotoTypeData {
+                                mod_path: "test::Foo",
+                                nav: NavigationTarget {
+                                    file_id: FileId(
+                                        0,
+                                    ),
+                                    full_range: 0..15,
+                                    focus_range: 6..9,
+                                    name: "Foo",
+                                    kind: Trait,
+                                    description: "trait Foo<T>",
+                                },
+                            },
+                            HoverGotoTypeData {
+                                mod_path: "test::S",
+                                nav: NavigationTarget {
+                                    file_id: FileId(
+                                        0,
+                                    ),
+                                    full_range: 16..25,
+                                    focus_range: 23..24,
+                                    name: "S",
+                                    kind: Struct,
+                                    description: "struct S",
+                                },
+                            },
+                        ],
+                    ),
+                ]
+            "#]],
+    );
+}
+
+#[test]
+fn test_hover_return_impl_traits_has_goto_type_action() {
+    check_actions(
+        r#"
+trait Foo {}
+trait Bar {}
+fn foo() -> impl Foo + Bar {}
+
+fn main() { let s$0t = foo(); }
+"#,
+        expect![[r#"
+                [
+                    GoToType(
+                        [
+                            HoverGotoTypeData {
+                                mod_path: "test::Foo",
+                                nav: NavigationTarget {
+                                    file_id: FileId(
+                                        0,
+                                    ),
+                                    full_range: 0..12,
+                                    focus_range: 6..9,
+                                    name: "Foo",
+                                    kind: Trait,
+                                    description: "trait Foo",
+                                },
+                            },
+                            HoverGotoTypeData {
+                                mod_path: "test::Bar",
+                                nav: NavigationTarget {
+                                    file_id: FileId(
+                                        0,
+                                    ),
+                                    full_range: 13..25,
+                                    focus_range: 19..22,
+                                    name: "Bar",
+                                    kind: Trait,
+                                    description: "trait Bar",
+                                },
+                            },
+                        ],
+                    ),
+                ]
+            "#]],
+    );
+}
+
+#[test]
+fn test_hover_generic_return_impl_traits_has_goto_type_action() {
+    check_actions(
+        r#"
+trait Foo<T> {}
+trait Bar<T> {}
+struct S1 {}
+struct S2 {}
+
+fn foo() -> impl Foo<S1> + Bar<S2> {}
+
+fn main() { let s$0t = foo(); }
+"#,
+        expect![[r#"
+                [
+                    GoToType(
+                        [
+                            HoverGotoTypeData {
+                                mod_path: "test::Foo",
+                                nav: NavigationTarget {
+                                    file_id: FileId(
+                                        0,
+                                    ),
+                                    full_range: 0..15,
+                                    focus_range: 6..9,
+                                    name: "Foo",
+                                    kind: Trait,
+                                    description: "trait Foo<T>",
+                                },
+                            },
+                            HoverGotoTypeData {
+                                mod_path: "test::Bar",
+                                nav: NavigationTarget {
+                                    file_id: FileId(
+                                        0,
+                                    ),
+                                    full_range: 16..31,
+                                    focus_range: 22..25,
+                                    name: "Bar",
+                                    kind: Trait,
+                                    description: "trait Bar<T>",
+                                },
+                            },
+                            HoverGotoTypeData {
+                                mod_path: "test::S1",
+                                nav: NavigationTarget {
+                                    file_id: FileId(
+                                        0,
+                                    ),
+                                    full_range: 32..44,
+                                    focus_range: 39..41,
+                                    name: "S1",
+                                    kind: Struct,
+                                    description: "struct S1",
+                                },
+                            },
+                            HoverGotoTypeData {
+                                mod_path: "test::S2",
+                                nav: NavigationTarget {
+                                    file_id: FileId(
+                                        0,
+                                    ),
+                                    full_range: 45..57,
+                                    focus_range: 52..54,
+                                    name: "S2",
+                                    kind: Struct,
+                                    description: "struct S2",
+                                },
+                            },
+                        ],
+                    ),
+                ]
+            "#]],
+    );
+}
+
+#[test]
+fn test_hover_arg_impl_trait_has_goto_type_action() {
+    check_actions(
+        r#"
+trait Foo {}
+fn foo(ar$0g: &impl Foo) {}
+"#,
+        expect![[r#"
+                [
+                    GoToType(
+                        [
+                            HoverGotoTypeData {
+                                mod_path: "test::Foo",
+                                nav: NavigationTarget {
+                                    file_id: FileId(
+                                        0,
+                                    ),
+                                    full_range: 0..12,
+                                    focus_range: 6..9,
+                                    name: "Foo",
+                                    kind: Trait,
+                                    description: "trait Foo",
+                                },
+                            },
+                        ],
+                    ),
+                ]
+            "#]],
+    );
+}
+
+#[test]
+fn test_hover_arg_impl_traits_has_goto_type_action() {
+    check_actions(
+        r#"
+trait Foo {}
+trait Bar<T> {}
+struct S{}
+
+fn foo(ar$0g: &impl Foo + Bar<S>) {}
+"#,
+        expect![[r#"
+                [
+                    GoToType(
+                        [
+                            HoverGotoTypeData {
+                                mod_path: "test::Foo",
+                                nav: NavigationTarget {
+                                    file_id: FileId(
+                                        0,
+                                    ),
+                                    full_range: 0..12,
+                                    focus_range: 6..9,
+                                    name: "Foo",
+                                    kind: Trait,
+                                    description: "trait Foo",
+                                },
+                            },
+                            HoverGotoTypeData {
+                                mod_path: "test::Bar",
+                                nav: NavigationTarget {
+                                    file_id: FileId(
+                                        0,
+                                    ),
+                                    full_range: 13..28,
+                                    focus_range: 19..22,
+                                    name: "Bar",
+                                    kind: Trait,
+                                    description: "trait Bar<T>",
+                                },
+                            },
+                            HoverGotoTypeData {
+                                mod_path: "test::S",
+                                nav: NavigationTarget {
+                                    file_id: FileId(
+                                        0,
+                                    ),
+                                    full_range: 29..39,
+                                    focus_range: 36..37,
+                                    name: "S",
+                                    kind: Struct,
+                                    description: "struct S",
+                                },
+                            },
+                        ],
+                    ),
+                ]
+            "#]],
+    );
+}
+
+#[test]
+fn test_hover_async_block_impl_trait_has_goto_type_action() {
+    check_actions(
+        r#"
+//- minicore: future
+struct S;
+fn foo() {
+    let fo$0o = async { S };
+}
+"#,
+        expect![[r#"
+                [
+                    GoToType(
+                        [
+                            HoverGotoTypeData {
+                                mod_path: "core::future::Future",
+                                nav: NavigationTarget {
+                                    file_id: FileId(
+                                        1,
+                                    ),
+                                    full_range: 254..436,
+                                    focus_range: 293..299,
+                                    name: "Future",
+                                    kind: Trait,
+                                    description: "pub trait Future",
+                                },
+                            },
+                            HoverGotoTypeData {
+                                mod_path: "test::S",
+                                nav: NavigationTarget {
+                                    file_id: FileId(
+                                        0,
+                                    ),
+                                    full_range: 0..9,
+                                    focus_range: 7..8,
+                                    name: "S",
+                                    kind: Struct,
+                                    description: "struct S",
+                                },
+                            },
+                        ],
+                    ),
+                ]
+            "#]],
+    );
+}
+
+#[test]
+fn test_hover_arg_generic_impl_trait_has_goto_type_action() {
+    check_actions(
+        r#"
+trait Foo<T> {}
+struct S {}
+fn foo(ar$0g: &impl Foo<S>) {}
+"#,
+        expect![[r#"
+                [
+                    GoToType(
+                        [
+                            HoverGotoTypeData {
+                                mod_path: "test::Foo",
+                                nav: NavigationTarget {
+                                    file_id: FileId(
+                                        0,
+                                    ),
+                                    full_range: 0..15,
+                                    focus_range: 6..9,
+                                    name: "Foo",
+                                    kind: Trait,
+                                    description: "trait Foo<T>",
+                                },
+                            },
+                            HoverGotoTypeData {
+                                mod_path: "test::S",
+                                nav: NavigationTarget {
+                                    file_id: FileId(
+                                        0,
+                                    ),
+                                    full_range: 16..27,
+                                    focus_range: 23..24,
+                                    name: "S",
+                                    kind: Struct,
+                                    description: "struct S",
+                                },
+                            },
+                        ],
+                    ),
+                ]
+            "#]],
+    );
+}
+
+#[test]
+fn test_hover_dyn_return_has_goto_type_action() {
+    check_actions(
+        r#"
+trait Foo {}
+struct S;
+impl Foo for S {}
+
+struct B<T>{}
+fn foo() -> B<dyn Foo> {}
+
+fn main() { let s$0t = foo(); }
+"#,
+        expect![[r#"
+                [
+                    GoToType(
+                        [
+                            HoverGotoTypeData {
+                                mod_path: "test::B",
+                                nav: NavigationTarget {
+                                    file_id: FileId(
+                                        0,
+                                    ),
+                                    full_range: 42..55,
+                                    focus_range: 49..50,
+                                    name: "B",
+                                    kind: Struct,
+                                    description: "struct B<T>",
+                                },
+                            },
+                            HoverGotoTypeData {
+                                mod_path: "test::Foo",
+                                nav: NavigationTarget {
+                                    file_id: FileId(
+                                        0,
+                                    ),
+                                    full_range: 0..12,
+                                    focus_range: 6..9,
+                                    name: "Foo",
+                                    kind: Trait,
+                                    description: "trait Foo",
+                                },
+                            },
+                        ],
+                    ),
+                ]
+            "#]],
+    );
+}
+
+#[test]
+fn test_hover_dyn_arg_has_goto_type_action() {
+    check_actions(
+        r#"
+trait Foo {}
+fn foo(ar$0g: &dyn Foo) {}
+"#,
+        expect![[r#"
+                [
+                    GoToType(
+                        [
+                            HoverGotoTypeData {
+                                mod_path: "test::Foo",
+                                nav: NavigationTarget {
+                                    file_id: FileId(
+                                        0,
+                                    ),
+                                    full_range: 0..12,
+                                    focus_range: 6..9,
+                                    name: "Foo",
+                                    kind: Trait,
+                                    description: "trait Foo",
+                                },
+                            },
+                        ],
+                    ),
+                ]
+            "#]],
+    );
+}
+
+#[test]
+fn test_hover_generic_dyn_arg_has_goto_type_action() {
+    check_actions(
+        r#"
+trait Foo<T> {}
+struct S {}
+fn foo(ar$0g: &dyn Foo<S>) {}
+"#,
+        expect![[r#"
+                [
+                    GoToType(
+                        [
+                            HoverGotoTypeData {
+                                mod_path: "test::Foo",
+                                nav: NavigationTarget {
+                                    file_id: FileId(
+                                        0,
+                                    ),
+                                    full_range: 0..15,
+                                    focus_range: 6..9,
+                                    name: "Foo",
+                                    kind: Trait,
+                                    description: "trait Foo<T>",
+                                },
+                            },
+                            HoverGotoTypeData {
+                                mod_path: "test::S",
+                                nav: NavigationTarget {
+                                    file_id: FileId(
+                                        0,
+                                    ),
+                                    full_range: 16..27,
+                                    focus_range: 23..24,
+                                    name: "S",
+                                    kind: Struct,
+                                    description: "struct S",
+                                },
+                            },
+                        ],
+                    ),
+                ]
+            "#]],
+    );
+}
+
+#[test]
+fn test_hover_goto_type_action_links_order() {
+    check_actions(
+        r#"
+trait ImplTrait<T> {}
+trait DynTrait<T> {}
+struct B<T> {}
+struct S {}
+
+fn foo(a$0rg: &impl ImplTrait<B<dyn DynTrait<B<S>>>>) {}
+"#,
+        expect![[r#"
+                [
+                    GoToType(
+                        [
+                            HoverGotoTypeData {
+                                mod_path: "test::ImplTrait",
+                                nav: NavigationTarget {
+                                    file_id: FileId(
+                                        0,
+                                    ),
+                                    full_range: 0..21,
+                                    focus_range: 6..15,
+                                    name: "ImplTrait",
+                                    kind: Trait,
+                                    description: "trait ImplTrait<T>",
+                                },
+                            },
+                            HoverGotoTypeData {
+                                mod_path: "test::B",
+                                nav: NavigationTarget {
+                                    file_id: FileId(
+                                        0,
+                                    ),
+                                    full_range: 43..57,
+                                    focus_range: 50..51,
+                                    name: "B",
+                                    kind: Struct,
+                                    description: "struct B<T>",
+                                },
+                            },
+                            HoverGotoTypeData {
+                                mod_path: "test::DynTrait",
+                                nav: NavigationTarget {
+                                    file_id: FileId(
+                                        0,
+                                    ),
+                                    full_range: 22..42,
+                                    focus_range: 28..36,
+                                    name: "DynTrait",
+                                    kind: Trait,
+                                    description: "trait DynTrait<T>",
+                                },
+                            },
+                            HoverGotoTypeData {
+                                mod_path: "test::S",
+                                nav: NavigationTarget {
+                                    file_id: FileId(
+                                        0,
+                                    ),
+                                    full_range: 58..69,
+                                    focus_range: 65..66,
+                                    name: "S",
+                                    kind: Struct,
+                                    description: "struct S",
+                                },
+                            },
+                        ],
+                    ),
+                ]
+            "#]],
+    );
+}
+
+#[test]
+fn test_hover_associated_type_has_goto_type_action() {
+    check_actions(
+        r#"
+trait Foo {
+    type Item;
+    fn get(self) -> Self::Item {}
+}
+
+struct Bar{}
+struct S{}
+
+impl Foo for S { type Item = Bar; }
+
+fn test() -> impl Foo { S {} }
+
+fn main() { let s$0t = test().get(); }
+"#,
+        expect![[r#"
+                [
+                    GoToType(
+                        [
+                            HoverGotoTypeData {
+                                mod_path: "test::Foo",
+                                nav: NavigationTarget {
+                                    file_id: FileId(
+                                        0,
+                                    ),
+                                    full_range: 0..62,
+                                    focus_range: 6..9,
+                                    name: "Foo",
+                                    kind: Trait,
+                                    description: "trait Foo",
+                                },
+                            },
+                        ],
+                    ),
+                ]
+            "#]],
+    );
+}
+
+#[test]
+fn test_hover_const_param_has_goto_type_action() {
+    check_actions(
+        r#"
+struct Bar;
+struct Foo<const BAR: Bar>;
+
+impl<const BAR: Bar> Foo<BAR$0> {}
+"#,
+        expect![[r#"
+                [
+                    GoToType(
+                        [
+                            HoverGotoTypeData {
+                                mod_path: "test::Bar",
+                                nav: NavigationTarget {
+                                    file_id: FileId(
+                                        0,
+                                    ),
+                                    full_range: 0..11,
+                                    focus_range: 7..10,
+                                    name: "Bar",
+                                    kind: Struct,
+                                    description: "struct Bar",
+                                },
+                            },
+                        ],
+                    ),
+                ]
+            "#]],
+    );
+}
+
+#[test]
+fn test_hover_type_param_has_goto_type_action() {
+    check_actions(
+        r#"
+trait Foo {}
+
+fn foo<T: Foo>(t: T$0){}
+"#,
+        expect![[r#"
+                [
+                    GoToType(
+                        [
+                            HoverGotoTypeData {
+                                mod_path: "test::Foo",
+                                nav: NavigationTarget {
+                                    file_id: FileId(
+                                        0,
+                                    ),
+                                    full_range: 0..12,
+                                    focus_range: 6..9,
+                                    name: "Foo",
+                                    kind: Trait,
+                                    description: "trait Foo",
+                                },
+                            },
+                        ],
+                    ),
+                ]
+            "#]],
+    );
+}
+
+#[test]
+fn test_hover_self_has_go_to_type() {
+    check_actions(
+        r#"
+struct Foo;
+impl Foo {
+    fn foo(&self$0) {}
+}
+"#,
+        expect![[r#"
+                [
+                    GoToType(
+                        [
+                            HoverGotoTypeData {
+                                mod_path: "test::Foo",
+                                nav: NavigationTarget {
+                                    file_id: FileId(
+                                        0,
+                                    ),
+                                    full_range: 0..11,
+                                    focus_range: 7..10,
+                                    name: "Foo",
+                                    kind: Struct,
+                                    description: "struct Foo",
+                                },
+                            },
+                        ],
+                    ),
+                ]
+            "#]],
+    );
+}
+
+#[test]
+fn hover_displays_normalized_crate_names() {
+    check(
+        r#"
+//- /lib.rs crate:name-with-dashes
+pub mod wrapper {
+    pub struct Thing { x: u32 }
+
+    impl Thing {
+        pub fn new() -> Thing { Thing { x: 0 } }
+    }
+}
+
+//- /main.rs crate:main deps:name-with-dashes
+fn main() { let foo_test = name_with_dashes::wrapper::Thing::new$0(); }
+"#,
+        expect![[r#"
+            *new*
+
+            ```rust
+            name_with_dashes::wrapper::Thing
+            ```
+
+            ```rust
+            pub fn new() -> Thing
+            ```
+            "#]],
+    )
+}
+
+#[test]
+fn hover_field_pat_shorthand_ref_match_ergonomics() {
+    check(
+        r#"
+struct S {
+    f: i32,
+}
+
+fn main() {
+    let s = S { f: 0 };
+    let S { f$0 } = &s;
+}
+"#,
+        expect![[r#"
+                *f*
+
+                ```rust
+                f: &i32
+                ```
+                ---
+
+                ```rust
+                test::S
+                ```
+
+                ```rust
+                f: i32
+                ```
+            "#]],
+    );
+}
+
+#[test]
+fn hover_self_param_shows_type() {
+    check(
+        r#"
+struct Foo {}
+impl Foo {
+    fn bar(&sel$0f) {}
+}
+"#,
+        expect![[r#"
+                *self*
+
+                ```rust
+                self: &Foo
+                ```
+            "#]],
+    );
+}
+
+#[test]
+fn hover_self_param_shows_type_for_arbitrary_self_type() {
+    check(
+        r#"
+struct Arc<T>(T);
+struct Foo {}
+impl Foo {
+    fn bar(sel$0f: Arc<Foo>) {}
+}
+"#,
+        expect![[r#"
+                *self*
+
+                ```rust
+                self: Arc<Foo>
+                ```
+            "#]],
+    );
+}
+
+#[test]
+fn hover_doc_outer_inner() {
+    check(
+        r#"
+/// Be quick;
+mod Foo$0 {
+    //! time is mana
+
+    /// This comment belongs to the function
+    fn foo() {}
+}
+"#,
+        expect![[r#"
+                *Foo*
+
+                ```rust
+                test
+                ```
+
+                ```rust
+                mod Foo
+                ```
+
+                ---
+
+                Be quick;
+                time is mana
+            "#]],
+    );
+}
+
+#[test]
+fn hover_doc_outer_inner_attribue() {
+    check(
+        r#"
+#[doc = "Be quick;"]
+mod Foo$0 {
+    #![doc = "time is mana"]
+
+    #[doc = "This comment belongs to the function"]
+    fn foo() {}
+}
+"#,
+        expect![[r#"
+                *Foo*
+
+                ```rust
+                test
+                ```
+
+                ```rust
+                mod Foo
+                ```
+
+                ---
+
+                Be quick;
+                time is mana
+            "#]],
+    );
+}
+
+#[test]
+fn hover_doc_block_style_indentend() {
+    check(
+        r#"
+/**
+    foo
+    ```rust
+    let x = 3;
+    ```
+*/
+fn foo$0() {}
+"#,
+        expect![[r#"
+                *foo*
+
+                ```rust
+                test
+                ```
+
+                ```rust
+                fn foo()
+                ```
+
+                ---
+
+                foo
+
+                ```rust
+                let x = 3;
+                ```
+            "#]],
+    );
+}
+
+#[test]
+fn hover_comments_dont_highlight_parent() {
+    cov_mark::check!(no_highlight_on_comment_hover);
+    check_hover_no_result(
+        r#"
+fn no_hover() {
+    // no$0hover
+}
+"#,
+    );
+}
+
+#[test]
+fn hover_label() {
+    check(
+        r#"
+fn foo() {
+    'label$0: loop {}
+}
+"#,
+        expect![[r#"
+            *'label*
+
+            ```rust
+            'label
+            ```
+            "#]],
+    );
+}
+
+#[test]
+fn hover_lifetime() {
+    check(
+        r#"fn foo<'lifetime>(_: &'lifetime$0 ()) {}"#,
+        expect![[r#"
+            *'lifetime*
+
+            ```rust
+            'lifetime
+            ```
+            "#]],
+    );
+}
+
+#[test]
+fn hover_type_param() {
+    check(
+        r#"
+//- minicore: sized
+struct Foo<T>(T);
+trait TraitA {}
+trait TraitB {}
+impl<T: TraitA + TraitB> Foo<T$0> where T: Sized {}
+"#,
+        expect![[r#"
+                *T*
+
+                ```rust
+                T: TraitA + TraitB
+                ```
+            "#]],
+    );
+    check(
+        r#"
+//- minicore: sized
+struct Foo<T>(T);
+impl<T> Foo<T$0> {}
+"#,
+        expect![[r#"
+                *T*
+
+                ```rust
+                T
+                ```
+                "#]],
+    );
+    // lifetimes bounds arent being tracked yet
+    check(
+        r#"
+//- minicore: sized
+struct Foo<T>(T);
+impl<T: 'static> Foo<T$0> {}
+"#,
+        expect![[r#"
+                *T*
+
+                ```rust
+                T
+                ```
+                "#]],
+    );
+}
+
+#[test]
+fn hover_type_param_sized_bounds() {
+    // implicit `: Sized` bound
+    check(
+        r#"
+//- minicore: sized
+trait Trait {}
+struct Foo<T>(T);
+impl<T: Trait> Foo<T$0> {}
+"#,
+        expect![[r#"
+                *T*
+
+                ```rust
+                T: Trait
+                ```
+            "#]],
+    );
+    check(
+        r#"
+//- minicore: sized
+trait Trait {}
+struct Foo<T>(T);
+impl<T: Trait + ?Sized> Foo<T$0> {}
+"#,
+        expect![[r#"
+                *T*
+
+                ```rust
+                T: Trait + ?Sized
+                ```
+            "#]],
+    );
+}
+
+mod type_param_sized_bounds {
+    use super::*;
+
+    #[test]
+    fn single_implicit() {
+        check(
+            r#"
+//- minicore: sized
+fn foo<T$0>() {}
+"#,
+            expect![[r#"
+                    *T*
+
+                    ```rust
+                    T
+                    ```
+                "#]],
+        );
+    }
+
+    #[test]
+    fn single_explicit() {
+        check(
+            r#"
+//- minicore: sized
+fn foo<T$0: Sized>() {}
+"#,
+            expect![[r#"
+                    *T*
+
+                    ```rust
+                    T
+                    ```
+                "#]],
+        );
+    }
+
+    #[test]
+    fn single_relaxed() {
+        check(
+            r#"
+//- minicore: sized
+fn foo<T$0: ?Sized>() {}
+"#,
+            expect![[r#"
+                    *T*
+
+                    ```rust
+                    T: ?Sized
+                    ```
+                "#]],
+        );
+    }
+
+    #[test]
+    fn multiple_implicit() {
+        check(
+            r#"
+//- minicore: sized
+trait Trait {}
+fn foo<T$0: Trait>() {}
+"#,
+            expect![[r#"
+                    *T*
+
+                    ```rust
+                    T: Trait
+                    ```
+                "#]],
+        );
+    }
+
+    #[test]
+    fn multiple_explicit() {
+        check(
+            r#"
+//- minicore: sized
+trait Trait {}
+fn foo<T$0: Trait + Sized>() {}
+"#,
+            expect![[r#"
+                    *T*
+
+                    ```rust
+                    T: Trait
+                    ```
+                "#]],
+        );
+    }
+
+    #[test]
+    fn multiple_relaxed() {
+        check(
+            r#"
+//- minicore: sized
+trait Trait {}
+fn foo<T$0: Trait + ?Sized>() {}
+"#,
+            expect![[r#"
+                    *T*
+
+                    ```rust
+                    T: Trait + ?Sized
+                    ```
+                "#]],
+        );
+    }
+
+    #[test]
+    fn mixed() {
+        check(
+            r#"
+//- minicore: sized
+fn foo<T$0: ?Sized + Sized + Sized>() {}
+"#,
+            expect![[r#"
+                    *T*
+
+                    ```rust
+                    T
+                    ```
+                "#]],
+        );
+        check(
+            r#"
+//- minicore: sized
+trait Trait {}
+fn foo<T$0: Sized + ?Sized + Sized + Trait>() {}
+"#,
+            expect![[r#"
+                    *T*
+
+                    ```rust
+                    T: Trait
+                    ```
+                "#]],
+        );
+    }
+}
+
+#[test]
+fn hover_const_param() {
+    check(
+        r#"
+struct Foo<const LEN: usize>;
+impl<const LEN: usize> Foo<LEN$0> {}
+"#,
+        expect![[r#"
+                *LEN*
+
+                ```rust
+                const LEN: usize
+                ```
+            "#]],
+    );
+}
+
+#[test]
+fn hover_const_pat() {
+    check(
+        r#"
+/// This is a doc
+const FOO: usize = 3;
+fn foo() {
+    match 5 {
+        FOO$0 => (),
+        _ => ()
+    }
+}
+"#,
+        expect![[r#"
+                *FOO*
+
+                ```rust
+                test
+                ```
+
+                ```rust
+                const FOO: usize
+                ```
+
+                ---
+
+                This is a doc
+            "#]],
+    );
+}
+
+#[test]
+fn hover_mod_def() {
+    check(
+        r#"
+//- /main.rs
+mod foo$0;
+//- /foo.rs
+//! For the horde!
+"#,
+        expect![[r#"
+                *foo*
+
+                ```rust
+                test
+                ```
+
+                ```rust
+                mod foo
+                ```
+
+                ---
+
+                For the horde!
+            "#]],
+    );
+}
+
+#[test]
+fn hover_self_in_use() {
+    check(
+        r#"
+//! This should not appear
+mod foo {
+    /// But this should appear
+    pub mod bar {}
+}
+use foo::bar::{self$0};
+"#,
+        expect![[r#"
+                *self*
+
+                ```rust
+                test::foo
+                ```
+
+                ```rust
+                mod bar
+                ```
+
+                ---
+
+                But this should appear
+            "#]],
+    )
+}
+
+#[test]
+fn hover_keyword() {
+    check(
+        r#"
+//- /main.rs crate:main deps:std
+fn f() { retur$0n; }
+//- /libstd.rs crate:std
+/// Docs for return_keyword
+mod return_keyword {}
+"#,
+        expect![[r#"
+                *return*
+
+                ```rust
+                return
+                ```
+
+                ---
+
+                Docs for return_keyword
+            "#]],
+    );
+}
+
+#[test]
+fn hover_builtin() {
+    check(
+        r#"
+//- /main.rs crate:main deps:std
+cosnt _: &str$0 = ""; }
+
+//- /libstd.rs crate:std
+/// Docs for prim_str
+mod prim_str {}
+"#,
+        expect![[r#"
+                *str*
+
+                ```rust
+                str
+                ```
+
+                ---
+
+                Docs for prim_str
+            "#]],
+    );
+}
+
+#[test]
+fn hover_macro_expanded_function() {
+    check(
+        r#"
+struct S<'a, T>(&'a T);
+trait Clone {}
+macro_rules! foo {
+    () => {
+        fn bar<'t, T: Clone + 't>(s: &mut S<'t, T>, t: u32) -> *mut u32 where
+            't: 't + 't,
+            for<'a> T: Clone + 'a
+        { 0 as _ }
+    };
+}
+
+foo!();
+
+fn main() {
+    bar$0;
+}
+"#,
+        expect![[r#"
+                *bar*
+
+                ```rust
+                test
+                ```
+
+                ```rust
+                fn bar<'t, T>(s: &mut S<'t, T>, t: u32) -> *mut u32
+                where
+                    T: Clone + 't,
+                    't: 't + 't,
+                    for<'a> T: Clone + 'a,
+                ```
+            "#]],
+    )
+}
+
+#[test]
+fn hover_intra_doc_links() {
+    check(
+        r#"
+
+pub mod theitem {
+    /// This is the item. Cool!
+    pub struct TheItem;
+}
+
+/// Gives you a [`TheItem$0`].
+///
+/// [`TheItem`]: theitem::TheItem
+pub fn gimme() -> theitem::TheItem {
+    theitem::TheItem
+}
+"#,
+        expect![[r#"
+                *[`TheItem`]*
+
+                ```rust
+                test::theitem
+                ```
+
+                ```rust
+                pub struct TheItem
+                ```
+
+                ---
+
+                This is the item. Cool!
+            "#]],
+    );
+}
+
+#[test]
+fn hover_generic_assoc() {
+    check(
+        r#"
+fn foo<T: A>() where T::Assoc$0: {}
+
+trait A {
+    type Assoc;
+}"#,
+        expect![[r#"
+                *Assoc*
+
+                ```rust
+                test
+                ```
+
+                ```rust
+                type Assoc
+                ```
+            "#]],
+    );
+    check(
+        r#"
+fn foo<T: A>() {
+    let _: <T>::Assoc$0;
+}
+
+trait A {
+    type Assoc;
+}"#,
+        expect![[r#"
+                *Assoc*
+
+                ```rust
+                test
+                ```
+
+                ```rust
+                type Assoc
+                ```
+            "#]],
+    );
+    check(
+        r#"
+trait A where
+    Self::Assoc$0: ,
+{
+    type Assoc;
+}"#,
+        expect![[r#"
+                *Assoc*
+
+                ```rust
+                test
+                ```
+
+                ```rust
+                type Assoc
+                ```
+            "#]],
+    );
+}
+
+#[test]
+fn string_shadowed_with_inner_items() {
+    check(
+        r#"
+//- /main.rs crate:main deps:alloc
+
+/// Custom `String` type.
+struct String;
+
+fn f() {
+    let _: String$0;
+
+    fn inner() {}
+}
+
+//- /alloc.rs crate:alloc
+#[prelude_import]
+pub use string::*;
+
+mod string {
+    /// This is `alloc::String`.
+    pub struct String;
+}
+"#,
+        expect![[r#"
+                *String*
+
+                ```rust
+                main
+                ```
+
+                ```rust
+                struct String
+                ```
+
+                ---
+
+                Custom `String` type.
+            "#]],
+    )
+}
+
+#[test]
+fn function_doesnt_shadow_crate_in_use_tree() {
+    check(
+        r#"
+//- /main.rs crate:main deps:foo
+use foo$0::{foo};
+
+//- /foo.rs crate:foo
+pub fn foo() {}
+"#,
+        expect![[r#"
+                *foo*
+
+                ```rust
+                extern crate foo
+                ```
+            "#]],
+    )
+}
+
+#[test]
+fn hover_feature() {
+    check(
+        r#"#![feature(box_syntax$0)]"#,
+        expect![[r##"
+                *box_syntax*
+                ```
+                box_syntax
+                ```
+                ___
+
+                # `box_syntax`
+
+                The tracking issue for this feature is: [#49733]
+
+                [#49733]: https://github.com/rust-lang/rust/issues/49733
+
+                See also [`box_patterns`](box-patterns.md)
+
+                ------------------------
+
+                Currently the only stable way to create a `Box` is via the `Box::new` method.
+                Also it is not possible in stable Rust to destructure a `Box` in a match
+                pattern. The unstable `box` keyword can be used to create a `Box`. An example
+                usage would be:
+
+                ```rust
+                #![feature(box_syntax)]
+
+                fn main() {
+                    let b = box 5;
+                }
+                ```
+
+            "##]],
+    )
+}
+
+#[test]
+fn hover_lint() {
+    check(
+        r#"#![allow(arithmetic_overflow$0)]"#,
+        expect![[r#"
+                *arithmetic_overflow*
+                ```
+                arithmetic_overflow
+                ```
+                ___
+
+                arithmetic operation overflows
+            "#]],
+    )
+}
+
+#[test]
+fn hover_clippy_lint() {
+    check(
+        r#"#![allow(clippy::almost_swapped$0)]"#,
+        expect![[r#"
+                *almost_swapped*
+                ```
+                clippy::almost_swapped
+                ```
+                ___
+
+                Checks for `foo = bar; bar = foo` sequences.
+            "#]],
+    )
+}
+
+#[test]
+fn hover_attr_path_qualifier() {
+    cov_mark::check!(name_ref_classify_attr_path_qualifier);
+    check(
+        r#"
+//- /foo.rs crate:foo
+
+//- /lib.rs crate:main.rs deps:foo
+#[fo$0o::bar()]
+struct Foo;
+"#,
+        expect![[r#"
+                *foo*
+
+                ```rust
+                extern crate foo
+                ```
+            "#]],
+    )
+}
+
+#[test]
+fn hover_rename() {
+    check(
+        r#"
+use self as foo$0;
+"#,
+        expect![[r#"
+                *foo*
+
+                ```rust
+                extern crate test
+                ```
+            "#]],
+    );
+    check(
+        r#"
+mod bar {}
+use bar::{self as foo$0};
+"#,
+        expect![[r#"
+                *foo*
+
+                ```rust
+                test
+                ```
+
+                ```rust
+                mod bar
+                ```
+            "#]],
+    );
+    check(
+        r#"
+mod bar {
+    use super as foo$0;
+}
+"#,
+        expect![[r#"
+                *foo*
+
+                ```rust
+                extern crate test
+                ```
+            "#]],
+    );
+    check(
+        r#"
+use crate as foo$0;
+"#,
+        expect![[r#"
+                *foo*
+
+                ```rust
+                extern crate test
+                ```
+            "#]],
+    );
+}
+
+#[test]
+fn hover_attribute_in_macro() {
+    check(
+        r#"
+macro_rules! identity {
+    ($struct:item) => {
+        $struct
+    };
+}
+#[rustc_builtin_macro]
+pub macro Copy {}
+identity!{
+    #[derive(Copy$0)]
+    struct Foo;
+}
+"#,
+        expect![[r#"
+                *Copy*
+
+                ```rust
+                test
+                ```
+
+                ```rust
+                pub macro Copy
+                ```
+            "#]],
+    );
+}
+
+#[test]
+fn hover_derive_input() {
+    check(
+        r#"
+#[rustc_builtin_macro]
+pub macro Copy {}
+#[derive(Copy$0)]
+struct Foo;
+"#,
+        expect![[r#"
+                *Copy*
+
+                ```rust
+                test
+                ```
+
+                ```rust
+                pub macro Copy
+                ```
+            "#]],
+    );
+    check(
+        r#"
+mod foo {
+    #[rustc_builtin_macro]
+    pub macro Copy {}
+}
+#[derive(foo::Copy$0)]
+struct Foo;
+"#,
+        expect![[r#"
+                *Copy*
+
+                ```rust
+                test
+                ```
+
+                ```rust
+                pub macro Copy
+                ```
+            "#]],
+    );
+}
+
+#[test]
+fn hover_range_math() {
+    check_hover_range(
+        r#"
+fn f() { let expr = $01 + 2 * 3$0 }
+"#,
+        expect![[r#"
+            ```rust
+            i32
+            ```"#]],
+    );
+
+    check_hover_range(
+        r#"
+fn f() { let expr = 1 $0+ 2 * $03 }
+"#,
+        expect![[r#"
+            ```rust
+            i32
+            ```"#]],
+    );
+
+    check_hover_range(
+        r#"
+fn f() { let expr = 1 + $02 * 3$0 }
+"#,
+        expect![[r#"
+            ```rust
+            i32
+            ```"#]],
+    );
+}
+
+#[test]
+fn hover_range_arrays() {
+    check_hover_range(
+        r#"
+fn f() { let expr = $0[1, 2, 3, 4]$0 }
+"#,
+        expect![[r#"
+            ```rust
+            [i32; 4]
+            ```"#]],
+    );
+
+    check_hover_range(
+        r#"
+fn f() { let expr = [1, 2, $03, 4]$0 }
+"#,
+        expect![[r#"
+            ```rust
+            [i32; 4]
+            ```"#]],
+    );
+
+    check_hover_range(
+        r#"
+fn f() { let expr = [1, 2, $03$0, 4] }
+"#,
+        expect![[r#"
+            ```rust
+            i32
+            ```"#]],
+    );
+}
+
+#[test]
+fn hover_range_functions() {
+    check_hover_range(
+        r#"
+fn f<T>(a: &[T]) { }
+fn b() { $0f$0(&[1, 2, 3, 4, 5]); }
+"#,
+        expect![[r#"
+            ```rust
+            fn f<i32>(&[i32])
+            ```"#]],
+    );
+
+    check_hover_range(
+        r#"
+fn f<T>(a: &[T]) { }
+fn b() { f($0&[1, 2, 3, 4, 5]$0); }
+"#,
+        expect![[r#"
+            ```rust
+            &[i32; 5]
+            ```"#]],
+    );
+}
+
+#[test]
+fn hover_range_shows_nothing_when_invalid() {
+    check_hover_range_no_results(
+        r#"
+fn f<T>(a: &[T]) { }
+fn b()$0 { f(&[1, 2, 3, 4, 5]); }$0
+"#,
+    );
+
+    check_hover_range_no_results(
+        r#"
+fn f<T>$0(a: &[T]) { }
+fn b() { f(&[1, 2, 3,$0 4, 5]); }
+"#,
+    );
+
+    check_hover_range_no_results(
+        r#"
+fn $0f() { let expr = [1, 2, 3, 4]$0 }
+"#,
+    );
+}
+
+#[test]
+fn hover_range_shows_unit_for_statements() {
+    check_hover_range(
+        r#"
+fn f<T>(a: &[T]) { }
+fn b() { $0f(&[1, 2, 3, 4, 5]); }$0
+"#,
+        expect![[r#"
+            ```rust
+            ()
+            ```"#]],
+    );
+
+    check_hover_range(
+        r#"
+fn f() { let expr$0 = $0[1, 2, 3, 4] }
+"#,
+        expect![[r#"
+            ```rust
+            ()
+            ```"#]],
+    );
+}
+
+#[test]
+fn hover_range_for_pat() {
+    check_hover_range(
+        r#"
+fn foo() {
+    let $0x$0 = 0;
+}
+"#,
+        expect![[r#"
+                ```rust
+                i32
+                ```"#]],
+    );
+
+    check_hover_range(
+        r#"
+fn foo() {
+    let $0x$0 = "";
+}
+"#,
+        expect![[r#"
+                ```rust
+                &str
+                ```"#]],
+    );
+}
+
+#[test]
+fn hover_range_shows_coercions_if_applicable_expr() {
+    check_hover_range(
+        r#"
+fn foo() {
+    let x: &u32 = $0&&&&&0$0;
+}
+"#,
+        expect![[r#"
+                ```text
+                Type:       &&&&&u32
+                Coerced to:     &u32
+                ```
+            "#]],
+    );
+    check_hover_range(
+        r#"
+fn foo() {
+    let x: *const u32 = $0&0$0;
+}
+"#,
+        expect![[r#"
+                ```text
+                Type:             &u32
+                Coerced to: *const u32
+                ```
+            "#]],
+    );
+}
+
+#[test]
+fn hover_range_shows_type_actions() {
+    check_actions(
+        r#"
+struct Foo;
+fn foo() {
+    let x: &Foo = $0&&&&&Foo$0;
+}
+"#,
+        expect![[r#"
+                [
+                    GoToType(
+                        [
+                            HoverGotoTypeData {
+                                mod_path: "test::Foo",
+                                nav: NavigationTarget {
+                                    file_id: FileId(
+                                        0,
+                                    ),
+                                    full_range: 0..11,
+                                    focus_range: 7..10,
+                                    name: "Foo",
+                                    kind: Struct,
+                                    description: "struct Foo",
+                                },
+                            },
+                        ],
+                    ),
+                ]
+            "#]],
+    );
+}
+
+#[test]
+fn hover_try_expr_res() {
+    check_hover_range(
+        r#"
+//- minicore:result
+struct FooError;
+
+fn foo() -> Result<(), FooError> {
+    Ok($0Result::<(), FooError>::Ok(())?$0)
+}
+"#,
+        expect![[r#"
+                ```rust
+                ()
+                ```"#]],
+    );
+    check_hover_range(
+        r#"
+//- minicore:result
+struct FooError;
+struct BarError;
+
+fn foo() -> Result<(), FooError> {
+    Ok($0Result::<(), BarError>::Ok(())?$0)
+}
+"#,
+        expect![[r#"
+                ```text
+                Try Error Type: BarError
+                Propagated as:  FooError
+                ```
+            "#]],
+    );
+}
+
+#[test]
+fn hover_try_expr() {
+    check_hover_range(
+        r#"
+struct NotResult<T, U>(T, U);
+struct Short;
+struct Looooong;
+
+fn foo() -> NotResult<(), Looooong> {
+    $0NotResult((), Short)?$0;
+}
+"#,
+        expect![[r#"
+                ```text
+                Try Target Type:    NotResult<(), Short>
+                Propagated as:   NotResult<(), Looooong>
+                ```
+            "#]],
+    );
+    check_hover_range(
+        r#"
+struct NotResult<T, U>(T, U);
+struct Short;
+struct Looooong;
+
+fn foo() -> NotResult<(), Short> {
+    $0NotResult((), Looooong)?$0;
+}
+"#,
+        expect![[r#"
+                ```text
+                Try Target Type: NotResult<(), Looooong>
+                Propagated as:      NotResult<(), Short>
+                ```
+            "#]],
+    );
+}
+
+#[test]
+fn hover_try_expr_option() {
+    cov_mark::check!(hover_try_expr_opt_opt);
+    check_hover_range(
+        r#"
+//- minicore: option, try
+
+fn foo() -> Option<()> {
+    $0Some(0)?$0;
+    None
+}
+"#,
+        expect![[r#"
+                ```rust
+                <Option<i32> as Try>::Output
+                ```"#]],
+    );
+}
+
+#[test]
+fn hover_deref_expr() {
+    check_hover_range(
+        r#"
+//- minicore: deref
+use core::ops::Deref;
+
+struct DerefExample<T> {
+    value: T
+}
+
+impl<T> Deref for DerefExample<T> {
+    type Target = T;
+
+    fn deref(&self) -> &Self::Target {
+        &self.value
+    }
+}
+
+fn foo() {
+    let x = DerefExample { value: 0 };
+    let y: i32 = $0*x$0;
+}
+"#,
+        expect![[r#"
+                ```text
+                Dereferenced from: DerefExample<i32>
+                To type:                         i32
+                ```
+            "#]],
+    );
+}
+
+#[test]
+fn hover_deref_expr_with_coercion() {
+    check_hover_range(
+        r#"
+//- minicore: deref
+use core::ops::Deref;
+
+struct DerefExample<T> {
+    value: T
+}
+
+impl<T> Deref for DerefExample<T> {
+    type Target = T;
+
+    fn deref(&self) -> &Self::Target {
+        &self.value
+    }
+}
+
+fn foo() {
+    let x = DerefExample { value: &&&&&0 };
+    let y: &i32 = $0*x$0;
+}
+"#,
+        expect![[r#"
+                ```text
+                Dereferenced from: DerefExample<&&&&&i32>
+                To type:                         &&&&&i32
+                Coerced to:                          &i32
+                ```
+            "#]],
+    );
+}
+
+#[test]
+fn hover_intra_in_macro() {
+    check(
+        r#"
+macro_rules! foo_macro {
+    ($(#[$attr:meta])* $name:ident) => {
+        $(#[$attr])*
+        pub struct $name;
+    }
+}
+
+foo_macro!(
+    /// Doc comment for [`Foo$0`]
+    Foo
+);
+"#,
+        expect![[r#"
+                *[`Foo`]*
+
+                ```rust
+                test
+                ```
+
+                ```rust
+                pub struct Foo
+                ```
+
+                ---
+
+                Doc comment for [`Foo`](https://docs.rs/test/*/test/struct.Foo.html)
+            "#]],
+    );
+}
+
+#[test]
+fn hover_intra_in_attr() {
+    check(
+        r#"
+#[doc = "Doc comment for [`Foo$0`]"]
+pub struct Foo;
+"#,
+        expect![[r#"
+                *[`Foo`]*
+
+                ```rust
+                test
+                ```
+
+                ```rust
+                pub struct Foo
+                ```
+
+                ---
+
+                Doc comment for [`Foo`](https://docs.rs/test/*/test/struct.Foo.html)
+            "#]],
+    );
+}
index b2a8e2a2091b97c8c690727f2c71dfa7b71b48be..79e68aa1dddcbe804987de2a6bf0ed38cf0ecc47 100644 (file)
@@ -1,7 +1,7 @@
 use either::Either;
 use hir::{known, Callable, HasVisibility, HirDisplay, Semantics, TypeInfo};
-use ide_db::helpers::FamousDefs;
 use ide_db::RootDatabase;
+use ide_db::{base_db::FileRange, helpers::FamousDefs};
 use stdx::to_lower_snake_case;
 use syntax::{
     ast::{self, ArgListOwner, AstNode, NameOwner},
@@ -62,20 +62,24 @@ pub(crate) fn inlay_hints(
     let _p = profile::span("inlay_hints");
     let sema = Semantics::new(db);
     let file = sema.parse(file_id);
+    let file = file.syntax();
 
     let mut res = Vec::new();
-    for node in file.syntax().descendants() {
-        if let Some(expr) = ast::Expr::cast(node.clone()) {
-            get_chaining_hints(&mut res, &sema, config, expr);
-        }
 
-        match_ast! {
-            match node {
-                ast::CallExpr(it) => { get_param_name_hints(&mut res, &sema, config, ast::Expr::from(it)); },
-                ast::MethodCallExpr(it) => { get_param_name_hints(&mut res, &sema, config, ast::Expr::from(it)); },
-                ast::IdentPat(it) => { get_bind_pat_hints(&mut res, &sema, config, it); },
+    for node in file.descendants() {
+        if let Some(expr) = ast::Expr::cast(node.clone()) {
+            get_chaining_hints(&mut res, &sema, config, &expr);
+            match expr {
+                ast::Expr::CallExpr(it) => {
+                    get_param_name_hints(&mut res, &sema, config, ast::Expr::from(it));
+                }
+                ast::Expr::MethodCallExpr(it) => {
+                    get_param_name_hints(&mut res, &sema, config, ast::Expr::from(it));
+                }
                 _ => (),
             }
+        } else if let Some(it) = ast::IdentPat::cast(node.clone()) {
+            get_bind_pat_hints(&mut res, &sema, config, &it);
         }
     }
     res
@@ -85,7 +89,7 @@ fn get_chaining_hints(
     acc: &mut Vec<InlayHint>,
     sema: &Semantics<RootDatabase>,
     config: &InlayHintsConfig,
-    expr: ast::Expr,
+    expr: &ast::Expr,
 ) -> Option<()> {
     if !config.chaining_hints {
         return None;
@@ -95,7 +99,9 @@ fn get_chaining_hints(
         return None;
     }
 
-    let krate = sema.scope(expr.syntax()).module().map(|it| it.krate());
+    let descended = sema.descend_node_into_attributes(expr.clone()).pop();
+    let desc_expr = descended.as_ref().unwrap_or(expr);
+    let krate = sema.scope(desc_expr.syntax()).module().map(|it| it.krate());
     let famous_defs = FamousDefs(sema, krate);
 
     let mut tokens = expr
@@ -117,7 +123,7 @@ fn get_chaining_hints(
             next_next = tokens.next()?.kind();
         }
         if next_next == T![.] {
-            let ty = sema.type_of_expr(&expr)?.original;
+            let ty = sema.type_of_expr(desc_expr)?.original;
             if ty.is_unknown() {
                 return None;
             }
@@ -156,6 +162,8 @@ fn get_param_name_hints(
         .into_iter()
         .zip(arg_list.args())
         .filter_map(|((param, _ty), arg)| {
+            // Only annotate hints for expressions that exist in the original file
+            let range = sema.original_range_opt(arg.syntax())?;
             let param_name = match param? {
                 Either::Left(_) => "self".to_string(),
                 Either::Right(pat) => match pat {
@@ -163,11 +171,13 @@ fn get_param_name_hints(
                     _ => return None,
                 },
             };
-            Some((param_name, arg))
+            Some((param_name, arg, range))
         })
-        .filter(|(param_name, arg)| !should_hide_param_name_hint(sema, &callable, param_name, arg))
-        .map(|(param_name, arg)| InlayHint {
-            range: arg.syntax().text_range(),
+        .filter(|(param_name, arg, _)| {
+            !should_hide_param_name_hint(sema, &callable, param_name, arg)
+        })
+        .map(|(param_name, _, FileRange { range, .. })| InlayHint {
+            range,
             kind: InlayKind::ParameterHint,
             label: param_name.into(),
         });
@@ -180,16 +190,18 @@ fn get_bind_pat_hints(
     acc: &mut Vec<InlayHint>,
     sema: &Semantics<RootDatabase>,
     config: &InlayHintsConfig,
-    pat: ast::IdentPat,
+    pat: &ast::IdentPat,
 ) -> Option<()> {
     if !config.type_hints {
         return None;
     }
 
-    let krate = sema.scope(pat.syntax()).module().map(|it| it.krate());
+    let descended = sema.descend_node_into_attributes(pat.clone()).pop();
+    let desc_pat = descended.as_ref().unwrap_or(pat);
+    let krate = sema.scope(desc_pat.syntax()).module().map(|it| it.krate());
     let famous_defs = FamousDefs(sema, krate);
 
-    let ty = sema.type_of_pat(&pat.clone().into())?.original;
+    let ty = sema.type_of_pat(&desc_pat.clone().into())?.original;
 
     if should_not_display_type_hint(sema, &pat, &ty) {
         return None;
@@ -431,9 +443,13 @@ fn get_callable(
 ) -> Option<(hir::Callable, ast::ArgList)> {
     match expr {
         ast::Expr::CallExpr(expr) => {
+            let descended = sema.descend_node_into_attributes(expr.clone()).pop();
+            let expr = descended.as_ref().unwrap_or(expr);
             sema.type_of_expr(&expr.expr()?)?.original.as_callable(sema.db).zip(expr.arg_list())
         }
         ast::Expr::MethodCallExpr(expr) => {
+            let descended = sema.descend_node_into_attributes(expr.clone()).pop();
+            let expr = descended.as_ref().unwrap_or(expr);
             sema.resolve_method_call_as_callable(expr).zip(expr.arg_list())
         }
         _ => None,
@@ -1467,4 +1483,53 @@ fn main() {
             "#]],
         );
     }
+
+    #[test]
+    fn hints_in_attr_call() {
+        check_expect(
+            TEST_CONFIG,
+            r#"
+//- proc_macros: identity, input_replace
+struct Struct;
+impl Struct {
+    fn chain(self) -> Self {
+        self
+    }
+}
+#[proc_macros::identity]
+fn main() {
+    let strukt = Struct;
+    strukt
+        .chain()
+        .chain()
+        .chain();
+    Struct::chain(strukt);
+}
+"#,
+            expect![[r#"
+                [
+                    InlayHint {
+                        range: 124..130,
+                        kind: TypeHint,
+                        label: "Struct",
+                    },
+                    InlayHint {
+                        range: 145..185,
+                        kind: ChainingHint,
+                        label: "Struct",
+                    },
+                    InlayHint {
+                        range: 145..168,
+                        kind: ChainingHint,
+                        label: "Struct",
+                    },
+                    InlayHint {
+                        range: 222..228,
+                        kind: ParameterHint,
+                        label: "self",
+                    },
+                ]
+            "#]],
+        );
+    }
 }
index 0d30dafd56fb9ec3b98035fe31d0e85661cdc2b6..c39d00b6e7da342713ca1906183e09995f253df4 100644 (file)
@@ -87,7 +87,10 @@ pub(crate) fn find_all_refs(
                 }
                 .map(|nav| {
                     let decl_range = nav.focus_or_full_range();
-                    Declaration { nav, access: decl_access(&def, &syntax, decl_range) }
+                    Declaration {
+                        access: decl_access(&def, sema.parse(nav.file_id).syntax(), decl_range),
+                        nav,
+                    }
                 });
                 if is_literal_search {
                     retain_adt_literal_usages(&mut usages, def, sema);
@@ -1504,4 +1507,23 @@ fn f() {
             "#]],
         )
     }
+
+    #[test]
+    fn attr_expanded() {
+        check(
+            r#"
+//- proc_macros: identity
+
+#[proc_macros::identity]
+fn func$0() {
+    func();
+}
+"#,
+            expect![[r#"
+                func Function FileId(0) 26..51 29..33
+
+                FileId(0) 42..46
+            "#]],
+        )
+    }
 }
index a4297a2fec5540783f6a2ba710687b632077eb2f..a495e6c5432d93842ed2e6c0a0ed14d223422d2e 100644 (file)
@@ -1880,4 +1880,26 @@ fn main() { f$0()  }
             "error: No identifier available to rename",
         )
     }
+
+    #[test]
+    fn attributed_item() {
+        check(
+            "function",
+            r#"
+//- proc_macros: identity
+
+#[proc_macros::identity]
+fn func$0() {
+    func();
+}
+"#,
+            r#"
+
+#[proc_macros::identity]
+fn function() {
+    function();
+}
+"#,
+        )
+    }
 }
index 6b74ec3430de57c5319fc344718c28800f905eab..376384670a299534e3e7bcd51824d6959579fa24 100644 (file)
@@ -1737,6 +1737,88 @@ fn t1() {}
         );
     }
 
+    #[test]
+    fn attributed_module() {
+        check(
+            r#"
+//- proc_macros: identity
+//- /lib.rs
+$0
+#[proc_macros::identity]
+mod module {
+    #[test]
+    fn t0() {}
+    #[test]
+    fn t1() {}
+}
+"#,
+            &[TestMod, Test, Test],
+            expect![[r#"
+                [
+                    Runnable {
+                        use_name_in_title: true,
+                        nav: NavigationTarget {
+                            file_id: FileId(
+                                0,
+                            ),
+                            full_range: 26..94,
+                            focus_range: 30..36,
+                            name: "module",
+                            kind: Module,
+                            description: "mod module",
+                        },
+                        kind: TestMod {
+                            path: "module",
+                        },
+                        cfg: None,
+                    },
+                    Runnable {
+                        use_name_in_title: true,
+                        nav: NavigationTarget {
+                            file_id: FileId(
+                                0,
+                            ),
+                            full_range: 43..65,
+                            focus_range: 58..60,
+                            name: "t0",
+                            kind: Function,
+                        },
+                        kind: Test {
+                            test_id: Path(
+                                "module::t0",
+                            ),
+                            attr: TestAttr {
+                                ignore: false,
+                            },
+                        },
+                        cfg: None,
+                    },
+                    Runnable {
+                        use_name_in_title: true,
+                        nav: NavigationTarget {
+                            file_id: FileId(
+                                0,
+                            ),
+                            full_range: 70..92,
+                            focus_range: 85..87,
+                            name: "t1",
+                            kind: Function,
+                        },
+                        kind: Test {
+                            test_id: Path(
+                                "module::t1",
+                            ),
+                            attr: TestAttr {
+                                ignore: false,
+                            },
+                        },
+                        cfg: None,
+                    },
+                ]
+            "#]],
+        );
+    }
+
     #[test]
     fn find_no_tests() {
         check_tests(
index f82566d978e5d4c0df74355de9168beba6fd1f43..dba0636930f36c7e70d77ddd0142686811f500a6 100644 (file)
@@ -286,7 +286,9 @@ fn highlight_name_ref(
                     Definition::ModuleDef(hir::ModuleDef::Trait(trait_))
                         if trait_.is_unsafe(db) =>
                     {
-                        if ast::Impl::for_trait_name_ref(&name_ref).is_some() {
+                        if ast::Impl::for_trait_name_ref(&name_ref)
+                            .map_or(false, |impl_| impl_.unsafe_token().is_some())
+                        {
                             h |= HlMod::Unsafe;
                         }
                     }
index 6064ef4517695afe11d34d55121c279d29ffcd69..5af73e6976d60cff8009563020219b0752067933 100644 (file)
@@ -69,6 +69,8 @@ fn html_escape(text: &str) -> String {
 .function.unsafe    { color: #BC8383; }
 .trait.unsafe       { color: #BC8383; }
 .operator.unsafe    { color: #BC8383; }
+.mutable.unsafe     { color: #BC8383; text-decoration: underline; }
+.keyword.unsafe     { color: #BC8383; font-weight: bold; }
 .parameter          { color: #94BFF3; }
 .text               { color: #DCDCCC; }
 .type               { color: #7CB8BB; }
@@ -85,7 +87,6 @@ fn html_escape(text: &str) -> String {
 .mutable            { text-decoration: underline; }
 .escape_sequence    { color: #94BFF3; }
 .keyword            { color: #F0DFAF; font-weight: bold; }
-.keyword.unsafe     { color: #BC8383; font-weight: bold; }
 .control            { font-style: italic; }
 .reference          { font-style: italic; font-weight: bold; }
 
index a5d2ba31ee55e378a18b67244a8cd862713515a4..0d0c21cb0c79eef19751b0f6f3f3a8df5e29e63f 100644 (file)
@@ -17,6 +17,8 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
 .function.unsafe    { color: #BC8383; }
 .trait.unsafe       { color: #BC8383; }
 .operator.unsafe    { color: #BC8383; }
+.mutable.unsafe     { color: #BC8383; text-decoration: underline; }
+.keyword.unsafe     { color: #BC8383; font-weight: bold; }
 .parameter          { color: #94BFF3; }
 .text               { color: #DCDCCC; }
 .type               { color: #7CB8BB; }
@@ -33,7 +35,6 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
 .mutable            { text-decoration: underline; }
 .escape_sequence    { color: #94BFF3; }
 .keyword            { color: #F0DFAF; font-weight: bold; }
-.keyword.unsafe     { color: #BC8383; font-weight: bold; }
 .control            { font-style: italic; }
 .reference          { font-style: italic; font-weight: bold; }
 
index a5af8a6c86a3b78055bd0a833ee712fbf36685cf..f54af2d92e40c29667230816cd63983a35e79b9e 100644 (file)
@@ -17,6 +17,8 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
 .function.unsafe    { color: #BC8383; }
 .trait.unsafe       { color: #BC8383; }
 .operator.unsafe    { color: #BC8383; }
+.mutable.unsafe     { color: #BC8383; text-decoration: underline; }
+.keyword.unsafe     { color: #BC8383; font-weight: bold; }
 .parameter          { color: #94BFF3; }
 .text               { color: #DCDCCC; }
 .type               { color: #7CB8BB; }
@@ -33,7 +35,6 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
 .mutable            { text-decoration: underline; }
 .escape_sequence    { color: #94BFF3; }
 .keyword            { color: #F0DFAF; font-weight: bold; }
-.keyword.unsafe     { color: #BC8383; font-weight: bold; }
 .control            { font-style: italic; }
 .reference          { font-style: italic; font-weight: bold; }
 
index e49de797b3774929a1da35d55eeacd20876138cf..9c01832e7544743a39e208db919da5e4c657b8c2 100644 (file)
@@ -17,6 +17,8 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
 .function.unsafe    { color: #BC8383; }
 .trait.unsafe       { color: #BC8383; }
 .operator.unsafe    { color: #BC8383; }
+.mutable.unsafe     { color: #BC8383; text-decoration: underline; }
+.keyword.unsafe     { color: #BC8383; font-weight: bold; }
 .parameter          { color: #94BFF3; }
 .text               { color: #DCDCCC; }
 .type               { color: #7CB8BB; }
@@ -33,7 +35,6 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
 .mutable            { text-decoration: underline; }
 .escape_sequence    { color: #94BFF3; }
 .keyword            { color: #F0DFAF; font-weight: bold; }
-.keyword.unsafe     { color: #BC8383; font-weight: bold; }
 .control            { font-style: italic; }
 .reference          { font-style: italic; font-weight: bold; }
 
index 0c1aa1fe118434284c7e78b4d6c6efbcb57dffbf..88380bdfe12ab3db1d2bfa0054ae560938b98762 100644 (file)
@@ -17,6 +17,8 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
 .function.unsafe    { color: #BC8383; }
 .trait.unsafe       { color: #BC8383; }
 .operator.unsafe    { color: #BC8383; }
+.mutable.unsafe     { color: #BC8383; text-decoration: underline; }
+.keyword.unsafe     { color: #BC8383; font-weight: bold; }
 .parameter          { color: #94BFF3; }
 .text               { color: #DCDCCC; }
 .type               { color: #7CB8BB; }
@@ -33,7 +35,6 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
 .mutable            { text-decoration: underline; }
 .escape_sequence    { color: #94BFF3; }
 .keyword            { color: #F0DFAF; font-weight: bold; }
-.keyword.unsafe     { color: #BC8383; font-weight: bold; }
 .control            { font-style: italic; }
 .reference          { font-style: italic; font-weight: bold; }
 
index e519b22007a61fa75f813020f53a9682e5b08ad3..9c20c8ae4b803285f995cdf71f3b884c38ecef93 100644 (file)
@@ -17,6 +17,8 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
 .function.unsafe    { color: #BC8383; }
 .trait.unsafe       { color: #BC8383; }
 .operator.unsafe    { color: #BC8383; }
+.mutable.unsafe     { color: #BC8383; text-decoration: underline; }
+.keyword.unsafe     { color: #BC8383; font-weight: bold; }
 .parameter          { color: #94BFF3; }
 .text               { color: #DCDCCC; }
 .type               { color: #7CB8BB; }
@@ -33,7 +35,6 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
 .mutable            { text-decoration: underline; }
 .escape_sequence    { color: #94BFF3; }
 .keyword            { color: #F0DFAF; font-weight: bold; }
-.keyword.unsafe     { color: #BC8383; font-weight: bold; }
 .control            { font-style: italic; }
 .reference          { font-style: italic; font-weight: bold; }
 
index 987f06a23ab1a2b2d71f7cd78978efaf0b3f72ad..2b86340efd75a00fc6eb5214079933949226bc11 100644 (file)
@@ -17,6 +17,8 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
 .function.unsafe    { color: #BC8383; }
 .trait.unsafe       { color: #BC8383; }
 .operator.unsafe    { color: #BC8383; }
+.mutable.unsafe     { color: #BC8383; text-decoration: underline; }
+.keyword.unsafe     { color: #BC8383; font-weight: bold; }
 .parameter          { color: #94BFF3; }
 .text               { color: #DCDCCC; }
 .type               { color: #7CB8BB; }
@@ -33,31 +35,25 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
 .mutable            { text-decoration: underline; }
 .escape_sequence    { color: #94BFF3; }
 .keyword            { color: #F0DFAF; font-weight: bold; }
-.keyword.unsafe     { color: #BC8383; font-weight: bold; }
 .control            { font-style: italic; }
 .reference          { font-style: italic; font-weight: bold; }
 
 .unresolved_reference { color: #FC5555; text-decoration: wavy underline; }
 </style>
-<pre><code><span class="keyword unsafe">unsafe</span> <span class="keyword">fn</span> <span class="function declaration unsafe">unsafe_fn</span><span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="brace">{</span><span class="brace">}</span>
+<pre><code><span class="keyword">static</span> <span class="keyword">mut</span> <span class="static declaration mutable unsafe">MUT_GLOBAL</span><span class="colon">:</span> <span class="struct">Struct</span> <span class="operator">=</span> <span class="struct">Struct</span> <span class="brace">{</span> <span class="field">field</span><span class="colon">:</span> <span class="numeric_literal">0</span> <span class="brace">}</span><span class="semicolon">;</span>
+<span class="keyword">static</span> <span class="static declaration">GLOBAL</span><span class="colon">:</span> <span class="struct">Struct</span> <span class="operator">=</span> <span class="struct">Struct</span> <span class="brace">{</span> <span class="field">field</span><span class="colon">:</span> <span class="numeric_literal">0</span> <span class="brace">}</span><span class="semicolon">;</span>
+<span class="keyword unsafe">unsafe</span> <span class="keyword">fn</span> <span class="function declaration unsafe">unsafe_fn</span><span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="brace">{</span><span class="brace">}</span>
 
 <span class="keyword">union</span> <span class="union declaration">Union</span> <span class="brace">{</span>
     <span class="field declaration">a</span><span class="colon">:</span> <span class="builtin_type">u32</span><span class="comma">,</span>
     <span class="field declaration">b</span><span class="colon">:</span> <span class="builtin_type">f32</span><span class="comma">,</span>
 <span class="brace">}</span>
 
-<span class="keyword">struct</span> <span class="struct declaration">HasUnsafeFn</span><span class="semicolon">;</span>
-
-<span class="keyword">impl</span> <span class="struct">HasUnsafeFn</span> <span class="brace">{</span>
+<span class="keyword">struct</span> <span class="struct declaration">Struct</span> <span class="brace">{</span> <span class="field declaration">field</span><span class="colon">:</span> <span class="builtin_type">i32</span> <span class="brace">}</span>
+<span class="keyword">impl</span> <span class="struct">Struct</span> <span class="brace">{</span>
     <span class="keyword unsafe">unsafe</span> <span class="keyword">fn</span> <span class="function associated declaration reference unsafe">unsafe_method</span><span class="parenthesis">(</span><span class="operator">&</span><span class="self_keyword declaration reference">self</span><span class="parenthesis">)</span> <span class="brace">{</span><span class="brace">}</span>
 <span class="brace">}</span>
 
-<span class="keyword">struct</span> <span class="struct declaration">TypeForStaticMut</span> <span class="brace">{</span>
-    <span class="field declaration">a</span><span class="colon">:</span> <span class="builtin_type">u8</span>
-<span class="brace">}</span>
-
-<span class="keyword">static</span> <span class="keyword">mut</span> <span class="static declaration mutable unsafe">global_mut</span><span class="colon">:</span> <span class="struct">TypeForStaticMut</span> <span class="operator">=</span> <span class="struct">TypeForStaticMut</span> <span class="brace">{</span> <span class="field">a</span><span class="colon">:</span> <span class="numeric_literal">0</span> <span class="brace">}</span><span class="semicolon">;</span>
-
 <span class="attribute attribute">#</span><span class="attribute attribute">[</span><span class="builtin_attr attribute">repr</span><span class="parenthesis attribute">(</span><span class="none attribute">packed</span><span class="parenthesis attribute">)</span><span class="attribute attribute">]</span>
 <span class="keyword">struct</span> <span class="struct declaration">Packed</span> <span class="brace">{</span>
     <span class="field declaration">a</span><span class="colon">:</span> <span class="builtin_type">u16</span><span class="comma">,</span>
@@ -65,8 +61,9 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
 
 <span class="keyword unsafe">unsafe</span> <span class="keyword">trait</span> <span class="trait declaration unsafe">UnsafeTrait</span> <span class="brace">{</span><span class="brace">}</span>
 <span class="keyword unsafe">unsafe</span> <span class="keyword">impl</span> <span class="trait unsafe">UnsafeTrait</span> <span class="keyword">for</span> <span class="struct">Packed</span> <span class="brace">{</span><span class="brace">}</span>
+<span class="keyword">impl</span> <span class="punctuation">!</span><span class="trait">UnsafeTrait</span> <span class="keyword">for</span> <span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="brace">{</span><span class="brace">}</span>
 
-<span class="keyword">fn</span> <span class="function declaration">require_unsafe_trait</span><span class="angle">&lt;</span><span class="type_param declaration">T</span><span class="colon">:</span> <span class="trait">UnsafeTrait</span><span class="angle">&gt;</span><span class="parenthesis">(</span><span class="punctuation">_</span><span class="colon">:</span> <span class="type_param">T</span><span class="parenthesis">)</span> <span class="brace">{</span><span class="brace">}</span>
+<span class="keyword">fn</span> <span class="function declaration">unsafe_trait_bound</span><span class="angle">&lt;</span><span class="type_param declaration">T</span><span class="colon">:</span> <span class="trait">UnsafeTrait</span><span class="angle">&gt;</span><span class="parenthesis">(</span><span class="punctuation">_</span><span class="colon">:</span> <span class="type_param">T</span><span class="parenthesis">)</span> <span class="brace">{</span><span class="brace">}</span>
 
 <span class="keyword">trait</span> <span class="trait declaration">DoTheAutoref</span> <span class="brace">{</span>
     <span class="keyword">fn</span> <span class="function associated declaration reference trait">calls_autoref</span><span class="parenthesis">(</span><span class="operator">&</span><span class="self_keyword declaration reference">self</span><span class="parenthesis">)</span><span class="semicolon">;</span>
@@ -87,13 +84,14 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
             <span class="union">Union</span> <span class="brace">{</span> <span class="field unsafe">b</span><span class="colon">:</span> <span class="numeric_literal">0</span> <span class="brace">}</span> <span class="operator">=&gt;</span> <span class="parenthesis">(</span><span class="parenthesis">)</span><span class="comma">,</span>
             <span class="union">Union</span> <span class="brace">{</span> <span class="field unsafe">a</span> <span class="brace">}</span> <span class="operator">=&gt;</span> <span class="parenthesis">(</span><span class="parenthesis">)</span><span class="comma">,</span>
         <span class="brace">}</span>
-        <span class="struct">HasUnsafeFn</span><span class="operator">.</span><span class="function associated reference unsafe">unsafe_method</span><span class="parenthesis">(</span><span class="parenthesis">)</span><span class="semicolon">;</span>
+        <span class="struct">Struct</span> <span class="brace">{</span> <span class="field">field</span><span class="colon">:</span> <span class="numeric_literal">0</span> <span class="brace">}</span><span class="operator">.</span><span class="function associated reference unsafe">unsafe_method</span><span class="parenthesis">(</span><span class="parenthesis">)</span><span class="semicolon">;</span>
 
         <span class="comment">// unsafe deref</span>
-        <span class="keyword">let</span> <span class="variable declaration">y</span> <span class="operator">=</span> <span class="operator unsafe">*</span><span class="variable">x</span><span class="semicolon">;</span>
+        <span class="operator unsafe">*</span><span class="variable">x</span><span class="semicolon">;</span>
 
         <span class="comment">// unsafe access to a static mut</span>
-        <span class="keyword">let</span> <span class="variable declaration">a</span> <span class="operator">=</span> <span class="static mutable unsafe">global_mut</span><span class="operator">.</span><span class="field">a</span><span class="semicolon">;</span>
+        <span class="static mutable unsafe">MUT_GLOBAL</span><span class="operator">.</span><span class="field">field</span><span class="semicolon">;</span>
+        <span class="static">GLOBAL</span><span class="operator">.</span><span class="field">field</span><span class="semicolon">;</span>
 
         <span class="comment">// unsafe ref of packed fields</span>
         <span class="keyword">let</span> <span class="variable declaration">packed</span> <span class="operator">=</span> <span class="struct">Packed</span> <span class="brace">{</span> <span class="field">a</span><span class="colon">:</span> <span class="numeric_literal">0</span> <span class="brace">}</span><span class="semicolon">;</span>
index e43119aa3b0f42b03037552154912a0af1b8f379..16760c24debd5e461775db520a7e616284dcb02f 100644 (file)
@@ -17,6 +17,8 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
 .function.unsafe    { color: #BC8383; }
 .trait.unsafe       { color: #BC8383; }
 .operator.unsafe    { color: #BC8383; }
+.mutable.unsafe     { color: #BC8383; text-decoration: underline; }
+.keyword.unsafe     { color: #BC8383; font-weight: bold; }
 .parameter          { color: #94BFF3; }
 .text               { color: #DCDCCC; }
 .type               { color: #7CB8BB; }
@@ -33,7 +35,6 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
 .mutable            { text-decoration: underline; }
 .escape_sequence    { color: #94BFF3; }
 .keyword            { color: #F0DFAF; font-weight: bold; }
-.keyword.unsafe     { color: #BC8383; font-weight: bold; }
 .control            { font-style: italic; }
 .reference          { font-style: italic; font-weight: bold; }
 
@@ -51,21 +52,23 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
     <span class="keyword">pub</span> <span class="keyword">trait</span> <span class="trait declaration public">Copy</span> <span class="brace">{</span><span class="brace">}</span>
 <span class="brace">}</span>
 
+<span class="attribute attribute">#</span><span class="attribute attribute">[</span><span class="module attribute">proc_macros</span><span class="operator attribute">::</span><span class="builtin_attr attribute">identity</span><span class="attribute attribute">]</span>
 <span class="keyword">pub</span> <span class="keyword">mod</span> <span class="module declaration public">ops</span> <span class="brace">{</span>
-    <span class="attribute attribute">#</span><span class="attribute attribute">[</span><span class="builtin_attr attribute">lang</span><span class="attribute attribute"> </span><span class="operator attribute">=</span><span class="attribute attribute"> </span><span class="string_literal attribute">"fn_once"</span><span class="attribute attribute">]</span>
+    <span class="attribute attribute">#</span><span class="attribute attribute">[</span><span class="builtin_attr attribute">lang</span> <span class="operator attribute">=</span> <span class="string_literal attribute">"fn_once"</span><span class="attribute attribute">]</span>
     <span class="keyword">pub</span> <span class="keyword">trait</span> <span class="trait declaration public">FnOnce</span><span class="angle">&lt;</span><span class="type_param declaration">Args</span><span class="angle">&gt;</span> <span class="brace">{</span><span class="brace">}</span>
 
-    <span class="attribute attribute">#</span><span class="attribute attribute">[</span><span class="builtin_attr attribute">lang</span><span class="attribute attribute"> </span><span class="operator attribute">=</span><span class="attribute attribute"> </span><span class="string_literal attribute">"fn_mut"</span><span class="attribute attribute">]</span>
+    <span class="attribute attribute">#</span><span class="attribute attribute">[</span><span class="builtin_attr attribute">lang</span> <span class="operator attribute">=</span> <span class="string_literal attribute">"fn_mut"</span><span class="attribute attribute">]</span>
     <span class="keyword">pub</span> <span class="keyword">trait</span> <span class="trait declaration public">FnMut</span><span class="angle">&lt;</span><span class="type_param declaration">Args</span><span class="angle">&gt;</span><span class="colon">:</span> <span class="trait public">FnOnce</span><span class="angle">&lt;</span><span class="type_param">Args</span><span class="angle">&gt;</span> <span class="brace">{</span><span class="brace">}</span>
 
-    <span class="attribute attribute">#</span><span class="attribute attribute">[</span><span class="builtin_attr attribute">lang</span><span class="attribute attribute"> </span><span class="operator attribute">=</span><span class="attribute attribute"> </span><span class="string_literal attribute">"fn"</span><span class="attribute attribute">]</span>
+    <span class="attribute attribute">#</span><span class="attribute attribute">[</span><span class="builtin_attr attribute">lang</span> <span class="operator attribute">=</span> <span class="string_literal attribute">"fn"</span><span class="attribute attribute">]</span>
     <span class="keyword">pub</span> <span class="keyword">trait</span> <span class="trait declaration public">Fn</span><span class="angle">&lt;</span><span class="type_param declaration">Args</span><span class="angle">&gt;</span><span class="colon">:</span> <span class="trait public">FnMut</span><span class="angle">&lt;</span><span class="type_param">Args</span><span class="angle">&gt;</span> <span class="brace">{</span><span class="brace">}</span>
 <span class="brace">}</span>
 
-
-<span class="keyword">struct</span> <span class="struct declaration">Foo</span> <span class="brace">{</span>
-    <span class="keyword">pub</span> <span class="field declaration public">x</span><span class="colon">:</span> <span class="builtin_type">i32</span><span class="comma">,</span>
-    <span class="keyword">pub</span> <span class="field declaration public">y</span><span class="colon">:</span> <span class="builtin_type">i32</span><span class="comma">,</span>
+proc_macros::<span class="macro">mirror!</span> <span class="brace">{</span>
+    <span class="brace">{</span>
+        <span class="comma">,</span><span class="builtin_type">i32</span> <span class="colon">:</span><span class="field declaration public">x</span> <span class="keyword">pub</span>
+        <span class="comma">,</span><span class="builtin_type">i32</span> <span class="colon">:</span><span class="field declaration public">y</span> <span class="keyword">pub</span>
+    <span class="brace">}</span> <span class="struct declaration">Foo</span> <span class="keyword">struct</span>
 <span class="brace">}</span>
 
 <span class="keyword">trait</span> <span class="trait declaration">Bar</span> <span class="keyword">where</span> <span class="type_param">Self</span><span class="colon">:</span> <span class="brace">{</span>
@@ -115,8 +118,6 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
     <span class="function">str</span><span class="parenthesis">(</span><span class="parenthesis">)</span><span class="semicolon">;</span>
 <span class="brace">}</span>
 
-<span class="keyword">static</span> <span class="keyword">mut</span> <span class="static declaration mutable unsafe">STATIC_MUT</span><span class="colon">:</span> <span class="builtin_type">i32</span> <span class="operator">=</span> <span class="numeric_literal">0</span><span class="semicolon">;</span>
-
 <span class="keyword">fn</span> <span class="function declaration">foo</span><span class="angle">&lt;</span><span class="lifetime declaration">'a</span><span class="comma">,</span> <span class="type_param declaration">T</span><span class="angle">&gt;</span><span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="operator">-&gt;</span> <span class="type_param">T</span> <span class="brace">{</span>
     <span class="function">foo</span><span class="operator">::</span><span class="angle">&lt;</span><span class="lifetime">'a</span><span class="comma">,</span> <span class="builtin_type">i32</span><span class="angle">&gt;</span><span class="parenthesis">(</span><span class="parenthesis">)</span>
 <span class="brace">}</span>
@@ -184,10 +185,6 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
         <span class="keyword">let</span> <span class="variable declaration">x</span> <span class="operator">=</span> <span class="numeric_literal">92</span><span class="semicolon">;</span>
         <span class="variable mutable">vec</span><span class="operator">.</span><span class="unresolved_reference">push</span><span class="parenthesis">(</span><span class="struct">Foo</span> <span class="brace">{</span> <span class="field">x</span><span class="comma">,</span> <span class="field public">y</span><span class="colon">:</span> <span class="numeric_literal">1</span> <span class="brace">}</span><span class="parenthesis">)</span><span class="semicolon">;</span>
     <span class="brace">}</span>
-    <span class="keyword unsafe">unsafe</span> <span class="brace">{</span>
-        <span class="variable mutable">vec</span><span class="operator">.</span><span class="unresolved_reference">set_len</span><span class="parenthesis">(</span><span class="numeric_literal">0</span><span class="parenthesis">)</span><span class="semicolon">;</span>
-        <span class="static mutable unsafe">STATIC_MUT</span> <span class="operator">=</span> <span class="numeric_literal">1</span><span class="semicolon">;</span>
-    <span class="brace">}</span>
 
     <span class="keyword control">for</span> <span class="variable declaration">e</span> <span class="keyword control">in</span> <span class="variable mutable">vec</span> <span class="brace">{</span>
         <span class="comment">// Do nothing</span>
@@ -255,9 +252,6 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
     futures::<span class="macro">join!</span><span class="parenthesis">(</span>f1<span class="comma">,</span> f2<span class="parenthesis">)</span><span class="semicolon">;</span>
 <span class="brace">}</span>
 
-<span class="keyword unsafe">unsafe</span> <span class="keyword">trait</span> <span class="trait declaration unsafe">Dangerous</span> <span class="brace">{</span><span class="brace">}</span>
-<span class="keyword">impl</span> <span class="trait unsafe">Dangerous</span> <span class="keyword">for</span> <span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="brace">{</span><span class="brace">}</span>
-
 <span class="keyword">fn</span> <span class="function declaration">use_foo_items</span><span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="brace">{</span>
     <span class="keyword">let</span> <span class="variable declaration">bob</span> <span class="operator">=</span> <span class="module library">foo</span><span class="operator">::</span><span class="struct library">Person</span> <span class="brace">{</span>
         <span class="field library">name</span><span class="colon">:</span> <span class="string_literal">"Bob"</span><span class="comma">,</span>
index afb32746f14836db13b24a415c2fed5a6d47da7f..28c2bed768ac6bb80bccaa1b54cc95aadd153d1e 100644 (file)
@@ -17,6 +17,8 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
 .function.unsafe    { color: #BC8383; }
 .trait.unsafe       { color: #BC8383; }
 .operator.unsafe    { color: #BC8383; }
+.mutable.unsafe     { color: #BC8383; text-decoration: underline; }
+.keyword.unsafe     { color: #BC8383; font-weight: bold; }
 .parameter          { color: #94BFF3; }
 .text               { color: #DCDCCC; }
 .type               { color: #7CB8BB; }
@@ -33,7 +35,6 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
 .mutable            { text-decoration: underline; }
 .escape_sequence    { color: #94BFF3; }
 .keyword            { color: #F0DFAF; font-weight: bold; }
-.keyword.unsafe     { color: #BC8383; font-weight: bold; }
 .control            { font-style: italic; }
 .reference          { font-style: italic; font-weight: bold; }
 
index f0c96412f11e2f7af6e8801ad504f6292580bed2..30c4bf40f4e4ba40d6bfad0f0e43b75d46b80c77 100644 (file)
@@ -17,6 +17,8 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
 .function.unsafe    { color: #BC8383; }
 .trait.unsafe       { color: #BC8383; }
 .operator.unsafe    { color: #BC8383; }
+.mutable.unsafe     { color: #BC8383; text-decoration: underline; }
+.keyword.unsafe     { color: #BC8383; font-weight: bold; }
 .parameter          { color: #94BFF3; }
 .text               { color: #DCDCCC; }
 .type               { color: #7CB8BB; }
@@ -33,7 +35,6 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
 .mutable            { text-decoration: underline; }
 .escape_sequence    { color: #94BFF3; }
 .keyword            { color: #F0DFAF; font-weight: bold; }
-.keyword.unsafe     { color: #BC8383; font-weight: bold; }
 .control            { font-style: italic; }
 .reference          { font-style: italic; font-weight: bold; }
 
index 740127202ffb5fe3125f95069e9e8c104d3e299f..9fc730e007716ceb0347553deecfafefab023923 100644 (file)
@@ -10,6 +10,7 @@
 fn test_highlighting() {
     check_highlighting(
         r#"
+//- proc_macros: identity, mirror
 //- /main.rs crate:main deps:foo
 use inner::{self as inner_mod};
 mod inner {}
@@ -23,6 +24,7 @@ pub mod marker {
     pub trait Copy {}
 }
 
+#[proc_macros::identity]
 pub mod ops {
     #[lang = "fn_once"]
     pub trait FnOnce<Args> {}
@@ -34,10 +36,11 @@ pub trait FnMut<Args>: FnOnce<Args> {}
     pub trait Fn<Args>: FnMut<Args> {}
 }
 
-
-struct Foo {
-    pub x: i32,
-    pub y: i32,
+proc_macros::mirror! {
+    {
+        ,i32 :x pub
+        ,i32 :y pub
+    } Foo struct
 }
 
 trait Bar where Self: {
@@ -87,8 +90,6 @@ fn str() {
     str();
 }
 
-static mut STATIC_MUT: i32 = 0;
-
 fn foo<'a, T>() -> T {
     foo::<'a, i32>()
 }
@@ -156,10 +157,6 @@ fn main() {
         let x = 92;
         vec.push(Foo { x, y: 1 });
     }
-    unsafe {
-        vec.set_len(0);
-        STATIC_MUT = 1;
-    }
 
     for e in vec {
         // Do nothing
@@ -227,9 +224,6 @@ async fn async_main() {
     futures::join!(f1, f2);
 }
 
-unsafe trait Dangerous {}
-impl Dangerous for () {}
-
 fn use_foo_items() {
     let bob = foo::Person {
         name: "Bob",
@@ -512,6 +506,8 @@ fn main() {
 fn test_unsafe_highlighting() {
     check_highlighting(
         r#"
+static mut MUT_GLOBAL: Struct = Struct { field: 0 };
+static GLOBAL: Struct = Struct { field: 0 };
 unsafe fn unsafe_fn() {}
 
 union Union {
@@ -519,18 +515,11 @@ union Union {
     b: f32,
 }
 
-struct HasUnsafeFn;
-
-impl HasUnsafeFn {
+struct Struct { field: i32 }
+impl Struct {
     unsafe fn unsafe_method(&self) {}
 }
 
-struct TypeForStaticMut {
-    a: u8
-}
-
-static mut global_mut: TypeForStaticMut = TypeForStaticMut { a: 0 };
-
 #[repr(packed)]
 struct Packed {
     a: u16,
@@ -538,8 +527,9 @@ struct Packed {
 
 unsafe trait UnsafeTrait {}
 unsafe impl UnsafeTrait for Packed {}
+impl !UnsafeTrait for () {}
 
-fn require_unsafe_trait<T: UnsafeTrait>(_: T) {}
+fn unsafe_trait_bound<T: UnsafeTrait>(_: T) {}
 
 trait DoTheAutoref {
     fn calls_autoref(&self);
@@ -560,13 +550,14 @@ fn main() {
             Union { b: 0 } => (),
             Union { a } => (),
         }
-        HasUnsafeFn.unsafe_method();
+        Struct { field: 0 }.unsafe_method();
 
         // unsafe deref
-        let y = *x;
+        *x;
 
         // unsafe access to a static mut
-        let a = global_mut.a;
+        MUT_GLOBAL.field;
+        GLOBAL.field;
 
         // unsafe ref of packed fields
         let packed = Packed { a: 0 };
index 0244f5fb291160df3043df9ec0c2b80f47ed0328..2b1f3d1633e563dcb62e0040f9d09b963be5fe19 100644 (file)
@@ -167,7 +167,7 @@ fn add_impl(
             None
         };
 
-        let label = Label::new(label.into());
+        let label = Label::new(label);
         let group = group.cloned();
         self.buf.push(Assist { id, label, group, target, source_change });
         Some(())
diff --git a/crates/ide_assists/src/handlers/add_missing_match_arms.rs b/crates/ide_assists/src/handlers/add_missing_match_arms.rs
new file mode 100644 (file)
index 0000000..cdbd8ac
--- /dev/null
@@ -0,0 +1,1133 @@
+use std::iter::{self, Peekable};
+
+use either::Either;
+use hir::{Adt, HasSource, ModuleDef, Semantics};
+use ide_db::helpers::{mod_path_to_ast, FamousDefs};
+use ide_db::RootDatabase;
+use itertools::Itertools;
+use syntax::ast::{self, make, AstNode, MatchArm, NameOwner, Pat};
+
+use crate::{
+    utils::{self, render_snippet, Cursor},
+    AssistContext, AssistId, AssistKind, Assists,
+};
+
+// Assist: add_missing_match_arms
+//
+// Adds missing clauses to a `match` expression.
+//
+// ```
+// enum Action { Move { distance: u32 }, Stop }
+//
+// fn handle(action: Action) {
+//     match action {
+//         $0
+//     }
+// }
+// ```
+// ->
+// ```
+// enum Action { Move { distance: u32 }, Stop }
+//
+// fn handle(action: Action) {
+//     match action {
+//         $0Action::Move { distance } => todo!(),
+//         Action::Stop => todo!(),
+//     }
+// }
+// ```
+pub(crate) fn add_missing_match_arms(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
+    let match_expr = ctx.find_node_at_offset_with_descend::<ast::MatchExpr>()?;
+    let match_arm_list = match_expr.match_arm_list()?;
+
+    let expr = match_expr.expr()?;
+
+    let mut arms: Vec<MatchArm> = match_arm_list.arms().collect();
+    if let [arm] = arms.as_slice() {
+        if let Some(Pat::WildcardPat(..)) = arm.pat() {
+            arms.clear();
+        }
+    }
+
+    let top_lvl_pats: Vec<_> = arms
+        .iter()
+        .filter_map(ast::MatchArm::pat)
+        .flat_map(|pat| match pat {
+            // Special case OrPat as separate top-level pats
+            Pat::OrPat(or_pat) => Either::Left(or_pat.pats()),
+            _ => Either::Right(iter::once(pat)),
+        })
+        // Exclude top level wildcards so that they are expanded by this assist, retains status quo in #8129.
+        .filter(|pat| !matches!(pat, Pat::WildcardPat(_)))
+        .collect();
+
+    let module = ctx.sema.scope(expr.syntax()).module()?;
+
+    let mut missing_pats: Peekable<Box<dyn Iterator<Item = ast::Pat>>> = if let Some(enum_def) =
+        resolve_enum_def(&ctx.sema, &expr)
+    {
+        let variants = enum_def.variants(ctx.db());
+
+        let missing_pats = variants
+            .into_iter()
+            .filter_map(|variant| build_pat(ctx.db(), module, variant))
+            .filter(|variant_pat| is_variant_missing(&top_lvl_pats, variant_pat));
+
+        let option_enum =
+            FamousDefs(&ctx.sema, Some(module.krate())).core_option_Option().map(lift_enum);
+        let missing_pats: Box<dyn Iterator<Item = _>> = if Some(enum_def) == option_enum {
+            // Match `Some` variant first.
+            cov_mark::hit!(option_order);
+            Box::new(missing_pats.rev())
+        } else {
+            Box::new(missing_pats)
+        };
+        missing_pats.peekable()
+    } else if let Some(enum_defs) = resolve_tuple_of_enum_def(&ctx.sema, &expr) {
+        let mut n_arms = 1;
+        let variants_of_enums: Vec<Vec<ExtendedVariant>> = enum_defs
+            .into_iter()
+            .map(|enum_def| enum_def.variants(ctx.db()))
+            .inspect(|variants| n_arms *= variants.len())
+            .collect();
+
+        // When calculating the match arms for a tuple of enums, we want
+        // to create a match arm for each possible combination of enum
+        // values. The `multi_cartesian_product` method transforms
+        // Vec<Vec<EnumVariant>> into Vec<(EnumVariant, .., EnumVariant)>
+        // where each tuple represents a proposed match arm.
+
+        // A number of arms grows very fast on even a small tuple of large enums.
+        // We skip the assist beyond an arbitrary threshold.
+        if n_arms > 256 {
+            return None;
+        }
+        let missing_pats = variants_of_enums
+            .into_iter()
+            .multi_cartesian_product()
+            .inspect(|_| cov_mark::hit!(add_missing_match_arms_lazy_computation))
+            .map(|variants| {
+                let patterns =
+                    variants.into_iter().filter_map(|variant| build_pat(ctx.db(), module, variant));
+                ast::Pat::from(make::tuple_pat(patterns))
+            })
+            .filter(|variant_pat| is_variant_missing(&top_lvl_pats, variant_pat));
+        (Box::new(missing_pats) as Box<dyn Iterator<Item = _>>).peekable()
+    } else {
+        return None;
+    };
+
+    if missing_pats.peek().is_none() {
+        return None;
+    }
+
+    let target = ctx.sema.original_range(match_expr.syntax()).range;
+    acc.add(
+        AssistId("add_missing_match_arms", AssistKind::QuickFix),
+        "Fill match arms",
+        target,
+        |builder| {
+            let new_match_arm_list = match_arm_list.clone_for_update();
+            let missing_arms = missing_pats
+                .map(|pat| make::match_arm(iter::once(pat), None, make::ext::expr_todo()))
+                .map(|it| it.clone_for_update());
+
+            let catch_all_arm = new_match_arm_list
+                .arms()
+                .find(|arm| matches!(arm.pat(), Some(ast::Pat::WildcardPat(_))));
+            if let Some(arm) = catch_all_arm {
+                let is_empty_expr = arm.expr().map_or(true, |e| match e {
+                    ast::Expr::BlockExpr(b) => {
+                        b.statements().next().is_none() && b.tail_expr().is_none()
+                    }
+                    ast::Expr::TupleExpr(t) => t.fields().next().is_none(),
+                    _ => false,
+                });
+                if is_empty_expr {
+                    arm.remove();
+                } else {
+                    cov_mark::hit!(add_missing_match_arms_empty_expr);
+                }
+            }
+            let mut first_new_arm = None;
+            for arm in missing_arms {
+                first_new_arm.get_or_insert_with(|| arm.clone());
+                new_match_arm_list.add_arm(arm);
+            }
+
+            let old_range = ctx.sema.original_range(match_arm_list.syntax()).range;
+            match (first_new_arm, ctx.config.snippet_cap) {
+                (Some(first_new_arm), Some(cap)) => {
+                    let extend_lifetime;
+                    let cursor =
+                        match first_new_arm.syntax().descendants().find_map(ast::WildcardPat::cast)
+                        {
+                            Some(it) => {
+                                extend_lifetime = it.syntax().clone();
+                                Cursor::Replace(&extend_lifetime)
+                            }
+                            None => Cursor::Before(first_new_arm.syntax()),
+                        };
+                    let snippet = render_snippet(cap, new_match_arm_list.syntax(), cursor);
+                    builder.replace_snippet(cap, old_range, snippet);
+                }
+                _ => builder.replace(old_range, new_match_arm_list.to_string()),
+            }
+        },
+    )
+}
+
+fn is_variant_missing(existing_pats: &[Pat], var: &Pat) -> bool {
+    !existing_pats.iter().any(|pat| does_pat_match_variant(pat, var))
+}
+
+// Fixme: this is still somewhat limited, use hir_ty::diagnostics::match_check?
+fn does_pat_match_variant(pat: &Pat, var: &Pat) -> bool {
+    match (pat, var) {
+        (Pat::WildcardPat(_), _) => true,
+        (Pat::TuplePat(tpat), Pat::TuplePat(tvar)) => {
+            tpat.fields().zip(tvar.fields()).all(|(p, v)| does_pat_match_variant(&p, &v))
+        }
+        _ => utils::does_pat_match_variant(pat, var),
+    }
+}
+
+#[derive(Eq, PartialEq, Clone, Copy)]
+enum ExtendedEnum {
+    Bool,
+    Enum(hir::Enum),
+}
+
+#[derive(Eq, PartialEq, Clone, Copy)]
+enum ExtendedVariant {
+    True,
+    False,
+    Variant(hir::Variant),
+}
+
+fn lift_enum(e: hir::Enum) -> ExtendedEnum {
+    ExtendedEnum::Enum(e)
+}
+
+impl ExtendedEnum {
+    fn variants(self, db: &RootDatabase) -> Vec<ExtendedVariant> {
+        match self {
+            ExtendedEnum::Enum(e) => {
+                e.variants(db).into_iter().map(ExtendedVariant::Variant).collect::<Vec<_>>()
+            }
+            ExtendedEnum::Bool => {
+                Vec::<ExtendedVariant>::from([ExtendedVariant::True, ExtendedVariant::False])
+            }
+        }
+    }
+}
+
+fn resolve_enum_def(sema: &Semantics<RootDatabase>, expr: &ast::Expr) -> Option<ExtendedEnum> {
+    sema.type_of_expr(expr)?.adjusted().autoderef(sema.db).find_map(|ty| match ty.as_adt() {
+        Some(Adt::Enum(e)) => Some(ExtendedEnum::Enum(e)),
+        _ => ty.is_bool().then(|| ExtendedEnum::Bool),
+    })
+}
+
+fn resolve_tuple_of_enum_def(
+    sema: &Semantics<RootDatabase>,
+    expr: &ast::Expr,
+) -> Option<Vec<ExtendedEnum>> {
+    sema.type_of_expr(expr)?
+        .adjusted()
+        .tuple_fields(sema.db)
+        .iter()
+        .map(|ty| {
+            ty.autoderef(sema.db).find_map(|ty| match ty.as_adt() {
+                Some(Adt::Enum(e)) => Some(lift_enum(e)),
+                // For now we only handle expansion for a tuple of enums. Here
+                // we map non-enum items to None and rely on `collect` to
+                // convert Vec<Option<hir::Enum>> into Option<Vec<hir::Enum>>.
+                _ => ty.is_bool().then(|| ExtendedEnum::Bool),
+            })
+        })
+        .collect()
+}
+
+fn build_pat(db: &RootDatabase, module: hir::Module, var: ExtendedVariant) -> Option<ast::Pat> {
+    match var {
+        ExtendedVariant::Variant(var) => {
+            let path = mod_path_to_ast(&module.find_use_path(db, ModuleDef::from(var))?);
+
+            // FIXME: use HIR for this; it doesn't currently expose struct vs. tuple vs. unit variants though
+            let pat: ast::Pat = match var.source(db)?.value.kind() {
+                ast::StructKind::Tuple(field_list) => {
+                    let pats =
+                        iter::repeat(make::wildcard_pat().into()).take(field_list.fields().count());
+                    make::tuple_struct_pat(path, pats).into()
+                }
+                ast::StructKind::Record(field_list) => {
+                    let pats = field_list
+                        .fields()
+                        .map(|f| make::ext::simple_ident_pat(f.name().unwrap()).into());
+                    make::record_pat(path, pats).into()
+                }
+                ast::StructKind::Unit => make::path_pat(path),
+            };
+
+            Some(pat)
+        }
+        ExtendedVariant::True => Some(ast::Pat::from(make::literal_pat("true"))),
+        ExtendedVariant::False => Some(ast::Pat::from(make::literal_pat("false"))),
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use crate::tests::{
+        check_assist, check_assist_not_applicable, check_assist_target, check_assist_unresolved,
+    };
+
+    use super::add_missing_match_arms;
+
+    #[test]
+    fn all_match_arms_provided() {
+        check_assist_not_applicable(
+            add_missing_match_arms,
+            r#"
+enum A {
+    As,
+    Bs{x:i32, y:Option<i32>},
+    Cs(i32, Option<i32>),
+}
+fn main() {
+    match A::As$0 {
+        A::As,
+        A::Bs{x,y:Some(_)} => {}
+        A::Cs(_, Some(_)) => {}
+    }
+}
+            "#,
+        );
+    }
+
+    #[test]
+    fn all_boolean_match_arms_provided() {
+        check_assist_not_applicable(
+            add_missing_match_arms,
+            r#"
+fn foo(a: bool) {
+    match a$0 {
+        true => {}
+        false => {}
+    }
+}
+"#,
+        )
+    }
+
+    #[test]
+    fn tuple_of_non_enum() {
+        // for now this case is not handled, although it potentially could be
+        // in the future
+        check_assist_not_applicable(
+            add_missing_match_arms,
+            r#"
+fn main() {
+    match (0, false)$0 {
+    }
+}
+"#,
+        );
+    }
+
+    #[test]
+    fn add_missing_match_arms_boolean() {
+        check_assist(
+            add_missing_match_arms,
+            r#"
+fn foo(a: bool) {
+    match a$0 {
+    }
+}
+"#,
+            r#"
+fn foo(a: bool) {
+    match a {
+        $0true => todo!(),
+        false => todo!(),
+    }
+}
+"#,
+        )
+    }
+
+    #[test]
+    fn partial_fill_boolean() {
+        check_assist(
+            add_missing_match_arms,
+            r#"
+fn foo(a: bool) {
+    match a$0 {
+        true => {}
+    }
+}
+"#,
+            r#"
+fn foo(a: bool) {
+    match a {
+        true => {}
+        $0false => todo!(),
+    }
+}
+"#,
+        )
+    }
+
+    #[test]
+    fn all_boolean_tuple_arms_provided() {
+        check_assist_not_applicable(
+            add_missing_match_arms,
+            r#"
+fn foo(a: bool) {
+    match (a, a)$0 {
+        (true, true) => {}
+        (true, false) => {}
+        (false, true) => {}
+        (false, false) => {}
+    }
+}
+"#,
+        )
+    }
+
+    #[test]
+    fn fill_boolean_tuple() {
+        check_assist(
+            add_missing_match_arms,
+            r#"
+fn foo(a: bool) {
+    match (a, a)$0 {
+    }
+}
+"#,
+            r#"
+fn foo(a: bool) {
+    match (a, a) {
+        $0(true, true) => todo!(),
+        (true, false) => todo!(),
+        (false, true) => todo!(),
+        (false, false) => todo!(),
+    }
+}
+"#,
+        )
+    }
+
+    #[test]
+    fn partial_fill_boolean_tuple() {
+        check_assist(
+            add_missing_match_arms,
+            r#"
+fn foo(a: bool) {
+    match (a, a)$0 {
+        (false, true) => {}
+    }
+}
+"#,
+            r#"
+fn foo(a: bool) {
+    match (a, a) {
+        (false, true) => {}
+        $0(true, true) => todo!(),
+        (true, false) => todo!(),
+        (false, false) => todo!(),
+    }
+}
+"#,
+        )
+    }
+
+    #[test]
+    fn partial_fill_record_tuple() {
+        check_assist(
+            add_missing_match_arms,
+            r#"
+enum A {
+    As,
+    Bs { x: i32, y: Option<i32> },
+    Cs(i32, Option<i32>),
+}
+fn main() {
+    match A::As$0 {
+        A::Bs { x, y: Some(_) } => {}
+        A::Cs(_, Some(_)) => {}
+    }
+}
+"#,
+            r#"
+enum A {
+    As,
+    Bs { x: i32, y: Option<i32> },
+    Cs(i32, Option<i32>),
+}
+fn main() {
+    match A::As {
+        A::Bs { x, y: Some(_) } => {}
+        A::Cs(_, Some(_)) => {}
+        $0A::As => todo!(),
+    }
+}
+"#,
+        );
+    }
+
+    #[test]
+    fn partial_fill_option() {
+        check_assist(
+            add_missing_match_arms,
+            r#"
+//- minicore: option
+fn main() {
+    match None$0 {
+        None => {}
+    }
+}
+"#,
+            r#"
+fn main() {
+    match None {
+        None => {}
+        Some(${0:_}) => todo!(),
+    }
+}
+"#,
+        );
+    }
+
+    #[test]
+    fn partial_fill_or_pat() {
+        check_assist(
+            add_missing_match_arms,
+            r#"
+enum A { As, Bs, Cs(Option<i32>) }
+fn main() {
+    match A::As$0 {
+        A::Cs(_) | A::Bs => {}
+    }
+}
+"#,
+            r#"
+enum A { As, Bs, Cs(Option<i32>) }
+fn main() {
+    match A::As {
+        A::Cs(_) | A::Bs => {}
+        $0A::As => todo!(),
+    }
+}
+"#,
+        );
+    }
+
+    #[test]
+    fn partial_fill() {
+        check_assist(
+            add_missing_match_arms,
+            r#"
+enum A { As, Bs, Cs, Ds(String), Es(B) }
+enum B { Xs, Ys }
+fn main() {
+    match A::As$0 {
+        A::Bs if 0 < 1 => {}
+        A::Ds(_value) => { let x = 1; }
+        A::Es(B::Xs) => (),
+    }
+}
+"#,
+            r#"
+enum A { As, Bs, Cs, Ds(String), Es(B) }
+enum B { Xs, Ys }
+fn main() {
+    match A::As {
+        A::Bs if 0 < 1 => {}
+        A::Ds(_value) => { let x = 1; }
+        A::Es(B::Xs) => (),
+        $0A::As => todo!(),
+        A::Cs => todo!(),
+    }
+}
+"#,
+        );
+    }
+
+    #[test]
+    fn partial_fill_bind_pat() {
+        check_assist(
+            add_missing_match_arms,
+            r#"
+enum A { As, Bs, Cs(Option<i32>) }
+fn main() {
+    match A::As$0 {
+        A::As(_) => {}
+        a @ A::Bs(_) => {}
+    }
+}
+"#,
+            r#"
+enum A { As, Bs, Cs(Option<i32>) }
+fn main() {
+    match A::As {
+        A::As(_) => {}
+        a @ A::Bs(_) => {}
+        A::Cs(${0:_}) => todo!(),
+    }
+}
+"#,
+        );
+    }
+
+    #[test]
+    fn add_missing_match_arms_empty_body() {
+        check_assist(
+            add_missing_match_arms,
+            r#"
+enum A { As, Bs, Cs(String), Ds(String, String), Es { x: usize, y: usize } }
+
+fn main() {
+    let a = A::As;
+    match a$0 {}
+}
+"#,
+            r#"
+enum A { As, Bs, Cs(String), Ds(String, String), Es { x: usize, y: usize } }
+
+fn main() {
+    let a = A::As;
+    match a {
+        $0A::As => todo!(),
+        A::Bs => todo!(),
+        A::Cs(_) => todo!(),
+        A::Ds(_, _) => todo!(),
+        A::Es { x, y } => todo!(),
+    }
+}
+"#,
+        );
+    }
+
+    #[test]
+    fn add_missing_match_arms_tuple_of_enum() {
+        check_assist(
+            add_missing_match_arms,
+            r#"
+enum A { One, Two }
+enum B { One, Two }
+
+fn main() {
+    let a = A::One;
+    let b = B::One;
+    match (a$0, b) {}
+}
+"#,
+            r#"
+enum A { One, Two }
+enum B { One, Two }
+
+fn main() {
+    let a = A::One;
+    let b = B::One;
+    match (a, b) {
+        $0(A::One, B::One) => todo!(),
+        (A::One, B::Two) => todo!(),
+        (A::Two, B::One) => todo!(),
+        (A::Two, B::Two) => todo!(),
+    }
+}
+"#,
+        );
+    }
+
+    #[test]
+    fn add_missing_match_arms_tuple_of_enum_ref() {
+        check_assist(
+            add_missing_match_arms,
+            r#"
+enum A { One, Two }
+enum B { One, Two }
+
+fn main() {
+    let a = A::One;
+    let b = B::One;
+    match (&a$0, &b) {}
+}
+"#,
+            r#"
+enum A { One, Two }
+enum B { One, Two }
+
+fn main() {
+    let a = A::One;
+    let b = B::One;
+    match (&a, &b) {
+        $0(A::One, B::One) => todo!(),
+        (A::One, B::Two) => todo!(),
+        (A::Two, B::One) => todo!(),
+        (A::Two, B::Two) => todo!(),
+    }
+}
+"#,
+        );
+    }
+
+    #[test]
+    fn add_missing_match_arms_tuple_of_enum_partial() {
+        check_assist(
+            add_missing_match_arms,
+            r#"
+enum A { One, Two }
+enum B { One, Two }
+
+fn main() {
+    let a = A::One;
+    let b = B::One;
+    match (a$0, b) {
+        (A::Two, B::One) => {}
+    }
+}
+"#,
+            r#"
+enum A { One, Two }
+enum B { One, Two }
+
+fn main() {
+    let a = A::One;
+    let b = B::One;
+    match (a, b) {
+        (A::Two, B::One) => {}
+        $0(A::One, B::One) => todo!(),
+        (A::One, B::Two) => todo!(),
+        (A::Two, B::Two) => todo!(),
+    }
+}
+"#,
+        );
+    }
+
+    #[test]
+    fn add_missing_match_arms_tuple_of_enum_partial_with_wildcards() {
+        check_assist(
+            add_missing_match_arms,
+            r#"
+//- minicore: option
+fn main() {
+    let a = Some(1);
+    let b = Some(());
+    match (a$0, b) {
+        (Some(_), _) => {}
+        (None, Some(_)) => {}
+    }
+}
+"#,
+            r#"
+fn main() {
+    let a = Some(1);
+    let b = Some(());
+    match (a, b) {
+        (Some(_), _) => {}
+        (None, Some(_)) => {}
+        $0(None, None) => todo!(),
+    }
+}
+"#,
+        );
+    }
+
+    #[test]
+    fn add_missing_match_arms_partial_with_deep_pattern() {
+        // Fixme: cannot handle deep patterns
+        check_assist_not_applicable(
+            add_missing_match_arms,
+            r#"
+//- minicore: option
+fn main() {
+    match $0Some(true) {
+        Some(true) => {}
+        None => {}
+    }
+}
+"#,
+        );
+    }
+
+    #[test]
+    fn add_missing_match_arms_tuple_of_enum_not_applicable() {
+        check_assist_not_applicable(
+            add_missing_match_arms,
+            r#"
+enum A { One, Two }
+enum B { One, Two }
+
+fn main() {
+    let a = A::One;
+    let b = B::One;
+    match (a$0, b) {
+        (A::Two, B::One) => {}
+        (A::One, B::One) => {}
+        (A::One, B::Two) => {}
+        (A::Two, B::Two) => {}
+    }
+}
+"#,
+        );
+    }
+
+    #[test]
+    fn add_missing_match_arms_single_element_tuple_of_enum() {
+        check_assist(
+            add_missing_match_arms,
+            r#"
+enum A { One, Two }
+
+fn main() {
+    let a = A::One;
+    match (a$0, ) {
+    }
+}
+"#,
+            r#"
+enum A { One, Two }
+
+fn main() {
+    let a = A::One;
+    match (a, ) {
+        $0(A::One,) => todo!(),
+        (A::Two,) => todo!(),
+    }
+}
+"#,
+        );
+    }
+
+    #[test]
+    fn test_fill_match_arm_refs() {
+        check_assist(
+            add_missing_match_arms,
+            r#"
+enum A { As }
+
+fn foo(a: &A) {
+    match a$0 {
+    }
+}
+"#,
+            r#"
+enum A { As }
+
+fn foo(a: &A) {
+    match a {
+        $0A::As => todo!(),
+    }
+}
+"#,
+        );
+
+        check_assist(
+            add_missing_match_arms,
+            r#"
+enum A {
+    Es { x: usize, y: usize }
+}
+
+fn foo(a: &mut A) {
+    match a$0 {
+    }
+}
+"#,
+            r#"
+enum A {
+    Es { x: usize, y: usize }
+}
+
+fn foo(a: &mut A) {
+    match a {
+        $0A::Es { x, y } => todo!(),
+    }
+}
+"#,
+        );
+    }
+
+    #[test]
+    fn add_missing_match_arms_target() {
+        check_assist_target(
+            add_missing_match_arms,
+            r#"
+enum E { X, Y }
+
+fn main() {
+    match E::X$0 {}
+}
+"#,
+            "match E::X {}",
+        );
+    }
+
+    #[test]
+    fn add_missing_match_arms_trivial_arm() {
+        check_assist(
+            add_missing_match_arms,
+            r#"
+enum E { X, Y }
+
+fn main() {
+    match E::X {
+        $0_ => {}
+    }
+}
+"#,
+            r#"
+enum E { X, Y }
+
+fn main() {
+    match E::X {
+        $0E::X => todo!(),
+        E::Y => todo!(),
+    }
+}
+"#,
+        );
+    }
+
+    #[test]
+    fn add_missing_match_arms_qualifies_path() {
+        check_assist(
+            add_missing_match_arms,
+            r#"
+mod foo { pub enum E { X, Y } }
+use foo::E::X;
+
+fn main() {
+    match X {
+        $0
+    }
+}
+"#,
+            r#"
+mod foo { pub enum E { X, Y } }
+use foo::E::X;
+
+fn main() {
+    match X {
+        $0X => todo!(),
+        foo::E::Y => todo!(),
+    }
+}
+"#,
+        );
+    }
+
+    #[test]
+    fn add_missing_match_arms_preserves_comments() {
+        check_assist(
+            add_missing_match_arms,
+            r#"
+enum A { One, Two }
+fn foo(a: A) {
+    match a {
+        // foo bar baz$0
+        A::One => {}
+        // This is where the rest should be
+    }
+}
+"#,
+            r#"
+enum A { One, Two }
+fn foo(a: A) {
+    match a {
+        // foo bar baz
+        A::One => {}
+        $0A::Two => todo!(),
+        // This is where the rest should be
+    }
+}
+"#,
+        );
+    }
+
+    #[test]
+    fn add_missing_match_arms_preserves_comments_empty() {
+        check_assist(
+            add_missing_match_arms,
+            r#"
+enum A { One, Two }
+fn foo(a: A) {
+    match a {
+        // foo bar baz$0
+    }
+}
+"#,
+            r#"
+enum A { One, Two }
+fn foo(a: A) {
+    match a {
+        $0A::One => todo!(),
+        A::Two => todo!(),
+        // foo bar baz
+    }
+}
+"#,
+        );
+    }
+
+    #[test]
+    fn add_missing_match_arms_placeholder() {
+        check_assist(
+            add_missing_match_arms,
+            r#"
+enum A { One, Two, }
+fn foo(a: A) {
+    match a$0 {
+        _ => (),
+    }
+}
+"#,
+            r#"
+enum A { One, Two, }
+fn foo(a: A) {
+    match a {
+        $0A::One => todo!(),
+        A::Two => todo!(),
+    }
+}
+"#,
+        );
+    }
+
+    #[test]
+    fn option_order() {
+        cov_mark::check!(option_order);
+        check_assist(
+            add_missing_match_arms,
+            r#"
+//- minicore: option
+fn foo(opt: Option<i32>) {
+    match opt$0 {
+    }
+}
+"#,
+            r#"
+fn foo(opt: Option<i32>) {
+    match opt {
+        Some(${0:_}) => todo!(),
+        None => todo!(),
+    }
+}
+"#,
+        );
+    }
+
+    #[test]
+    fn works_inside_macro_call() {
+        check_assist(
+            add_missing_match_arms,
+            r#"
+macro_rules! m { ($expr:expr) => {$expr}}
+enum Test {
+    A,
+    B,
+    C,
+}
+
+fn foo(t: Test) {
+    m!(match t$0 {});
+}"#,
+            r#"
+macro_rules! m { ($expr:expr) => {$expr}}
+enum Test {
+    A,
+    B,
+    C,
+}
+
+fn foo(t: Test) {
+    m!(match t {
+    $0Test::A => todo!(),
+    Test::B => todo!(),
+    Test::C => todo!(),
+});
+}"#,
+        );
+    }
+
+    #[test]
+    fn lazy_computation() {
+        // Computing a single missing arm is enough to determine applicability of the assist.
+        cov_mark::check_count!(add_missing_match_arms_lazy_computation, 1);
+        check_assist_unresolved(
+            add_missing_match_arms,
+            r#"
+enum A { One, Two, }
+fn foo(tuple: (A, A)) {
+    match $0tuple {};
+}
+"#,
+        );
+    }
+
+    #[test]
+    fn adds_comma_before_new_arms() {
+        check_assist(
+            add_missing_match_arms,
+            r#"
+fn foo(t: bool) {
+    match $0t {
+        true => 1 + 2
+    }
+}"#,
+            r#"
+fn foo(t: bool) {
+    match t {
+        true => 1 + 2,
+        $0false => todo!(),
+    }
+}"#,
+        );
+    }
+
+    #[test]
+    fn does_not_add_extra_comma() {
+        check_assist(
+            add_missing_match_arms,
+            r#"
+fn foo(t: bool) {
+    match $0t {
+        true => 1 + 2,
+    }
+}"#,
+            r#"
+fn foo(t: bool) {
+    match t {
+        true => 1 + 2,
+        $0false => todo!(),
+    }
+}"#,
+        );
+    }
+
+    #[test]
+    fn does_not_remove_catch_all_with_non_empty_expr() {
+        cov_mark::check!(add_missing_match_arms_empty_expr);
+        check_assist(
+            add_missing_match_arms,
+            r#"
+fn foo(t: bool) {
+    match $0t {
+        _ => 1 + 2,
+    }
+}"#,
+            r#"
+fn foo(t: bool) {
+    match t {
+        _ => 1 + 2,
+        $0true => todo!(),
+        false => todo!(),
+    }
+}"#,
+        );
+    }
+}
diff --git a/crates/ide_assists/src/handlers/add_return_type.rs b/crates/ide_assists/src/handlers/add_return_type.rs
new file mode 100644 (file)
index 0000000..ce5981b
--- /dev/null
@@ -0,0 +1,344 @@
+use hir::HirDisplay;
+use syntax::{ast, AstNode, TextRange, TextSize};
+
+use crate::{AssistContext, AssistId, AssistKind, Assists};
+
+// Assist: add_return_type
+//
+// Adds the return type to a function or closure inferred from its tail expression if it doesn't have a return
+// type specified. This assists is useable in a functions or closures tail expression or return type position.
+//
+// ```
+// fn foo() { 4$02i32 }
+// ```
+// ->
+// ```
+// fn foo() -> i32 { 42i32 }
+// ```
+pub(crate) fn add_return_type(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
+    let (fn_type, tail_expr, builder_edit_pos) = extract_tail(ctx)?;
+    let module = ctx.sema.scope(tail_expr.syntax()).module()?;
+    let ty = ctx.sema.type_of_expr(&tail_expr)?.adjusted();
+    if ty.is_unit() {
+        return None;
+    }
+    let ty = ty.display_source_code(ctx.db(), module.into()).ok()?;
+
+    acc.add(
+        AssistId("add_return_type", AssistKind::RefactorRewrite),
+        match fn_type {
+            FnType::Function => "Add this function's return type",
+            FnType::Closure { .. } => "Add this closure's return type",
+        },
+        tail_expr.syntax().text_range(),
+        |builder| {
+            match builder_edit_pos {
+                InsertOrReplace::Insert(insert_pos) => {
+                    builder.insert(insert_pos, &format!("-> {} ", ty))
+                }
+                InsertOrReplace::Replace(text_range) => {
+                    builder.replace(text_range, &format!("-> {}", ty))
+                }
+            }
+            if let FnType::Closure { wrap_expr: true } = fn_type {
+                cov_mark::hit!(wrap_closure_non_block_expr);
+                // `|x| x` becomes `|x| -> T x` which is invalid, so wrap it in a block
+                builder.replace(tail_expr.syntax().text_range(), &format!("{{{}}}", tail_expr));
+            }
+        },
+    )
+}
+
+enum InsertOrReplace {
+    Insert(TextSize),
+    Replace(TextRange),
+}
+
+/// Check the potentially already specified return type and reject it or turn it into a builder command
+/// if allowed.
+fn ret_ty_to_action(ret_ty: Option<ast::RetType>, insert_pos: TextSize) -> Option<InsertOrReplace> {
+    match ret_ty {
+        Some(ret_ty) => match ret_ty.ty() {
+            Some(ast::Type::InferType(_)) | None => {
+                cov_mark::hit!(existing_infer_ret_type);
+                cov_mark::hit!(existing_infer_ret_type_closure);
+                Some(InsertOrReplace::Replace(ret_ty.syntax().text_range()))
+            }
+            _ => {
+                cov_mark::hit!(existing_ret_type);
+                cov_mark::hit!(existing_ret_type_closure);
+                None
+            }
+        },
+        None => Some(InsertOrReplace::Insert(insert_pos + TextSize::from(1))),
+    }
+}
+
+enum FnType {
+    Function,
+    Closure { wrap_expr: bool },
+}
+
+fn extract_tail(ctx: &AssistContext) -> Option<(FnType, ast::Expr, InsertOrReplace)> {
+    let (fn_type, tail_expr, return_type_range, action) =
+        if let Some(closure) = ctx.find_node_at_offset::<ast::ClosureExpr>() {
+            let rpipe_pos = closure.param_list()?.syntax().last_token()?.text_range().end();
+            let action = ret_ty_to_action(closure.ret_type(), rpipe_pos)?;
+
+            let body = closure.body()?;
+            let body_start = body.syntax().first_token()?.text_range().start();
+            let (tail_expr, wrap_expr) = match body {
+                ast::Expr::BlockExpr(block) => (block.tail_expr()?, false),
+                body => (body, true),
+            };
+
+            let ret_range = TextRange::new(rpipe_pos, body_start);
+            (FnType::Closure { wrap_expr }, tail_expr, ret_range, action)
+        } else {
+            let func = ctx.find_node_at_offset::<ast::Fn>()?;
+            let rparen_pos = func.param_list()?.r_paren_token()?.text_range().end();
+            let action = ret_ty_to_action(func.ret_type(), rparen_pos)?;
+
+            let body = func.body()?;
+            let tail_expr = body.tail_expr()?;
+
+            let ret_range_end = body.l_curly_token()?.text_range().start();
+            let ret_range = TextRange::new(rparen_pos, ret_range_end);
+            (FnType::Function, tail_expr, ret_range, action)
+        };
+    let frange = ctx.frange.range;
+    if return_type_range.contains_range(frange) {
+        cov_mark::hit!(cursor_in_ret_position);
+        cov_mark::hit!(cursor_in_ret_position_closure);
+    } else if tail_expr.syntax().text_range().contains_range(frange) {
+        cov_mark::hit!(cursor_on_tail);
+        cov_mark::hit!(cursor_on_tail_closure);
+    } else {
+        return None;
+    }
+    Some((fn_type, tail_expr, action))
+}
+
+#[cfg(test)]
+mod tests {
+    use crate::tests::{check_assist, check_assist_not_applicable};
+
+    use super::*;
+
+    #[test]
+    fn infer_return_type_specified_inferred() {
+        cov_mark::check!(existing_infer_ret_type);
+        check_assist(
+            add_return_type,
+            r#"fn foo() -> $0_ {
+    45
+}"#,
+            r#"fn foo() -> i32 {
+    45
+}"#,
+        );
+    }
+
+    #[test]
+    fn infer_return_type_specified_inferred_closure() {
+        cov_mark::check!(existing_infer_ret_type_closure);
+        check_assist(
+            add_return_type,
+            r#"fn foo() {
+    || -> _ {$045};
+}"#,
+            r#"fn foo() {
+    || -> i32 {45};
+}"#,
+        );
+    }
+
+    #[test]
+    fn infer_return_type_cursor_at_return_type_pos() {
+        cov_mark::check!(cursor_in_ret_position);
+        check_assist(
+            add_return_type,
+            r#"fn foo() $0{
+    45
+}"#,
+            r#"fn foo() -> i32 {
+    45
+}"#,
+        );
+    }
+
+    #[test]
+    fn infer_return_type_cursor_at_return_type_pos_closure() {
+        cov_mark::check!(cursor_in_ret_position_closure);
+        check_assist(
+            add_return_type,
+            r#"fn foo() {
+    || $045
+}"#,
+            r#"fn foo() {
+    || -> i32 {45}
+}"#,
+        );
+    }
+
+    #[test]
+    fn infer_return_type() {
+        cov_mark::check!(cursor_on_tail);
+        check_assist(
+            add_return_type,
+            r#"fn foo() {
+    45$0
+}"#,
+            r#"fn foo() -> i32 {
+    45
+}"#,
+        );
+    }
+
+    #[test]
+    fn infer_return_type_nested() {
+        check_assist(
+            add_return_type,
+            r#"fn foo() {
+    if true {
+        3$0
+    } else {
+        5
+    }
+}"#,
+            r#"fn foo() -> i32 {
+    if true {
+        3
+    } else {
+        5
+    }
+}"#,
+        );
+    }
+
+    #[test]
+    fn not_applicable_ret_type_specified() {
+        cov_mark::check!(existing_ret_type);
+        check_assist_not_applicable(
+            add_return_type,
+            r#"fn foo() -> i32 {
+    ( 45$0 + 32 ) * 123
+}"#,
+        );
+    }
+
+    #[test]
+    fn not_applicable_non_tail_expr() {
+        check_assist_not_applicable(
+            add_return_type,
+            r#"fn foo() {
+    let x = $03;
+    ( 45 + 32 ) * 123
+}"#,
+        );
+    }
+
+    #[test]
+    fn not_applicable_unit_return_type() {
+        check_assist_not_applicable(
+            add_return_type,
+            r#"fn foo() {
+    ($0)
+}"#,
+        );
+    }
+
+    #[test]
+    fn infer_return_type_closure_block() {
+        cov_mark::check!(cursor_on_tail_closure);
+        check_assist(
+            add_return_type,
+            r#"fn foo() {
+    |x: i32| {
+        x$0
+    };
+}"#,
+            r#"fn foo() {
+    |x: i32| -> i32 {
+        x
+    };
+}"#,
+        );
+    }
+
+    #[test]
+    fn infer_return_type_closure() {
+        check_assist(
+            add_return_type,
+            r#"fn foo() {
+    |x: i32| { x$0 };
+}"#,
+            r#"fn foo() {
+    |x: i32| -> i32 { x };
+}"#,
+        );
+    }
+
+    #[test]
+    fn infer_return_type_closure_wrap() {
+        cov_mark::check!(wrap_closure_non_block_expr);
+        check_assist(
+            add_return_type,
+            r#"fn foo() {
+    |x: i32| x$0;
+}"#,
+            r#"fn foo() {
+    |x: i32| -> i32 {x};
+}"#,
+        );
+    }
+
+    #[test]
+    fn infer_return_type_nested_closure() {
+        check_assist(
+            add_return_type,
+            r#"fn foo() {
+    || {
+        if true {
+            3$0
+        } else {
+            5
+        }
+    }
+}"#,
+            r#"fn foo() {
+    || -> i32 {
+        if true {
+            3
+        } else {
+            5
+        }
+    }
+}"#,
+        );
+    }
+
+    #[test]
+    fn not_applicable_ret_type_specified_closure() {
+        cov_mark::check!(existing_ret_type_closure);
+        check_assist_not_applicable(
+            add_return_type,
+            r#"fn foo() {
+    || -> i32 { 3$0 }
+}"#,
+        );
+    }
+
+    #[test]
+    fn not_applicable_non_tail_expr_closure() {
+        check_assist_not_applicable(
+            add_return_type,
+            r#"fn foo() {
+    || -> i32 {
+        let x = 3$0;
+        6
+    }
+}"#,
+        );
+    }
+}
index 5adb3f5a1b3cccf9eaf357d3e00dd4a768cf8cd7..497e7c2546d0d0e0436d2fb1c7f579dbbc421e0e 100644 (file)
@@ -198,7 +198,7 @@ fn option_variants(
     sema: &Semantics<RootDatabase>,
     expr: &SyntaxNode,
 ) -> Option<(hir::Variant, hir::Variant)> {
-    let fam = FamousDefs(&sema, sema.scope(expr).krate());
+    let fam = FamousDefs(sema, sema.scope(expr).krate());
     let option_variants = fam.core_option_Option()?.variants(sema.db);
     match &*option_variants {
         &[variant0, variant1] => Some(if variant0.name(sema.db) == known::None {
@@ -224,7 +224,7 @@ fn is_invalid_body(
         invalid
     });
     if !invalid {
-        for_each_tail_expr(&expr, &mut |e| {
+        for_each_tail_expr(expr, &mut |e| {
             if invalid {
                 return;
             }
index 749e8685bf18e24e8af4f513545e3b86202b2b52..472aef26480bd3c31d061b6424738994118f6e3f 100644 (file)
@@ -6,21 +6,21 @@
 
 use crate::{AssistContext, AssistId, AssistKind, Assists};
 
-/// Assist: line_to_block
-///
-/// Converts comments between block and single-line form
-///
-/// ```
-///    // Multi-line
-///    // comment
-/// ```
-/// ->
-/// ```
-///   /**
-///   Multi-line
-///   comment
-///   */
-/// ```
+// Assist: line_to_block
+//
+// Converts comments between block and single-line form.
+//
+// ```
+//    // Multi-line$0
+//    // comment
+// ```
+// ->
+// ```
+//   /*
+//   Multi-line
+//   comment
+//   */
+// ```
 pub(crate) fn convert_comment_block(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
     let comment = ctx.find_token_at_offset::<ast::Comment>()?;
     // Only allow comments which are alone on their line
index de79023f01714220deb44a370f4b1179c6c4fc6b..aef03e3238e13120472af198665f8de5b9617ecc 100644 (file)
@@ -1,6 +1,8 @@
+use hir::known;
 use ide_db::helpers::FamousDefs;
+use stdx::format_to;
 use syntax::{
-    ast::{self, edit_in_place::Indent, make, ArgListOwner},
+    ast::{self, edit_in_place::Indent, make, ArgListOwner, LoopBodyOwner},
     AstNode,
 };
 
@@ -30,7 +32,6 @@
 //     }
 // }
 // ```
-
 pub(crate) fn convert_iter_for_each_to_for(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
     let method = ctx.find_node_at_offset::<ast::MethodCallExpr>()?;
 
@@ -69,6 +70,130 @@ pub(crate) fn convert_iter_for_each_to_for(acc: &mut Assists, ctx: &AssistContex
     )
 }
 
+// Assist: convert_for_loop_with_for_each
+//
+// Converts a for loop into a for_each loop on the Iterator.
+//
+// ```
+// fn main() {
+//     let x = vec![1, 2, 3];
+//     for$0 v in x {
+//         let y = v * 2;
+//     }
+// }
+// ```
+// ->
+// ```
+// fn main() {
+//     let x = vec![1, 2, 3];
+//     x.into_iter().for_each(|v| {
+//         let y = v * 2;
+//     });
+// }
+// ```
+pub(crate) fn convert_for_loop_with_for_each(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
+    let for_loop = ctx.find_node_at_offset::<ast::ForExpr>()?;
+    let iterable = for_loop.iterable()?;
+    let pat = for_loop.pat()?;
+    let body = for_loop.loop_body()?;
+    if body.syntax().text_range().start() < ctx.offset() {
+        cov_mark::hit!(not_available_in_body);
+        return None;
+    }
+
+    acc.add(
+        AssistId("convert_for_loop_with_for_each", AssistKind::RefactorRewrite),
+        "Replace this for loop with `Iterator::for_each`",
+        for_loop.syntax().text_range(),
+        |builder| {
+            let mut buf = String::new();
+
+            if let Some((expr_behind_ref, method)) =
+                is_ref_and_impls_iter_method(&ctx.sema, &iterable)
+            {
+                // We have either "for x in &col" and col implements a method called iter
+                //             or "for x in &mut col" and col implements a method called iter_mut
+                format_to!(buf, "{}.{}()", expr_behind_ref, method);
+            } else if let ast::Expr::RangeExpr(..) = iterable {
+                // range expressions need to be parenthesized for the syntax to be correct
+                format_to!(buf, "({})", iterable);
+            } else if impls_core_iter(&ctx.sema, &iterable) {
+                format_to!(buf, "{}", iterable);
+            } else if let ast::Expr::RefExpr(_) = iterable {
+                format_to!(buf, "({}).into_iter()", iterable);
+            } else {
+                format_to!(buf, "{}.into_iter()", iterable);
+            }
+
+            format_to!(buf, ".for_each(|{}| {});", pat, body);
+
+            builder.replace(for_loop.syntax().text_range(), buf)
+        },
+    )
+}
+
+/// If iterable is a reference where the expression behind the reference implements a method
+/// returning an Iterator called iter or iter_mut (depending on the type of reference) then return
+/// the expression behind the reference and the method name
+fn is_ref_and_impls_iter_method(
+    sema: &hir::Semantics<ide_db::RootDatabase>,
+    iterable: &ast::Expr,
+) -> Option<(ast::Expr, hir::Name)> {
+    let ref_expr = match iterable {
+        ast::Expr::RefExpr(r) => r,
+        _ => return None,
+    };
+    let wanted_method = if ref_expr.mut_token().is_some() { known::iter_mut } else { known::iter };
+    let expr_behind_ref = ref_expr.expr()?;
+    let ty = sema.type_of_expr(&expr_behind_ref)?.adjusted();
+    let scope = sema.scope(iterable.syntax());
+    let krate = scope.module()?.krate();
+    let traits_in_scope = scope.traits_in_scope();
+    let iter_trait = FamousDefs(sema, Some(krate)).core_iter_Iterator()?;
+
+    let has_wanted_method = ty
+        .iterate_method_candidates(
+            sema.db,
+            krate,
+            &traits_in_scope,
+            Some(&wanted_method),
+            |_, func| {
+                if func.ret_type(sema.db).impls_trait(sema.db, iter_trait, &[]) {
+                    return Some(());
+                }
+                None
+            },
+        )
+        .is_some();
+    if !has_wanted_method {
+        return None;
+    }
+
+    Some((expr_behind_ref, wanted_method))
+}
+
+/// Whether iterable implements core::Iterator
+fn impls_core_iter(sema: &hir::Semantics<ide_db::RootDatabase>, iterable: &ast::Expr) -> bool {
+    let it_typ = match sema.type_of_expr(iterable) {
+        Some(it) => it.adjusted(),
+        None => return false,
+    };
+
+    let module = match sema.scope(iterable.syntax()).module() {
+        Some(it) => it,
+        None => return false,
+    };
+
+    let krate = module.krate();
+    match FamousDefs(sema, Some(krate)).core_iter_Iterator() {
+        Some(iter_trait) => {
+            cov_mark::hit!(test_already_impls_iterator);
+            it_typ.impls_trait(sema.db, iter_trait, &[])
+        }
+        None => false,
+    }
+}
+
 fn validate_method_call_expr(
     ctx: &AssistContext,
     expr: ast::MethodCallExpr,
@@ -195,4 +320,238 @@ fn main() {
 }"#,
         )
     }
+
+    #[test]
+    fn each_to_for_not_for() {
+        check_assist_not_applicable(
+            convert_for_loop_with_for_each,
+            r"
+let mut x = vec![1, 2, 3];
+x.iter_mut().$0for_each(|v| *v *= 2);
+        ",
+        )
+    }
+
+    #[test]
+    fn each_to_for_simple_for() {
+        check_assist(
+            convert_for_loop_with_for_each,
+            r"
+fn main() {
+    let x = vec![1, 2, 3];
+    for $0v in x {
+        v *= 2;
+    }
+}",
+            r"
+fn main() {
+    let x = vec![1, 2, 3];
+    x.into_iter().for_each(|v| {
+        v *= 2;
+    });
+}",
+        )
+    }
+
+    #[test]
+    fn each_to_for_for_in_range() {
+        check_assist(
+            convert_for_loop_with_for_each,
+            r#"
+//- minicore: range, iterators
+impl<T> core::iter::Iterator for core::ops::Range<T> {
+    type Item = T;
+
+    fn next(&mut self) -> Option<Self::Item> {
+        None
+    }
+}
+
+fn main() {
+    for $0x in 0..92 {
+        print!("{}", x);
+    }
+}"#,
+            r#"
+impl<T> core::iter::Iterator for core::ops::Range<T> {
+    type Item = T;
+
+    fn next(&mut self) -> Option<Self::Item> {
+        None
+    }
+}
+
+fn main() {
+    (0..92).for_each(|x| {
+        print!("{}", x);
+    });
+}"#,
+        )
+    }
+
+    #[test]
+    fn each_to_for_not_available_in_body() {
+        cov_mark::check!(not_available_in_body);
+        check_assist_not_applicable(
+            convert_for_loop_with_for_each,
+            r"
+fn main() {
+    let x = vec![1, 2, 3];
+    for v in x {
+        $0v *= 2;
+    }
+}",
+        )
+    }
+
+    #[test]
+    fn each_to_for_for_borrowed() {
+        check_assist(
+            convert_for_loop_with_for_each,
+            r#"
+//- minicore: iterators
+use core::iter::{Repeat, repeat};
+
+struct S;
+impl S {
+    fn iter(&self) -> Repeat<i32> { repeat(92) }
+    fn iter_mut(&mut self) -> Repeat<i32> { repeat(92) }
+}
+
+fn main() {
+    let x = S;
+    for $0v in &x {
+        let a = v * 2;
+    }
+}
+"#,
+            r#"
+use core::iter::{Repeat, repeat};
+
+struct S;
+impl S {
+    fn iter(&self) -> Repeat<i32> { repeat(92) }
+    fn iter_mut(&mut self) -> Repeat<i32> { repeat(92) }
+}
+
+fn main() {
+    let x = S;
+    x.iter().for_each(|v| {
+        let a = v * 2;
+    });
+}
+"#,
+        )
+    }
+
+    #[test]
+    fn each_to_for_for_borrowed_no_iter_method() {
+        check_assist(
+            convert_for_loop_with_for_each,
+            r"
+struct NoIterMethod;
+fn main() {
+    let x = NoIterMethod;
+    for $0v in &x {
+        let a = v * 2;
+    }
+}
+",
+            r"
+struct NoIterMethod;
+fn main() {
+    let x = NoIterMethod;
+    (&x).into_iter().for_each(|v| {
+        let a = v * 2;
+    });
+}
+",
+        )
+    }
+
+    #[test]
+    fn each_to_for_for_borrowed_mut() {
+        check_assist(
+            convert_for_loop_with_for_each,
+            r#"
+//- minicore: iterators
+use core::iter::{Repeat, repeat};
+
+struct S;
+impl S {
+    fn iter(&self) -> Repeat<i32> { repeat(92) }
+    fn iter_mut(&mut self) -> Repeat<i32> { repeat(92) }
+}
+
+fn main() {
+    let x = S;
+    for $0v in &mut x {
+        let a = v * 2;
+    }
+}
+"#,
+            r#"
+use core::iter::{Repeat, repeat};
+
+struct S;
+impl S {
+    fn iter(&self) -> Repeat<i32> { repeat(92) }
+    fn iter_mut(&mut self) -> Repeat<i32> { repeat(92) }
+}
+
+fn main() {
+    let x = S;
+    x.iter_mut().for_each(|v| {
+        let a = v * 2;
+    });
+}
+"#,
+        )
+    }
+
+    #[test]
+    fn each_to_for_for_borrowed_mut_behind_var() {
+        check_assist(
+            convert_for_loop_with_for_each,
+            r"
+fn main() {
+    let x = vec![1, 2, 3];
+    let y = &mut x;
+    for $0v in y {
+        *v *= 2;
+    }
+}",
+            r"
+fn main() {
+    let x = vec![1, 2, 3];
+    let y = &mut x;
+    y.into_iter().for_each(|v| {
+        *v *= 2;
+    });
+}",
+        )
+    }
+
+    #[test]
+    fn each_to_for_already_impls_iterator() {
+        cov_mark::check!(test_already_impls_iterator);
+        check_assist(
+            convert_for_loop_with_for_each,
+            r#"
+//- minicore: iterators
+fn main() {
+    for$0 a in core::iter::repeat(92).take(1) {
+        println!("{}", a);
+    }
+}
+"#,
+            r#"
+fn main() {
+    core::iter::repeat(92).take(1).for_each(|a| {
+        println!("{}", a);
+    });
+}
+"#,
+        );
+    }
 }
index fc5a17f052ccbb4593989cd9d204fb8d12d83a92..bb0382e15f93e276e5011b41cf3c36d533cebd9f 100644 (file)
@@ -110,7 +110,9 @@ fn edit_struct_def(
         } else {
             edit.insert(tuple_fields_text_range.start(), ast::make::tokens::single_space().text());
         }
-        strukt.semicolon_token().map(|t| edit.delete(t.text_range()));
+        if let Some(t) = strukt.semicolon_token() {
+            edit.delete(t.text_range());
+        }
     } else {
         edit.insert(tuple_fields_text_range.start(), ast::make::tokens::single_space().text());
     }
diff --git a/crates/ide_assists/src/handlers/convert_while_to_loop.rs b/crates/ide_assists/src/handlers/convert_while_to_loop.rs
new file mode 100644 (file)
index 0000000..cbddc10
--- /dev/null
@@ -0,0 +1,189 @@
+use std::iter::once;
+
+use syntax::{
+    ast::{
+        self,
+        edit::{AstNodeEdit, IndentLevel},
+        make, LoopBodyOwner,
+    },
+    AstNode, T,
+};
+
+use crate::{
+    assist_context::{AssistContext, Assists},
+    utils::invert_boolean_expression,
+    AssistId, AssistKind,
+};
+
+// Assist: convert_while_to_loop
+//
+// Replace a while with a loop.
+//
+// ```
+// fn main() {
+//     $0while cond {
+//         foo();
+//     }
+// }
+// ```
+// ->
+// ```
+// fn main() {
+//     loop {
+//         if !cond {
+//             break;
+//         }
+//         foo();
+//     }
+// }
+// ```
+pub(crate) fn convert_while_to_loop(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
+    let while_kw = ctx.find_token_syntax_at_offset(T![while])?;
+    let while_expr: ast::WhileExpr = while_kw.parent().and_then(ast::WhileExpr::cast)?;
+    let while_body = while_expr.loop_body()?;
+    let cond = while_expr.condition()?;
+
+    // Don't handle while let
+    if let Some(_) = cond.pat() {
+        return None;
+    };
+
+    let cond_expr = cond.expr()?;
+
+    let target = while_expr.syntax().text_range();
+    acc.add(
+        AssistId("convert_while_to_loop", AssistKind::RefactorRewrite),
+        "Convert while to loop",
+        target,
+        |edit| {
+            let while_indent_level = IndentLevel::from_node(while_expr.syntax());
+
+            let replacement = {
+                let if_expr = {
+                    let cond = invert_boolean_expression(cond_expr);
+                    let then_branch = make::block_expr(
+                        once(make::expr_stmt(make::expr_break(None)).into()),
+                        None,
+                    );
+
+                    make::expr_if(make::condition(cond, None), then_branch, None)
+                };
+
+                let if_expr = if_expr.indent(while_indent_level);
+                let stmts = once(make::expr_stmt(if_expr).into()).chain(while_body.statements());
+
+                let block_expr = make::block_expr(stmts, while_body.tail_expr());
+
+                let block_expr = block_expr.indent(while_indent_level);
+
+                make::expr_loop(block_expr)
+            };
+
+            edit.replace(target, replacement.syntax().text())
+        },
+    )
+}
+
+#[cfg(test)]
+mod tests {
+    use crate::tests::{check_assist, check_assist_not_applicable};
+
+    use super::*;
+
+    #[test]
+    fn convert_inside_fn() {
+        check_assist(
+            convert_while_to_loop,
+            r#"
+fn main() {
+    while$0 cond {
+        foo();
+    }
+}
+"#,
+            r#"
+fn main() {
+    loop {
+        if !cond {
+            break;
+        }
+        foo();
+    }
+}
+"#,
+        );
+    }
+
+    #[test]
+    fn convert_busy_wait() {
+        check_assist(
+            convert_while_to_loop,
+            r#"
+fn main() {
+    while$0 cond() {}
+}
+"#,
+            r#"
+fn main() {
+    loop {
+        if !cond() {
+            break;
+        }
+    }
+}
+"#,
+        );
+    }
+
+    #[test]
+    fn convert_trailing_expr() {
+        check_assist(
+            convert_while_to_loop,
+            r#"
+fn main() {
+    while$0 cond() {
+        bar()
+    }
+}
+"#,
+            r#"
+fn main() {
+    loop {
+        if !cond() {
+            break;
+        }
+        bar()
+    }
+}
+"#,
+        );
+    }
+
+    #[test]
+    fn ignore_while_let() {
+        check_assist_not_applicable(
+            convert_while_to_loop,
+            r#"
+fn main() {
+    while$0 let Some(_) = foo() {
+        bar();
+    }
+}
+"#,
+        );
+    }
+
+    #[test]
+    fn ignore_cursor_in_body() {
+        check_assist_not_applicable(
+            convert_while_to_loop,
+            r#"
+fn main() {
+    while cond {$0
+        bar();
+    }
+}
+"#,
+        );
+    }
+}
index aedb2f95f92eeb9e7292b1c85a9aad8067850cdd..6a07136cc89ba1fc043aea81b5de130f2e649440 100644 (file)
@@ -227,14 +227,12 @@ fn find_imported_defs(ctx: &AssistContext, star: SyntaxToken) -> Option<Vec<Def>
     Some(
         [Direction::Prev, Direction::Next]
             .iter()
-            .map(|dir| {
+            .flat_map(|dir| {
                 parent_use_item_syntax
                     .siblings(dir.to_owned())
                     .filter(|n| ast::Use::can_cast(n.kind()))
             })
-            .flatten()
-            .filter_map(|n| Some(n.descendants().filter_map(ast::NameRef::cast)))
-            .flatten()
+            .flat_map(|n| n.descendants().filter_map(ast::NameRef::cast))
             .filter_map(|r| match NameRefClass::classify(&ctx.sema, &r)? {
                 NameRefClass::Definition(Definition::ModuleDef(def)) => Some(Def::ModuleDef(def)),
                 NameRefClass::Definition(Definition::Macro(def)) => Some(Def::MacroDef(def)),
index 1e21050911d7cc878b4b286f49c6772f0c335ed2..c0cf3fac039dbd8ef4c42dad3ea9fa56951c90b8 100644 (file)
@@ -885,12 +885,9 @@ fn reference_is_exclusive(
 
 /// checks if this expr requires `&mut` access, recurses on field access
 fn expr_require_exclusive_access(ctx: &AssistContext, expr: &ast::Expr) -> Option<bool> {
-    match expr {
-        ast::Expr::MacroCall(_) => {
-            // FIXME: expand macro and check output for mutable usages of the variable?
-            return None;
-        }
-        _ => (),
+    if let ast::Expr::MacroCall(_) = expr {
+        // FIXME: expand macro and check output for mutable usages of the variable?
+        return None;
     }
 
     let parent = expr.syntax().parent()?;
index 961158811034dfaba2bb8413f48aab4fe8a4b9d2..3bc347b1e2e6ca121acc306784ede1fc697df6af 100644 (file)
@@ -231,7 +231,7 @@ fn create_struct_def(
     let variant_attrs = attrs_and_docs(variant.syntax())
         .map(|tok| match tok.kind() {
             WHITESPACE => make::tokens::single_newline().into(),
-            _ => tok.into(),
+            _ => tok,
         })
         .collect();
     ted::insert_all(Position::first_child_of(strukt.syntax()), variant_attrs);
@@ -251,12 +251,14 @@ fn update_variant(variant: &ast::Variant, generic: Option<ast::GenericParamList>
         Some(gpl) => {
             let gpl = gpl.clone_for_update();
             gpl.generic_params().for_each(|gp| {
-                match gp {
+                let tbl = match gp {
                     ast::GenericParam::LifetimeParam(it) => it.type_bound_list(),
                     ast::GenericParam::TypeParam(it) => it.type_bound_list(),
                     ast::GenericParam::ConstParam(_) => return,
+                };
+                if let Some(tbl) = tbl {
+                    tbl.remove();
                 }
-                .map(|it| it.remove());
             });
             make::ty(&format!("{}<{}>", name.text(), gpl.generic_params().join(", ")))
         }
index c72ed324237bbe8b7b709760db2c05db8959dbbc..9398b84b3fc85e4dea1517824dce1fa853ab2970 100644 (file)
@@ -79,7 +79,7 @@ pub(crate) fn extract_variable(acc: &mut Assists, ctx: &AssistContext) -> Option
             if let Anchor::Replace(stmt) = anchor {
                 cov_mark::hit!(test_extract_var_expr_stmt);
                 if stmt.semicolon_token().is_none() {
-                    buf.push_str(";");
+                    buf.push(';');
                 }
                 match ctx.config.snippet_cap {
                     Some(cap) => {
@@ -92,7 +92,7 @@ pub(crate) fn extract_variable(acc: &mut Assists, ctx: &AssistContext) -> Option
                 return;
             }
 
-            buf.push_str(";");
+            buf.push(';');
 
             // We want to maintain the indent level,
             // but we do not want to duplicate possible
diff --git a/crates/ide_assists/src/handlers/fill_match_arms.rs b/crates/ide_assists/src/handlers/fill_match_arms.rs
deleted file mode 100644 (file)
index f71b9e3..0000000
+++ /dev/null
@@ -1,1133 +0,0 @@
-use std::iter::{self, Peekable};
-
-use either::Either;
-use hir::{Adt, HasSource, ModuleDef, Semantics};
-use ide_db::helpers::{mod_path_to_ast, FamousDefs};
-use ide_db::RootDatabase;
-use itertools::Itertools;
-use syntax::ast::{self, make, AstNode, MatchArm, NameOwner, Pat};
-
-use crate::{
-    utils::{self, render_snippet, Cursor},
-    AssistContext, AssistId, AssistKind, Assists,
-};
-
-// Assist: fill_match_arms
-//
-// Adds missing clauses to a `match` expression.
-//
-// ```
-// enum Action { Move { distance: u32 }, Stop }
-//
-// fn handle(action: Action) {
-//     match action {
-//         $0
-//     }
-// }
-// ```
-// ->
-// ```
-// enum Action { Move { distance: u32 }, Stop }
-//
-// fn handle(action: Action) {
-//     match action {
-//         $0Action::Move { distance } => todo!(),
-//         Action::Stop => todo!(),
-//     }
-// }
-// ```
-pub(crate) fn fill_match_arms(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
-    let match_expr = ctx.find_node_at_offset_with_descend::<ast::MatchExpr>()?;
-    let match_arm_list = match_expr.match_arm_list()?;
-
-    let expr = match_expr.expr()?;
-
-    let mut arms: Vec<MatchArm> = match_arm_list.arms().collect();
-    if let [arm] = arms.as_slice() {
-        if let Some(Pat::WildcardPat(..)) = arm.pat() {
-            arms.clear();
-        }
-    }
-
-    let top_lvl_pats: Vec<_> = arms
-        .iter()
-        .filter_map(ast::MatchArm::pat)
-        .flat_map(|pat| match pat {
-            // Special case OrPat as separate top-level pats
-            Pat::OrPat(or_pat) => Either::Left(or_pat.pats()),
-            _ => Either::Right(iter::once(pat)),
-        })
-        // Exclude top level wildcards so that they are expanded by this assist, retains status quo in #8129.
-        .filter(|pat| !matches!(pat, Pat::WildcardPat(_)))
-        .collect();
-
-    let module = ctx.sema.scope(expr.syntax()).module()?;
-
-    let mut missing_pats: Peekable<Box<dyn Iterator<Item = ast::Pat>>> = if let Some(enum_def) =
-        resolve_enum_def(&ctx.sema, &expr)
-    {
-        let variants = enum_def.variants(ctx.db());
-
-        let missing_pats = variants
-            .into_iter()
-            .filter_map(|variant| build_pat(ctx.db(), module, variant))
-            .filter(|variant_pat| is_variant_missing(&top_lvl_pats, variant_pat));
-
-        let option_enum =
-            FamousDefs(&ctx.sema, Some(module.krate())).core_option_Option().map(lift_enum);
-        let missing_pats: Box<dyn Iterator<Item = _>> = if Some(enum_def) == option_enum {
-            // Match `Some` variant first.
-            cov_mark::hit!(option_order);
-            Box::new(missing_pats.rev())
-        } else {
-            Box::new(missing_pats)
-        };
-        missing_pats.peekable()
-    } else if let Some(enum_defs) = resolve_tuple_of_enum_def(&ctx.sema, &expr) {
-        let mut n_arms = 1;
-        let variants_of_enums: Vec<Vec<ExtendedVariant>> = enum_defs
-            .into_iter()
-            .map(|enum_def| enum_def.variants(ctx.db()))
-            .inspect(|variants| n_arms *= variants.len())
-            .collect();
-
-        // When calculating the match arms for a tuple of enums, we want
-        // to create a match arm for each possible combination of enum
-        // values. The `multi_cartesian_product` method transforms
-        // Vec<Vec<EnumVariant>> into Vec<(EnumVariant, .., EnumVariant)>
-        // where each tuple represents a proposed match arm.
-
-        // A number of arms grows very fast on even a small tuple of large enums.
-        // We skip the assist beyond an arbitrary threshold.
-        if n_arms > 256 {
-            return None;
-        }
-        let missing_pats = variants_of_enums
-            .into_iter()
-            .multi_cartesian_product()
-            .inspect(|_| cov_mark::hit!(fill_match_arms_lazy_computation))
-            .map(|variants| {
-                let patterns =
-                    variants.into_iter().filter_map(|variant| build_pat(ctx.db(), module, variant));
-                ast::Pat::from(make::tuple_pat(patterns))
-            })
-            .filter(|variant_pat| is_variant_missing(&top_lvl_pats, variant_pat));
-        (Box::new(missing_pats) as Box<dyn Iterator<Item = _>>).peekable()
-    } else {
-        return None;
-    };
-
-    if missing_pats.peek().is_none() {
-        return None;
-    }
-
-    let target = ctx.sema.original_range(match_expr.syntax()).range;
-    acc.add(
-        AssistId("fill_match_arms", AssistKind::QuickFix),
-        "Fill match arms",
-        target,
-        |builder| {
-            let new_match_arm_list = match_arm_list.clone_for_update();
-            let missing_arms = missing_pats
-                .map(|pat| make::match_arm(iter::once(pat), None, make::ext::expr_todo()))
-                .map(|it| it.clone_for_update());
-
-            let catch_all_arm = new_match_arm_list
-                .arms()
-                .find(|arm| matches!(arm.pat(), Some(ast::Pat::WildcardPat(_))));
-            if let Some(arm) = catch_all_arm {
-                let is_empty_expr = arm.expr().map_or(true, |e| match e {
-                    ast::Expr::BlockExpr(b) => {
-                        b.statements().next().is_none() && b.tail_expr().is_none()
-                    }
-                    ast::Expr::TupleExpr(t) => t.fields().next().is_none(),
-                    _ => false,
-                });
-                if is_empty_expr {
-                    arm.remove();
-                } else {
-                    cov_mark::hit!(fill_match_arms_empty_expr);
-                }
-            }
-            let mut first_new_arm = None;
-            for arm in missing_arms {
-                first_new_arm.get_or_insert_with(|| arm.clone());
-                new_match_arm_list.add_arm(arm);
-            }
-
-            let old_range = ctx.sema.original_range(match_arm_list.syntax()).range;
-            match (first_new_arm, ctx.config.snippet_cap) {
-                (Some(first_new_arm), Some(cap)) => {
-                    let extend_lifetime;
-                    let cursor =
-                        match first_new_arm.syntax().descendants().find_map(ast::WildcardPat::cast)
-                        {
-                            Some(it) => {
-                                extend_lifetime = it.syntax().clone();
-                                Cursor::Replace(&extend_lifetime)
-                            }
-                            None => Cursor::Before(first_new_arm.syntax()),
-                        };
-                    let snippet = render_snippet(cap, new_match_arm_list.syntax(), cursor);
-                    builder.replace_snippet(cap, old_range, snippet);
-                }
-                _ => builder.replace(old_range, new_match_arm_list.to_string()),
-            }
-        },
-    )
-}
-
-fn is_variant_missing(existing_pats: &[Pat], var: &Pat) -> bool {
-    !existing_pats.iter().any(|pat| does_pat_match_variant(pat, var))
-}
-
-// Fixme: this is still somewhat limited, use hir_ty::diagnostics::match_check?
-fn does_pat_match_variant(pat: &Pat, var: &Pat) -> bool {
-    match (pat, var) {
-        (Pat::WildcardPat(_), _) => true,
-        (Pat::TuplePat(tpat), Pat::TuplePat(tvar)) => {
-            tpat.fields().zip(tvar.fields()).all(|(p, v)| does_pat_match_variant(&p, &v))
-        }
-        _ => utils::does_pat_match_variant(pat, var),
-    }
-}
-
-#[derive(Eq, PartialEq, Clone, Copy)]
-enum ExtendedEnum {
-    Bool,
-    Enum(hir::Enum),
-}
-
-#[derive(Eq, PartialEq, Clone, Copy)]
-enum ExtendedVariant {
-    True,
-    False,
-    Variant(hir::Variant),
-}
-
-fn lift_enum(e: hir::Enum) -> ExtendedEnum {
-    ExtendedEnum::Enum(e)
-}
-
-impl ExtendedEnum {
-    fn variants(self, db: &RootDatabase) -> Vec<ExtendedVariant> {
-        match self {
-            ExtendedEnum::Enum(e) => {
-                e.variants(db).into_iter().map(ExtendedVariant::Variant).collect::<Vec<_>>()
-            }
-            ExtendedEnum::Bool => {
-                Vec::<ExtendedVariant>::from([ExtendedVariant::True, ExtendedVariant::False])
-            }
-        }
-    }
-}
-
-fn resolve_enum_def(sema: &Semantics<RootDatabase>, expr: &ast::Expr) -> Option<ExtendedEnum> {
-    sema.type_of_expr(expr)?.adjusted().autoderef(sema.db).find_map(|ty| match ty.as_adt() {
-        Some(Adt::Enum(e)) => Some(ExtendedEnum::Enum(e)),
-        _ => ty.is_bool().then(|| ExtendedEnum::Bool),
-    })
-}
-
-fn resolve_tuple_of_enum_def(
-    sema: &Semantics<RootDatabase>,
-    expr: &ast::Expr,
-) -> Option<Vec<ExtendedEnum>> {
-    sema.type_of_expr(expr)?
-        .adjusted()
-        .tuple_fields(sema.db)
-        .iter()
-        .map(|ty| {
-            ty.autoderef(sema.db).find_map(|ty| match ty.as_adt() {
-                Some(Adt::Enum(e)) => Some(lift_enum(e)),
-                // For now we only handle expansion for a tuple of enums. Here
-                // we map non-enum items to None and rely on `collect` to
-                // convert Vec<Option<hir::Enum>> into Option<Vec<hir::Enum>>.
-                _ => ty.is_bool().then(|| ExtendedEnum::Bool),
-            })
-        })
-        .collect()
-}
-
-fn build_pat(db: &RootDatabase, module: hir::Module, var: ExtendedVariant) -> Option<ast::Pat> {
-    match var {
-        ExtendedVariant::Variant(var) => {
-            let path = mod_path_to_ast(&module.find_use_path(db, ModuleDef::from(var))?);
-
-            // FIXME: use HIR for this; it doesn't currently expose struct vs. tuple vs. unit variants though
-            let pat: ast::Pat = match var.source(db)?.value.kind() {
-                ast::StructKind::Tuple(field_list) => {
-                    let pats =
-                        iter::repeat(make::wildcard_pat().into()).take(field_list.fields().count());
-                    make::tuple_struct_pat(path, pats).into()
-                }
-                ast::StructKind::Record(field_list) => {
-                    let pats = field_list
-                        .fields()
-                        .map(|f| make::ext::simple_ident_pat(f.name().unwrap()).into());
-                    make::record_pat(path, pats).into()
-                }
-                ast::StructKind::Unit => make::path_pat(path),
-            };
-
-            Some(pat)
-        }
-        ExtendedVariant::True => Some(ast::Pat::from(make::literal_pat("true"))),
-        ExtendedVariant::False => Some(ast::Pat::from(make::literal_pat("false"))),
-    }
-}
-
-#[cfg(test)]
-mod tests {
-    use crate::tests::{
-        check_assist, check_assist_not_applicable, check_assist_target, check_assist_unresolved,
-    };
-
-    use super::fill_match_arms;
-
-    #[test]
-    fn all_match_arms_provided() {
-        check_assist_not_applicable(
-            fill_match_arms,
-            r#"
-enum A {
-    As,
-    Bs{x:i32, y:Option<i32>},
-    Cs(i32, Option<i32>),
-}
-fn main() {
-    match A::As$0 {
-        A::As,
-        A::Bs{x,y:Some(_)} => {}
-        A::Cs(_, Some(_)) => {}
-    }
-}
-            "#,
-        );
-    }
-
-    #[test]
-    fn all_boolean_match_arms_provided() {
-        check_assist_not_applicable(
-            fill_match_arms,
-            r#"
-fn foo(a: bool) {
-    match a$0 {
-        true => {}
-        false => {}
-    }
-}
-"#,
-        )
-    }
-
-    #[test]
-    fn tuple_of_non_enum() {
-        // for now this case is not handled, although it potentially could be
-        // in the future
-        check_assist_not_applicable(
-            fill_match_arms,
-            r#"
-fn main() {
-    match (0, false)$0 {
-    }
-}
-"#,
-        );
-    }
-
-    #[test]
-    fn fill_match_arms_boolean() {
-        check_assist(
-            fill_match_arms,
-            r#"
-fn foo(a: bool) {
-    match a$0 {
-    }
-}
-"#,
-            r#"
-fn foo(a: bool) {
-    match a {
-        $0true => todo!(),
-        false => todo!(),
-    }
-}
-"#,
-        )
-    }
-
-    #[test]
-    fn partial_fill_boolean() {
-        check_assist(
-            fill_match_arms,
-            r#"
-fn foo(a: bool) {
-    match a$0 {
-        true => {}
-    }
-}
-"#,
-            r#"
-fn foo(a: bool) {
-    match a {
-        true => {}
-        $0false => todo!(),
-    }
-}
-"#,
-        )
-    }
-
-    #[test]
-    fn all_boolean_tuple_arms_provided() {
-        check_assist_not_applicable(
-            fill_match_arms,
-            r#"
-fn foo(a: bool) {
-    match (a, a)$0 {
-        (true, true) => {}
-        (true, false) => {}
-        (false, true) => {}
-        (false, false) => {}
-    }
-}
-"#,
-        )
-    }
-
-    #[test]
-    fn fill_boolean_tuple() {
-        check_assist(
-            fill_match_arms,
-            r#"
-fn foo(a: bool) {
-    match (a, a)$0 {
-    }
-}
-"#,
-            r#"
-fn foo(a: bool) {
-    match (a, a) {
-        $0(true, true) => todo!(),
-        (true, false) => todo!(),
-        (false, true) => todo!(),
-        (false, false) => todo!(),
-    }
-}
-"#,
-        )
-    }
-
-    #[test]
-    fn partial_fill_boolean_tuple() {
-        check_assist(
-            fill_match_arms,
-            r#"
-fn foo(a: bool) {
-    match (a, a)$0 {
-        (false, true) => {}
-    }
-}
-"#,
-            r#"
-fn foo(a: bool) {
-    match (a, a) {
-        (false, true) => {}
-        $0(true, true) => todo!(),
-        (true, false) => todo!(),
-        (false, false) => todo!(),
-    }
-}
-"#,
-        )
-    }
-
-    #[test]
-    fn partial_fill_record_tuple() {
-        check_assist(
-            fill_match_arms,
-            r#"
-enum A {
-    As,
-    Bs { x: i32, y: Option<i32> },
-    Cs(i32, Option<i32>),
-}
-fn main() {
-    match A::As$0 {
-        A::Bs { x, y: Some(_) } => {}
-        A::Cs(_, Some(_)) => {}
-    }
-}
-"#,
-            r#"
-enum A {
-    As,
-    Bs { x: i32, y: Option<i32> },
-    Cs(i32, Option<i32>),
-}
-fn main() {
-    match A::As {
-        A::Bs { x, y: Some(_) } => {}
-        A::Cs(_, Some(_)) => {}
-        $0A::As => todo!(),
-    }
-}
-"#,
-        );
-    }
-
-    #[test]
-    fn partial_fill_option() {
-        check_assist(
-            fill_match_arms,
-            r#"
-//- minicore: option
-fn main() {
-    match None$0 {
-        None => {}
-    }
-}
-"#,
-            r#"
-fn main() {
-    match None {
-        None => {}
-        Some(${0:_}) => todo!(),
-    }
-}
-"#,
-        );
-    }
-
-    #[test]
-    fn partial_fill_or_pat() {
-        check_assist(
-            fill_match_arms,
-            r#"
-enum A { As, Bs, Cs(Option<i32>) }
-fn main() {
-    match A::As$0 {
-        A::Cs(_) | A::Bs => {}
-    }
-}
-"#,
-            r#"
-enum A { As, Bs, Cs(Option<i32>) }
-fn main() {
-    match A::As {
-        A::Cs(_) | A::Bs => {}
-        $0A::As => todo!(),
-    }
-}
-"#,
-        );
-    }
-
-    #[test]
-    fn partial_fill() {
-        check_assist(
-            fill_match_arms,
-            r#"
-enum A { As, Bs, Cs, Ds(String), Es(B) }
-enum B { Xs, Ys }
-fn main() {
-    match A::As$0 {
-        A::Bs if 0 < 1 => {}
-        A::Ds(_value) => { let x = 1; }
-        A::Es(B::Xs) => (),
-    }
-}
-"#,
-            r#"
-enum A { As, Bs, Cs, Ds(String), Es(B) }
-enum B { Xs, Ys }
-fn main() {
-    match A::As {
-        A::Bs if 0 < 1 => {}
-        A::Ds(_value) => { let x = 1; }
-        A::Es(B::Xs) => (),
-        $0A::As => todo!(),
-        A::Cs => todo!(),
-    }
-}
-"#,
-        );
-    }
-
-    #[test]
-    fn partial_fill_bind_pat() {
-        check_assist(
-            fill_match_arms,
-            r#"
-enum A { As, Bs, Cs(Option<i32>) }
-fn main() {
-    match A::As$0 {
-        A::As(_) => {}
-        a @ A::Bs(_) => {}
-    }
-}
-"#,
-            r#"
-enum A { As, Bs, Cs(Option<i32>) }
-fn main() {
-    match A::As {
-        A::As(_) => {}
-        a @ A::Bs(_) => {}
-        A::Cs(${0:_}) => todo!(),
-    }
-}
-"#,
-        );
-    }
-
-    #[test]
-    fn fill_match_arms_empty_body() {
-        check_assist(
-            fill_match_arms,
-            r#"
-enum A { As, Bs, Cs(String), Ds(String, String), Es { x: usize, y: usize } }
-
-fn main() {
-    let a = A::As;
-    match a$0 {}
-}
-"#,
-            r#"
-enum A { As, Bs, Cs(String), Ds(String, String), Es { x: usize, y: usize } }
-
-fn main() {
-    let a = A::As;
-    match a {
-        $0A::As => todo!(),
-        A::Bs => todo!(),
-        A::Cs(_) => todo!(),
-        A::Ds(_, _) => todo!(),
-        A::Es { x, y } => todo!(),
-    }
-}
-"#,
-        );
-    }
-
-    #[test]
-    fn fill_match_arms_tuple_of_enum() {
-        check_assist(
-            fill_match_arms,
-            r#"
-enum A { One, Two }
-enum B { One, Two }
-
-fn main() {
-    let a = A::One;
-    let b = B::One;
-    match (a$0, b) {}
-}
-"#,
-            r#"
-enum A { One, Two }
-enum B { One, Two }
-
-fn main() {
-    let a = A::One;
-    let b = B::One;
-    match (a, b) {
-        $0(A::One, B::One) => todo!(),
-        (A::One, B::Two) => todo!(),
-        (A::Two, B::One) => todo!(),
-        (A::Two, B::Two) => todo!(),
-    }
-}
-"#,
-        );
-    }
-
-    #[test]
-    fn fill_match_arms_tuple_of_enum_ref() {
-        check_assist(
-            fill_match_arms,
-            r#"
-enum A { One, Two }
-enum B { One, Two }
-
-fn main() {
-    let a = A::One;
-    let b = B::One;
-    match (&a$0, &b) {}
-}
-"#,
-            r#"
-enum A { One, Two }
-enum B { One, Two }
-
-fn main() {
-    let a = A::One;
-    let b = B::One;
-    match (&a, &b) {
-        $0(A::One, B::One) => todo!(),
-        (A::One, B::Two) => todo!(),
-        (A::Two, B::One) => todo!(),
-        (A::Two, B::Two) => todo!(),
-    }
-}
-"#,
-        );
-    }
-
-    #[test]
-    fn fill_match_arms_tuple_of_enum_partial() {
-        check_assist(
-            fill_match_arms,
-            r#"
-enum A { One, Two }
-enum B { One, Two }
-
-fn main() {
-    let a = A::One;
-    let b = B::One;
-    match (a$0, b) {
-        (A::Two, B::One) => {}
-    }
-}
-"#,
-            r#"
-enum A { One, Two }
-enum B { One, Two }
-
-fn main() {
-    let a = A::One;
-    let b = B::One;
-    match (a, b) {
-        (A::Two, B::One) => {}
-        $0(A::One, B::One) => todo!(),
-        (A::One, B::Two) => todo!(),
-        (A::Two, B::Two) => todo!(),
-    }
-}
-"#,
-        );
-    }
-
-    #[test]
-    fn fill_match_arms_tuple_of_enum_partial_with_wildcards() {
-        check_assist(
-            fill_match_arms,
-            r#"
-//- minicore: option
-fn main() {
-    let a = Some(1);
-    let b = Some(());
-    match (a$0, b) {
-        (Some(_), _) => {}
-        (None, Some(_)) => {}
-    }
-}
-"#,
-            r#"
-fn main() {
-    let a = Some(1);
-    let b = Some(());
-    match (a, b) {
-        (Some(_), _) => {}
-        (None, Some(_)) => {}
-        $0(None, None) => todo!(),
-    }
-}
-"#,
-        );
-    }
-
-    #[test]
-    fn fill_match_arms_partial_with_deep_pattern() {
-        // Fixme: cannot handle deep patterns
-        check_assist_not_applicable(
-            fill_match_arms,
-            r#"
-//- minicore: option
-fn main() {
-    match $0Some(true) {
-        Some(true) => {}
-        None => {}
-    }
-}
-"#,
-        );
-    }
-
-    #[test]
-    fn fill_match_arms_tuple_of_enum_not_applicable() {
-        check_assist_not_applicable(
-            fill_match_arms,
-            r#"
-enum A { One, Two }
-enum B { One, Two }
-
-fn main() {
-    let a = A::One;
-    let b = B::One;
-    match (a$0, b) {
-        (A::Two, B::One) => {}
-        (A::One, B::One) => {}
-        (A::One, B::Two) => {}
-        (A::Two, B::Two) => {}
-    }
-}
-"#,
-        );
-    }
-
-    #[test]
-    fn fill_match_arms_single_element_tuple_of_enum() {
-        check_assist(
-            fill_match_arms,
-            r#"
-enum A { One, Two }
-
-fn main() {
-    let a = A::One;
-    match (a$0, ) {
-    }
-}
-"#,
-            r#"
-enum A { One, Two }
-
-fn main() {
-    let a = A::One;
-    match (a, ) {
-        $0(A::One,) => todo!(),
-        (A::Two,) => todo!(),
-    }
-}
-"#,
-        );
-    }
-
-    #[test]
-    fn test_fill_match_arm_refs() {
-        check_assist(
-            fill_match_arms,
-            r#"
-enum A { As }
-
-fn foo(a: &A) {
-    match a$0 {
-    }
-}
-"#,
-            r#"
-enum A { As }
-
-fn foo(a: &A) {
-    match a {
-        $0A::As => todo!(),
-    }
-}
-"#,
-        );
-
-        check_assist(
-            fill_match_arms,
-            r#"
-enum A {
-    Es { x: usize, y: usize }
-}
-
-fn foo(a: &mut A) {
-    match a$0 {
-    }
-}
-"#,
-            r#"
-enum A {
-    Es { x: usize, y: usize }
-}
-
-fn foo(a: &mut A) {
-    match a {
-        $0A::Es { x, y } => todo!(),
-    }
-}
-"#,
-        );
-    }
-
-    #[test]
-    fn fill_match_arms_target() {
-        check_assist_target(
-            fill_match_arms,
-            r#"
-enum E { X, Y }
-
-fn main() {
-    match E::X$0 {}
-}
-"#,
-            "match E::X {}",
-        );
-    }
-
-    #[test]
-    fn fill_match_arms_trivial_arm() {
-        check_assist(
-            fill_match_arms,
-            r#"
-enum E { X, Y }
-
-fn main() {
-    match E::X {
-        $0_ => {}
-    }
-}
-"#,
-            r#"
-enum E { X, Y }
-
-fn main() {
-    match E::X {
-        $0E::X => todo!(),
-        E::Y => todo!(),
-    }
-}
-"#,
-        );
-    }
-
-    #[test]
-    fn fill_match_arms_qualifies_path() {
-        check_assist(
-            fill_match_arms,
-            r#"
-mod foo { pub enum E { X, Y } }
-use foo::E::X;
-
-fn main() {
-    match X {
-        $0
-    }
-}
-"#,
-            r#"
-mod foo { pub enum E { X, Y } }
-use foo::E::X;
-
-fn main() {
-    match X {
-        $0X => todo!(),
-        foo::E::Y => todo!(),
-    }
-}
-"#,
-        );
-    }
-
-    #[test]
-    fn fill_match_arms_preserves_comments() {
-        check_assist(
-            fill_match_arms,
-            r#"
-enum A { One, Two }
-fn foo(a: A) {
-    match a {
-        // foo bar baz$0
-        A::One => {}
-        // This is where the rest should be
-    }
-}
-"#,
-            r#"
-enum A { One, Two }
-fn foo(a: A) {
-    match a {
-        // foo bar baz
-        A::One => {}
-        $0A::Two => todo!(),
-        // This is where the rest should be
-    }
-}
-"#,
-        );
-    }
-
-    #[test]
-    fn fill_match_arms_preserves_comments_empty() {
-        check_assist(
-            fill_match_arms,
-            r#"
-enum A { One, Two }
-fn foo(a: A) {
-    match a {
-        // foo bar baz$0
-    }
-}
-"#,
-            r#"
-enum A { One, Two }
-fn foo(a: A) {
-    match a {
-        $0A::One => todo!(),
-        A::Two => todo!(),
-        // foo bar baz
-    }
-}
-"#,
-        );
-    }
-
-    #[test]
-    fn fill_match_arms_placeholder() {
-        check_assist(
-            fill_match_arms,
-            r#"
-enum A { One, Two, }
-fn foo(a: A) {
-    match a$0 {
-        _ => (),
-    }
-}
-"#,
-            r#"
-enum A { One, Two, }
-fn foo(a: A) {
-    match a {
-        $0A::One => todo!(),
-        A::Two => todo!(),
-    }
-}
-"#,
-        );
-    }
-
-    #[test]
-    fn option_order() {
-        cov_mark::check!(option_order);
-        check_assist(
-            fill_match_arms,
-            r#"
-//- minicore: option
-fn foo(opt: Option<i32>) {
-    match opt$0 {
-    }
-}
-"#,
-            r#"
-fn foo(opt: Option<i32>) {
-    match opt {
-        Some(${0:_}) => todo!(),
-        None => todo!(),
-    }
-}
-"#,
-        );
-    }
-
-    #[test]
-    fn works_inside_macro_call() {
-        check_assist(
-            fill_match_arms,
-            r#"
-macro_rules! m { ($expr:expr) => {$expr}}
-enum Test {
-    A,
-    B,
-    C,
-}
-
-fn foo(t: Test) {
-    m!(match t$0 {});
-}"#,
-            r#"
-macro_rules! m { ($expr:expr) => {$expr}}
-enum Test {
-    A,
-    B,
-    C,
-}
-
-fn foo(t: Test) {
-    m!(match t {
-    $0Test::A => todo!(),
-    Test::B => todo!(),
-    Test::C => todo!(),
-});
-}"#,
-        );
-    }
-
-    #[test]
-    fn lazy_computation() {
-        // Computing a single missing arm is enough to determine applicability of the assist.
-        cov_mark::check_count!(fill_match_arms_lazy_computation, 1);
-        check_assist_unresolved(
-            fill_match_arms,
-            r#"
-enum A { One, Two, }
-fn foo(tuple: (A, A)) {
-    match $0tuple {};
-}
-"#,
-        );
-    }
-
-    #[test]
-    fn adds_comma_before_new_arms() {
-        check_assist(
-            fill_match_arms,
-            r#"
-fn foo(t: bool) {
-    match $0t {
-        true => 1 + 2
-    }
-}"#,
-            r#"
-fn foo(t: bool) {
-    match t {
-        true => 1 + 2,
-        $0false => todo!(),
-    }
-}"#,
-        );
-    }
-
-    #[test]
-    fn does_not_add_extra_comma() {
-        check_assist(
-            fill_match_arms,
-            r#"
-fn foo(t: bool) {
-    match $0t {
-        true => 1 + 2,
-    }
-}"#,
-            r#"
-fn foo(t: bool) {
-    match t {
-        true => 1 + 2,
-        $0false => todo!(),
-    }
-}"#,
-        );
-    }
-
-    #[test]
-    fn does_not_remove_catch_all_with_non_empty_expr() {
-        cov_mark::check!(fill_match_arms_empty_expr);
-        check_assist(
-            fill_match_arms,
-            r#"
-fn foo(t: bool) {
-    match $0t {
-        _ => 1 + 2,
-    }
-}"#,
-            r#"
-fn foo(t: bool) {
-    match t {
-        _ => 1 + 2,
-        $0true => todo!(),
-        false => todo!(),
-    }
-}"#,
-        );
-    }
-}
index 8feae78126ff223440558585d3f8ccecadf1a54a..0255e508b4fc4733451d2ebe6950fdf5b6aec042 100644 (file)
@@ -1,5 +1,10 @@
-use hir::{HasSource, HirDisplay, Module, TypeInfo};
-use ide_db::{base_db::FileId, helpers::SnippetCap};
+use hir::{HasSource, HirDisplay, Module, ModuleDef, Semantics, TypeInfo};
+use ide_db::{
+    base_db::FileId,
+    defs::{Definition, NameRefClass},
+    helpers::SnippetCap,
+    RootDatabase,
+};
 use rustc_hash::{FxHashMap, FxHashSet};
 use stdx::to_lower_snake_case;
 use syntax::{
@@ -71,12 +76,13 @@ fn gen_fn(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
     let path_expr: ast::PathExpr = ctx.find_node_at_offset()?;
     let call = path_expr.syntax().parent().and_then(ast::CallExpr::cast)?;
     let path = path_expr.path()?;
-    let fn_name = fn_name(&path)?;
+    let name_ref = path.segment()?.name_ref()?;
     if ctx.sema.resolve_path(&path).is_some() {
         // The function call already resolves, no need to add a function
         return None;
     }
 
+    let fn_name = &*name_ref.text();
     let target_module;
     let mut adt_name = None;
 
@@ -93,7 +99,7 @@ fn gen_fn(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
                 if current_module.krate() != module.krate() {
                     return None;
                 }
-                let (impl_, file) = get_adt_source(ctx, &adt, fn_name.text().as_str())?;
+                let (impl_, file) = get_adt_source(ctx, &adt, fn_name)?;
                 let (target, insert_offset) = get_method_target(ctx, &module, &impl_)?;
                 adt_name = if impl_.is_none() { Some(adt.name(ctx.sema.db)) } else { None };
                 (target, file, insert_offset)
@@ -107,9 +113,9 @@ fn gen_fn(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
             get_fn_target(ctx, &target_module, call.clone())?
         }
     };
-    let function_builder = FunctionBuilder::from_call(ctx, &call, &path, target_module, target)?;
+    let function_builder = FunctionBuilder::from_call(ctx, &call, fn_name, target_module, target)?;
     let text_range = call.syntax().text_range();
-    let label = format!("Generate {} function", function_builder.fn_name.clone());
+    let label = format!("Generate {} function", function_builder.fn_name);
     add_func_to_accumulator(
         acc,
         ctx,
@@ -139,7 +145,7 @@ fn gen_method(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
         FunctionBuilder::from_method_call(ctx, &call, &fn_name, target_module, target)?;
     let text_range = call.syntax().text_range();
     let adt_name = if impl_.is_none() { Some(adt.name(ctx.sema.db)) } else { None };
-    let label = format!("Generate {} method", function_builder.fn_name.clone());
+    let label = format!("Generate {} method", function_builder.fn_name);
     add_func_to_accumulator(
         acc,
         ctx,
@@ -241,13 +247,13 @@ impl FunctionBuilder {
     fn from_call(
         ctx: &AssistContext,
         call: &ast::CallExpr,
-        path: &ast::Path,
+        fn_name: &str,
         target_module: Option<hir::Module>,
         target: GeneratedFunctionTarget,
     ) -> Option<Self> {
         let needs_pub = target_module.is_some();
         let target_module = target_module.or_else(|| current_module(target.syntax(), ctx))?;
-        let fn_name = fn_name(path)?;
+        let fn_name = make::name(fn_name);
         let (type_params, params) = fn_args(ctx, target_module, FuncExpr::Func(call.clone()))?;
 
         let await_expr = call.syntax().parent().and_then(ast::AwaitExpr::cast);
@@ -369,7 +375,7 @@ fn make_return_type(
             }
         }
     };
-    let ret_type = ret_ty.map(|rt| make::ret_type(rt));
+    let ret_type = ret_ty.map(make::ret_type);
     (ret_type, should_focus_return_type)
 }
 
@@ -386,7 +392,7 @@ fn get_fn_target(
             file = in_file;
             target
         }
-        None => next_space_for_fn_after_call_site(FuncExpr::Func(call.clone()))?,
+        None => next_space_for_fn_after_call_site(FuncExpr::Func(call))?,
     };
     Some((target.clone(), file, get_insert_offset(&target)))
 }
@@ -397,7 +403,7 @@ fn get_method_target(
     impl_: &Option<ast::Impl>,
 ) -> Option<(GeneratedFunctionTarget, TextSize)> {
     let target = match impl_ {
-        Some(impl_) => next_space_for_fn_in_impl(&impl_)?,
+        Some(impl_) => next_space_for_fn_in_impl(impl_)?,
         None => {
             next_space_for_fn_in_module(ctx.sema.db, &target_module.definition_source(ctx.sema.db))?
                 .1
@@ -428,11 +434,6 @@ fn syntax(&self) -> &SyntaxNode {
     }
 }
 
-fn fn_name(call: &ast::Path) -> Option<ast::Name> {
-    let name = call.segment()?.syntax().to_string();
-    Some(make::name(&name))
-}
-
 /// Computes the type variables and arguments required for the generated function
 fn fn_args(
     ctx: &AssistContext,
@@ -442,13 +443,10 @@ fn fn_args(
     let mut arg_names = Vec::new();
     let mut arg_types = Vec::new();
     for arg in call.arg_list()?.args() {
-        arg_names.push(match fn_arg_name(&arg) {
-            Some(name) => name,
-            None => String::from("arg"),
-        });
+        arg_names.push(fn_arg_name(&ctx.sema, &arg));
         arg_types.push(match fn_arg_type(ctx, target_module, &arg) {
             Some(ty) => {
-                if ty.len() > 0 && ty.starts_with('&') {
+                if !ty.is_empty() && ty.starts_with('&') {
                     if let Some((new_ty, _)) = useless_type_special_case("", &ty[1..].to_owned()) {
                         new_ty
                     } else {
@@ -510,18 +508,27 @@ fn deduplicate_arg_names(arg_names: &mut Vec<String>) {
     }
 }
 
-fn fn_arg_name(fn_arg: &ast::Expr) -> Option<String> {
-    match fn_arg {
-        ast::Expr::CastExpr(cast_expr) => fn_arg_name(&cast_expr.expr()?),
-        _ => {
-            let s = fn_arg
-                .syntax()
-                .descendants()
-                .filter(|d| ast::NameRef::can_cast(d.kind()))
-                .last()?
-                .to_string();
-            Some(to_lower_snake_case(&s))
+fn fn_arg_name(sema: &Semantics<RootDatabase>, arg_expr: &ast::Expr) -> String {
+    let name = (|| match arg_expr {
+        ast::Expr::CastExpr(cast_expr) => Some(fn_arg_name(sema, &cast_expr.expr()?)),
+        expr => {
+            let name_ref = expr.syntax().descendants().filter_map(ast::NameRef::cast).last()?;
+            if let Some(NameRefClass::Definition(Definition::ModuleDef(
+                ModuleDef::Const(_) | ModuleDef::Static(_),
+            ))) = NameRefClass::classify(sema, &name_ref)
+            {
+                return Some(name_ref.to_string().to_lowercase());
+            };
+            Some(to_lower_snake_case(&name_ref.to_string()))
         }
+    })();
+    match name {
+        Some(mut name) if name.starts_with(|c: char| c.is_ascii_digit()) => {
+            name.insert_str(0, "arg");
+            name
+        }
+        Some(name) => name,
+        None => "arg".to_string(),
     }
 }
 
@@ -1643,6 +1650,119 @@ fn bar() ${0:-> _} {
     todo!()
 }
 }
+",
+        )
+    }
+
+    #[test]
+    fn no_panic_on_invalid_global_path() {
+        check_assist(
+            generate_function,
+            r"
+fn main() {
+    ::foo$0();
+}
+",
+            r"
+fn main() {
+    ::foo();
+}
+
+fn foo() ${0:-> _} {
+    todo!()
+}
+",
+        )
+    }
+
+    #[test]
+    fn handle_tuple_indexing() {
+        check_assist(
+            generate_function,
+            r"
+fn main() {
+    let a = ((),);
+    foo$0(a.0);
+}
+",
+            r"
+fn main() {
+    let a = ((),);
+    foo(a.0);
+}
+
+fn foo(arg0: ()) ${0:-> _} {
+    todo!()
+}
+",
+        )
+    }
+
+    #[test]
+    fn add_function_with_const_arg() {
+        check_assist(
+            generate_function,
+            r"
+const VALUE: usize = 0;
+fn main() {
+    foo$0(VALUE);
+}
+",
+            r"
+const VALUE: usize = 0;
+fn main() {
+    foo(VALUE);
+}
+
+fn foo(value: usize) ${0:-> _} {
+    todo!()
+}
+",
+        )
+    }
+
+    #[test]
+    fn add_function_with_static_arg() {
+        check_assist(
+            generate_function,
+            r"
+static VALUE: usize = 0;
+fn main() {
+    foo$0(VALUE);
+}
+",
+            r"
+static VALUE: usize = 0;
+fn main() {
+    foo(VALUE);
+}
+
+fn foo(value: usize) ${0:-> _} {
+    todo!()
+}
+",
+        )
+    }
+
+    #[test]
+    fn add_function_with_static_mut_arg() {
+        check_assist(
+            generate_function,
+            r"
+static mut VALUE: usize = 0;
+fn main() {
+    foo$0(VALUE);
+}
+",
+            r"
+static mut VALUE: usize = 0;
+fn main() {
+    foo(VALUE);
+}
+
+fn foo(value: usize) ${0:-> _} {
+    todo!()
+}
 ",
         )
     }
diff --git a/crates/ide_assists/src/handlers/infer_function_return_type.rs b/crates/ide_assists/src/handlers/infer_function_return_type.rs
deleted file mode 100644 (file)
index 778ad21..0000000
+++ /dev/null
@@ -1,344 +0,0 @@
-use hir::HirDisplay;
-use syntax::{ast, AstNode, TextRange, TextSize};
-
-use crate::{AssistContext, AssistId, AssistKind, Assists};
-
-// Assist: infer_function_return_type
-//
-// Adds the return type to a function or closure inferred from its tail expression if it doesn't have a return
-// type specified. This assists is useable in a functions or closures tail expression or return type position.
-//
-// ```
-// fn foo() { 4$02i32 }
-// ```
-// ->
-// ```
-// fn foo() -> i32 { 42i32 }
-// ```
-pub(crate) fn infer_function_return_type(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
-    let (fn_type, tail_expr, builder_edit_pos) = extract_tail(ctx)?;
-    let module = ctx.sema.scope(tail_expr.syntax()).module()?;
-    let ty = ctx.sema.type_of_expr(&tail_expr)?.adjusted();
-    if ty.is_unit() {
-        return None;
-    }
-    let ty = ty.display_source_code(ctx.db(), module.into()).ok()?;
-
-    acc.add(
-        AssistId("infer_function_return_type", AssistKind::RefactorRewrite),
-        match fn_type {
-            FnType::Function => "Add this function's return type",
-            FnType::Closure { .. } => "Add this closure's return type",
-        },
-        tail_expr.syntax().text_range(),
-        |builder| {
-            match builder_edit_pos {
-                InsertOrReplace::Insert(insert_pos) => {
-                    builder.insert(insert_pos, &format!("-> {} ", ty))
-                }
-                InsertOrReplace::Replace(text_range) => {
-                    builder.replace(text_range, &format!("-> {}", ty))
-                }
-            }
-            if let FnType::Closure { wrap_expr: true } = fn_type {
-                cov_mark::hit!(wrap_closure_non_block_expr);
-                // `|x| x` becomes `|x| -> T x` which is invalid, so wrap it in a block
-                builder.replace(tail_expr.syntax().text_range(), &format!("{{{}}}", tail_expr));
-            }
-        },
-    )
-}
-
-enum InsertOrReplace {
-    Insert(TextSize),
-    Replace(TextRange),
-}
-
-/// Check the potentially already specified return type and reject it or turn it into a builder command
-/// if allowed.
-fn ret_ty_to_action(ret_ty: Option<ast::RetType>, insert_pos: TextSize) -> Option<InsertOrReplace> {
-    match ret_ty {
-        Some(ret_ty) => match ret_ty.ty() {
-            Some(ast::Type::InferType(_)) | None => {
-                cov_mark::hit!(existing_infer_ret_type);
-                cov_mark::hit!(existing_infer_ret_type_closure);
-                Some(InsertOrReplace::Replace(ret_ty.syntax().text_range()))
-            }
-            _ => {
-                cov_mark::hit!(existing_ret_type);
-                cov_mark::hit!(existing_ret_type_closure);
-                None
-            }
-        },
-        None => Some(InsertOrReplace::Insert(insert_pos + TextSize::from(1))),
-    }
-}
-
-enum FnType {
-    Function,
-    Closure { wrap_expr: bool },
-}
-
-fn extract_tail(ctx: &AssistContext) -> Option<(FnType, ast::Expr, InsertOrReplace)> {
-    let (fn_type, tail_expr, return_type_range, action) =
-        if let Some(closure) = ctx.find_node_at_offset::<ast::ClosureExpr>() {
-            let rpipe_pos = closure.param_list()?.syntax().last_token()?.text_range().end();
-            let action = ret_ty_to_action(closure.ret_type(), rpipe_pos)?;
-
-            let body = closure.body()?;
-            let body_start = body.syntax().first_token()?.text_range().start();
-            let (tail_expr, wrap_expr) = match body {
-                ast::Expr::BlockExpr(block) => (block.tail_expr()?, false),
-                body => (body, true),
-            };
-
-            let ret_range = TextRange::new(rpipe_pos, body_start);
-            (FnType::Closure { wrap_expr }, tail_expr, ret_range, action)
-        } else {
-            let func = ctx.find_node_at_offset::<ast::Fn>()?;
-            let rparen_pos = func.param_list()?.r_paren_token()?.text_range().end();
-            let action = ret_ty_to_action(func.ret_type(), rparen_pos)?;
-
-            let body = func.body()?;
-            let tail_expr = body.tail_expr()?;
-
-            let ret_range_end = body.l_curly_token()?.text_range().start();
-            let ret_range = TextRange::new(rparen_pos, ret_range_end);
-            (FnType::Function, tail_expr, ret_range, action)
-        };
-    let frange = ctx.frange.range;
-    if return_type_range.contains_range(frange) {
-        cov_mark::hit!(cursor_in_ret_position);
-        cov_mark::hit!(cursor_in_ret_position_closure);
-    } else if tail_expr.syntax().text_range().contains_range(frange) {
-        cov_mark::hit!(cursor_on_tail);
-        cov_mark::hit!(cursor_on_tail_closure);
-    } else {
-        return None;
-    }
-    Some((fn_type, tail_expr, action))
-}
-
-#[cfg(test)]
-mod tests {
-    use crate::tests::{check_assist, check_assist_not_applicable};
-
-    use super::*;
-
-    #[test]
-    fn infer_return_type_specified_inferred() {
-        cov_mark::check!(existing_infer_ret_type);
-        check_assist(
-            infer_function_return_type,
-            r#"fn foo() -> $0_ {
-    45
-}"#,
-            r#"fn foo() -> i32 {
-    45
-}"#,
-        );
-    }
-
-    #[test]
-    fn infer_return_type_specified_inferred_closure() {
-        cov_mark::check!(existing_infer_ret_type_closure);
-        check_assist(
-            infer_function_return_type,
-            r#"fn foo() {
-    || -> _ {$045};
-}"#,
-            r#"fn foo() {
-    || -> i32 {45};
-}"#,
-        );
-    }
-
-    #[test]
-    fn infer_return_type_cursor_at_return_type_pos() {
-        cov_mark::check!(cursor_in_ret_position);
-        check_assist(
-            infer_function_return_type,
-            r#"fn foo() $0{
-    45
-}"#,
-            r#"fn foo() -> i32 {
-    45
-}"#,
-        );
-    }
-
-    #[test]
-    fn infer_return_type_cursor_at_return_type_pos_closure() {
-        cov_mark::check!(cursor_in_ret_position_closure);
-        check_assist(
-            infer_function_return_type,
-            r#"fn foo() {
-    || $045
-}"#,
-            r#"fn foo() {
-    || -> i32 {45}
-}"#,
-        );
-    }
-
-    #[test]
-    fn infer_return_type() {
-        cov_mark::check!(cursor_on_tail);
-        check_assist(
-            infer_function_return_type,
-            r#"fn foo() {
-    45$0
-}"#,
-            r#"fn foo() -> i32 {
-    45
-}"#,
-        );
-    }
-
-    #[test]
-    fn infer_return_type_nested() {
-        check_assist(
-            infer_function_return_type,
-            r#"fn foo() {
-    if true {
-        3$0
-    } else {
-        5
-    }
-}"#,
-            r#"fn foo() -> i32 {
-    if true {
-        3
-    } else {
-        5
-    }
-}"#,
-        );
-    }
-
-    #[test]
-    fn not_applicable_ret_type_specified() {
-        cov_mark::check!(existing_ret_type);
-        check_assist_not_applicable(
-            infer_function_return_type,
-            r#"fn foo() -> i32 {
-    ( 45$0 + 32 ) * 123
-}"#,
-        );
-    }
-
-    #[test]
-    fn not_applicable_non_tail_expr() {
-        check_assist_not_applicable(
-            infer_function_return_type,
-            r#"fn foo() {
-    let x = $03;
-    ( 45 + 32 ) * 123
-}"#,
-        );
-    }
-
-    #[test]
-    fn not_applicable_unit_return_type() {
-        check_assist_not_applicable(
-            infer_function_return_type,
-            r#"fn foo() {
-    ($0)
-}"#,
-        );
-    }
-
-    #[test]
-    fn infer_return_type_closure_block() {
-        cov_mark::check!(cursor_on_tail_closure);
-        check_assist(
-            infer_function_return_type,
-            r#"fn foo() {
-    |x: i32| {
-        x$0
-    };
-}"#,
-            r#"fn foo() {
-    |x: i32| -> i32 {
-        x
-    };
-}"#,
-        );
-    }
-
-    #[test]
-    fn infer_return_type_closure() {
-        check_assist(
-            infer_function_return_type,
-            r#"fn foo() {
-    |x: i32| { x$0 };
-}"#,
-            r#"fn foo() {
-    |x: i32| -> i32 { x };
-}"#,
-        );
-    }
-
-    #[test]
-    fn infer_return_type_closure_wrap() {
-        cov_mark::check!(wrap_closure_non_block_expr);
-        check_assist(
-            infer_function_return_type,
-            r#"fn foo() {
-    |x: i32| x$0;
-}"#,
-            r#"fn foo() {
-    |x: i32| -> i32 {x};
-}"#,
-        );
-    }
-
-    #[test]
-    fn infer_return_type_nested_closure() {
-        check_assist(
-            infer_function_return_type,
-            r#"fn foo() {
-    || {
-        if true {
-            3$0
-        } else {
-            5
-        }
-    }
-}"#,
-            r#"fn foo() {
-    || -> i32 {
-        if true {
-            3
-        } else {
-            5
-        }
-    }
-}"#,
-        );
-    }
-
-    #[test]
-    fn not_applicable_ret_type_specified_closure() {
-        cov_mark::check!(existing_ret_type_closure);
-        check_assist_not_applicable(
-            infer_function_return_type,
-            r#"fn foo() {
-    || -> i32 { 3$0 }
-}"#,
-        );
-    }
-
-    #[test]
-    fn not_applicable_non_tail_expr_closure() {
-        check_assist_not_applicable(
-            infer_function_return_type,
-            r#"fn foo() {
-    || -> i32 {
-        let x = 3$0;
-        6
-    }
-}"#,
-        );
-    }
-}
index 9cab0487e152d43ab00b02cc8b02739d952e372e..33e029c236f228af3e539ea478fb2d6566573a65 100644 (file)
@@ -199,7 +199,7 @@ pub(crate) fn inline_(
                             .sema
                             .type_of_expr(&expr)
                             .filter(TypeInfo::has_adjustment)
-                            .and_then(|_| param_ty);
+                            .and(param_ty);
                         body.push_front(
                             make::let_stmt(pat, ty, Some(expr)).clone_for_update().into(),
                         )
index 19e09d80686ca0ac942f5b18c472e3f9057978ba..7e5c7870584e66aff986d56ded3725cf44830516 100644 (file)
@@ -107,6 +107,7 @@ pub(crate) fn inline_local_variable(acc: &mut Assists, ctx: &AssistContext) -> O
                     | ast::Expr::BreakExpr(_)
                     | ast::Expr::ReturnExpr(_)
                     | ast::Expr::MatchExpr(_)
+                    | ast::Expr::BlockExpr(_)
             );
             Some((range, name_ref, !(initializer || parent)))
         })
diff --git a/crates/ide_assists/src/handlers/introduce_named_generic.rs b/crates/ide_assists/src/handlers/introduce_named_generic.rs
new file mode 100644 (file)
index 0000000..636b05d
--- /dev/null
@@ -0,0 +1,144 @@
+use syntax::{
+    ast::{self, edit_in_place::GenericParamsOwnerEdit, make, AstNode},
+    ted,
+};
+
+use crate::{utils::suggest_name, AssistContext, AssistId, AssistKind, Assists};
+
+// Assist: introduce_named_generic
+//
+// Replaces `impl Trait` function argument with the named generic.
+//
+// ```
+// fn foo(bar: $0impl Bar) {}
+// ```
+// ->
+// ```
+// fn foo<B: Bar>(bar: B) {}
+// ```
+pub(crate) fn introduce_named_generic(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
+    let impl_trait_type = ctx.find_node_at_offset::<ast::ImplTraitType>()?;
+    let param = impl_trait_type.syntax().parent().and_then(ast::Param::cast)?;
+    let fn_ = param.syntax().ancestors().find_map(ast::Fn::cast)?;
+
+    let type_bound_list = impl_trait_type.type_bound_list()?;
+
+    let target = fn_.syntax().text_range();
+    acc.add(
+        AssistId("introduce_named_generic", AssistKind::RefactorRewrite),
+        "Replace impl trait with generic",
+        target,
+        |edit| {
+            let impl_trait_type = edit.make_mut(impl_trait_type);
+            let fn_ = edit.make_mut(fn_);
+
+            let type_param_name = suggest_name::for_generic_parameter(&impl_trait_type);
+
+            let type_param = make::type_param(make::name(&type_param_name), Some(type_bound_list))
+                .clone_for_update();
+            let new_ty = make::ty(&type_param_name).clone_for_update();
+
+            ted::replace(impl_trait_type.syntax(), new_ty.syntax());
+            fn_.get_or_create_generic_param_list().add_generic_param(type_param.into())
+        },
+    )
+}
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+
+    use crate::tests::check_assist;
+
+    #[test]
+    fn introduce_named_generic_params() {
+        check_assist(
+            introduce_named_generic,
+            r#"fn foo<G>(bar: $0impl Bar) {}"#,
+            r#"fn foo<G, B: Bar>(bar: B) {}"#,
+        );
+    }
+
+    #[test]
+    fn replace_impl_trait_without_generic_params() {
+        check_assist(
+            introduce_named_generic,
+            r#"fn foo(bar: $0impl Bar) {}"#,
+            r#"fn foo<B: Bar>(bar: B) {}"#,
+        );
+    }
+
+    #[test]
+    fn replace_two_impl_trait_with_generic_params() {
+        check_assist(
+            introduce_named_generic,
+            r#"fn foo<G>(foo: impl Foo, bar: $0impl Bar) {}"#,
+            r#"fn foo<G, B: Bar>(foo: impl Foo, bar: B) {}"#,
+        );
+    }
+
+    #[test]
+    fn replace_impl_trait_with_empty_generic_params() {
+        check_assist(
+            introduce_named_generic,
+            r#"fn foo<>(bar: $0impl Bar) {}"#,
+            r#"fn foo<B: Bar>(bar: B) {}"#,
+        );
+    }
+
+    #[test]
+    fn replace_impl_trait_with_empty_multiline_generic_params() {
+        check_assist(
+            introduce_named_generic,
+            r#"
+fn foo<
+>(bar: $0impl Bar) {}
+"#,
+            r#"
+fn foo<B: Bar
+>(bar: B) {}
+"#,
+        );
+    }
+
+    #[test]
+    fn replace_impl_trait_with_exist_generic_letter() {
+        // FIXME: This is wrong, we should pick a different name if the one we
+        // want is already bound.
+        check_assist(
+            introduce_named_generic,
+            r#"fn foo<B>(bar: $0impl Bar) {}"#,
+            r#"fn foo<B, B: Bar>(bar: B) {}"#,
+        );
+    }
+
+    #[test]
+    fn replace_impl_trait_with_multiline_generic_params() {
+        check_assist(
+            introduce_named_generic,
+            r#"
+fn foo<
+    G: Foo,
+    F,
+    H,
+>(bar: $0impl Bar) {}
+"#,
+            r#"
+fn foo<
+    G: Foo,
+    F,
+    H, B: Bar,
+>(bar: B) {}
+"#,
+        );
+    }
+
+    #[test]
+    fn replace_impl_trait_multiple() {
+        check_assist(
+            introduce_named_generic,
+            r#"fn foo(bar: $0impl Foo + Bar) {}"#,
+            r#"fn foo<F: Foo + Bar>(bar: F) {}"#,
+        );
+    }
+}
index aa32c698a2b0518367a08fc96e371cdd9dacb75d..8077f73d1ea36f019b334c185433debfcfe63626 100644 (file)
@@ -77,7 +77,7 @@ fn generate_fn_def_assist(
             })
             .collect();
         match fn_params_without_lifetime.len() {
-            1 => Some(fn_params_without_lifetime.into_iter().nth(0)?),
+            1 => Some(fn_params_without_lifetime.into_iter().next()?),
             0 => None,
             // multiple unnnamed is invalid. assist is not applicable
             _ => return None,
@@ -93,8 +93,9 @@ fn generate_fn_def_assist(
             make::lifetime_param(new_lifetime_param.clone()).clone_for_update().into(),
         );
         ted::replace(lifetime.syntax(), new_lifetime_param.clone_for_update().syntax());
-        loc_needing_lifetime
-            .map(|position| ted::insert(position, new_lifetime_param.clone_for_update().syntax()));
+        if let Some(position) = loc_needing_lifetime {
+            ted::insert(position, new_lifetime_param.clone_for_update().syntax());
+        }
     })
 }
 
index 6841f506aa4cce12f8e6683f77d9a1b70f6a1329..d7e7c363af622513592bd6a7043acc902524eb0e 100644 (file)
 pub(crate) fn move_guard_to_arm_body(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
     let match_arm = ctx.find_node_at_offset::<MatchArm>()?;
     let guard = match_arm.guard()?;
+    if ctx.offset() > guard.syntax().text_range().end() {
+        cov_mark::hit!(move_guard_unapplicable_in_arm_body);
+        return None;
+    }
     let space_before_guard = guard.syntax().prev_sibling_or_token();
 
     // FIXME: support `if let` guards too
@@ -155,6 +159,21 @@ mod tests {
 
     use crate::tests::{check_assist, check_assist_not_applicable, check_assist_target};
 
+    #[test]
+    fn move_guard_to_arm_body_range() {
+        cov_mark::check!(move_guard_unapplicable_in_arm_body);
+        check_assist_not_applicable(
+            move_guard_to_arm_body,
+            r#"
+fn main() {
+    match 92 {
+        x if x > 10 => $0false,
+        _ => true
+    }
+}
+"#,
+        );
+    }
     #[test]
     fn move_guard_to_arm_body_target() {
         check_assist_target(
index d98a55ae4abf6a3ddf80377c1eea91418946c974..acd0829570c3a8081c3eec4ab1b3e1e0181516e7 100644 (file)
@@ -35,7 +35,7 @@ pub(crate) fn make_raw_string(acc: &mut Assists, ctx: &AssistContext) -> Option<
             if matches!(value, Cow::Borrowed(_)) {
                 // Avoid replacing the whole string to better position the cursor.
                 edit.insert(token.syntax().text_range().start(), format!("r{}", hashes));
-                edit.insert(token.syntax().text_range().end(), format!("{}", hashes));
+                edit.insert(token.syntax().text_range().end(), hashes);
             } else {
                 edit.replace(
                     token.syntax().text_range(),
index 9aa33e39bae2f473723ebb92aba3205f22b53ef0..75b9f827d7ae7de1e9d722e3bea208e6a10bffe3 100644 (file)
@@ -137,7 +137,7 @@ fn process_usage(
         return Some(range_to_remove(arg.syntax()));
     }
 
-    return None;
+    None
 }
 
 fn range_to_remove(node: &SyntaxNode) -> TextRange {
diff --git a/crates/ide_assists/src/handlers/replace_for_loop_with_for_each.rs b/crates/ide_assists/src/handlers/replace_for_loop_with_for_each.rs
deleted file mode 100644 (file)
index fcf7973..0000000
+++ /dev/null
@@ -1,372 +0,0 @@
-use ast::LoopBodyOwner;
-use hir::known;
-use ide_db::helpers::FamousDefs;
-use stdx::format_to;
-use syntax::{ast, AstNode};
-
-use crate::{AssistContext, AssistId, AssistKind, Assists};
-
-// Assist: replace_for_loop_with_for_each
-//
-// Converts a for loop into a for_each loop on the Iterator.
-//
-// ```
-// fn main() {
-//     let x = vec![1, 2, 3];
-//     for$0 v in x {
-//         let y = v * 2;
-//     }
-// }
-// ```
-// ->
-// ```
-// fn main() {
-//     let x = vec![1, 2, 3];
-//     x.into_iter().for_each(|v| {
-//         let y = v * 2;
-//     });
-// }
-// ```
-pub(crate) fn replace_for_loop_with_for_each(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
-    let for_loop = ctx.find_node_at_offset::<ast::ForExpr>()?;
-    let iterable = for_loop.iterable()?;
-    let pat = for_loop.pat()?;
-    let body = for_loop.loop_body()?;
-    if body.syntax().text_range().start() < ctx.offset() {
-        cov_mark::hit!(not_available_in_body);
-        return None;
-    }
-
-    acc.add(
-        AssistId("replace_for_loop_with_for_each", AssistKind::RefactorRewrite),
-        "Replace this for loop with `Iterator::for_each`",
-        for_loop.syntax().text_range(),
-        |builder| {
-            let mut buf = String::new();
-
-            if let Some((expr_behind_ref, method)) =
-                is_ref_and_impls_iter_method(&ctx.sema, &iterable)
-            {
-                // We have either "for x in &col" and col implements a method called iter
-                //             or "for x in &mut col" and col implements a method called iter_mut
-                format_to!(buf, "{}.{}()", expr_behind_ref, method);
-            } else if let ast::Expr::RangeExpr(..) = iterable {
-                // range expressions need to be parenthesized for the syntax to be correct
-                format_to!(buf, "({})", iterable);
-            } else if impls_core_iter(&ctx.sema, &iterable) {
-                format_to!(buf, "{}", iterable);
-            } else if let ast::Expr::RefExpr(_) = iterable {
-                format_to!(buf, "({}).into_iter()", iterable);
-            } else {
-                format_to!(buf, "{}.into_iter()", iterable);
-            }
-
-            format_to!(buf, ".for_each(|{}| {});", pat, body);
-
-            builder.replace(for_loop.syntax().text_range(), buf)
-        },
-    )
-}
-
-/// If iterable is a reference where the expression behind the reference implements a method
-/// returning an Iterator called iter or iter_mut (depending on the type of reference) then return
-/// the expression behind the reference and the method name
-fn is_ref_and_impls_iter_method(
-    sema: &hir::Semantics<ide_db::RootDatabase>,
-    iterable: &ast::Expr,
-) -> Option<(ast::Expr, hir::Name)> {
-    let ref_expr = match iterable {
-        ast::Expr::RefExpr(r) => r,
-        _ => return None,
-    };
-    let wanted_method = if ref_expr.mut_token().is_some() { known::iter_mut } else { known::iter };
-    let expr_behind_ref = ref_expr.expr()?;
-    let ty = sema.type_of_expr(&expr_behind_ref)?.adjusted();
-    let scope = sema.scope(iterable.syntax());
-    let krate = scope.module()?.krate();
-    let traits_in_scope = scope.traits_in_scope();
-    let iter_trait = FamousDefs(sema, Some(krate)).core_iter_Iterator()?;
-
-    let has_wanted_method = ty
-        .iterate_method_candidates(
-            sema.db,
-            krate,
-            &traits_in_scope,
-            Some(&wanted_method),
-            |_, func| {
-                if func.ret_type(sema.db).impls_trait(sema.db, iter_trait, &[]) {
-                    return Some(());
-                }
-                None
-            },
-        )
-        .is_some();
-    if !has_wanted_method {
-        return None;
-    }
-
-    Some((expr_behind_ref, wanted_method))
-}
-
-/// Whether iterable implements core::Iterator
-fn impls_core_iter(sema: &hir::Semantics<ide_db::RootDatabase>, iterable: &ast::Expr) -> bool {
-    let it_typ = match sema.type_of_expr(iterable) {
-        Some(it) => it.adjusted(),
-        None => return false,
-    };
-
-    let module = match sema.scope(iterable.syntax()).module() {
-        Some(it) => it,
-        None => return false,
-    };
-
-    let krate = module.krate();
-    match FamousDefs(sema, Some(krate)).core_iter_Iterator() {
-        Some(iter_trait) => {
-            cov_mark::hit!(test_already_impls_iterator);
-            it_typ.impls_trait(sema.db, iter_trait, &[])
-        }
-        None => false,
-    }
-}
-
-#[cfg(test)]
-mod tests {
-    use crate::tests::{check_assist, check_assist_not_applicable};
-
-    use super::*;
-
-    #[test]
-    fn test_not_for() {
-        check_assist_not_applicable(
-            replace_for_loop_with_for_each,
-            r"
-let mut x = vec![1, 2, 3];
-x.iter_mut().$0for_each(|v| *v *= 2);
-        ",
-        )
-    }
-
-    #[test]
-    fn test_simple_for() {
-        check_assist(
-            replace_for_loop_with_for_each,
-            r"
-fn main() {
-    let x = vec![1, 2, 3];
-    for $0v in x {
-        v *= 2;
-    }
-}",
-            r"
-fn main() {
-    let x = vec![1, 2, 3];
-    x.into_iter().for_each(|v| {
-        v *= 2;
-    });
-}",
-        )
-    }
-
-    #[test]
-    fn test_for_in_range() {
-        check_assist(
-            replace_for_loop_with_for_each,
-            r#"
-//- minicore: range, iterators
-impl<T> core::iter::Iterator for core::ops::Range<T> {
-    type Item = T;
-
-    fn next(&mut self) -> Option<Self::Item> {
-        None
-    }
-}
-
-fn main() {
-    for $0x in 0..92 {
-        print!("{}", x);
-    }
-}"#,
-            r#"
-impl<T> core::iter::Iterator for core::ops::Range<T> {
-    type Item = T;
-
-    fn next(&mut self) -> Option<Self::Item> {
-        None
-    }
-}
-
-fn main() {
-    (0..92).for_each(|x| {
-        print!("{}", x);
-    });
-}"#,
-        )
-    }
-
-    #[test]
-    fn not_available_in_body() {
-        cov_mark::check!(not_available_in_body);
-        check_assist_not_applicable(
-            replace_for_loop_with_for_each,
-            r"
-fn main() {
-    let x = vec![1, 2, 3];
-    for v in x {
-        $0v *= 2;
-    }
-}",
-        )
-    }
-
-    #[test]
-    fn test_for_borrowed() {
-        check_assist(
-            replace_for_loop_with_for_each,
-            r#"
-//- minicore: iterators
-use core::iter::{Repeat, repeat};
-
-struct S;
-impl S {
-    fn iter(&self) -> Repeat<i32> { repeat(92) }
-    fn iter_mut(&mut self) -> Repeat<i32> { repeat(92) }
-}
-
-fn main() {
-    let x = S;
-    for $0v in &x {
-        let a = v * 2;
-    }
-}
-"#,
-            r#"
-use core::iter::{Repeat, repeat};
-
-struct S;
-impl S {
-    fn iter(&self) -> Repeat<i32> { repeat(92) }
-    fn iter_mut(&mut self) -> Repeat<i32> { repeat(92) }
-}
-
-fn main() {
-    let x = S;
-    x.iter().for_each(|v| {
-        let a = v * 2;
-    });
-}
-"#,
-        )
-    }
-
-    #[test]
-    fn test_for_borrowed_no_iter_method() {
-        check_assist(
-            replace_for_loop_with_for_each,
-            r"
-struct NoIterMethod;
-fn main() {
-    let x = NoIterMethod;
-    for $0v in &x {
-        let a = v * 2;
-    }
-}
-",
-            r"
-struct NoIterMethod;
-fn main() {
-    let x = NoIterMethod;
-    (&x).into_iter().for_each(|v| {
-        let a = v * 2;
-    });
-}
-",
-        )
-    }
-
-    #[test]
-    fn test_for_borrowed_mut() {
-        check_assist(
-            replace_for_loop_with_for_each,
-            r#"
-//- minicore: iterators
-use core::iter::{Repeat, repeat};
-
-struct S;
-impl S {
-    fn iter(&self) -> Repeat<i32> { repeat(92) }
-    fn iter_mut(&mut self) -> Repeat<i32> { repeat(92) }
-}
-
-fn main() {
-    let x = S;
-    for $0v in &mut x {
-        let a = v * 2;
-    }
-}
-"#,
-            r#"
-use core::iter::{Repeat, repeat};
-
-struct S;
-impl S {
-    fn iter(&self) -> Repeat<i32> { repeat(92) }
-    fn iter_mut(&mut self) -> Repeat<i32> { repeat(92) }
-}
-
-fn main() {
-    let x = S;
-    x.iter_mut().for_each(|v| {
-        let a = v * 2;
-    });
-}
-"#,
-        )
-    }
-
-    #[test]
-    fn test_for_borrowed_mut_behind_var() {
-        check_assist(
-            replace_for_loop_with_for_each,
-            r"
-fn main() {
-    let x = vec![1, 2, 3];
-    let y = &mut x;
-    for $0v in y {
-        *v *= 2;
-    }
-}",
-            r"
-fn main() {
-    let x = vec![1, 2, 3];
-    let y = &mut x;
-    y.into_iter().for_each(|v| {
-        *v *= 2;
-    });
-}",
-        )
-    }
-
-    #[test]
-    fn test_already_impls_iterator() {
-        cov_mark::check!(test_already_impls_iterator);
-        check_assist(
-            replace_for_loop_with_for_each,
-            r#"
-//- minicore: iterators
-fn main() {
-    for$0 a in core::iter::repeat(92).take(1) {
-        println!("{}", a);
-    }
-}
-"#,
-            r#"
-fn main() {
-    core::iter::repeat(92).take(1).for_each(|a| {
-        println!("{}", a);
-    });
-}
-"#,
-        );
-    }
-}
index 5ce2ad3193e6723713b0577624b645205eb3eaa2..18736533068124e2f6f47cea887f436fb4857455 100644 (file)
@@ -8,7 +8,7 @@
         edit::{AstNodeEdit, IndentLevel},
         make, NameOwner,
     },
-    AstNode,
+    AstNode, TextRange,
 };
 
 use crate::{
 // ```
 pub(crate) fn replace_if_let_with_match(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
     let if_expr: ast::IfExpr = ctx.find_node_at_offset()?;
+    let available_range = TextRange::new(
+        if_expr.syntax().text_range().start(),
+        if_expr.then_branch()?.syntax().text_range().start(),
+    );
+    let cursor_in_range = available_range.contains_range(ctx.frange.range);
+    if !cursor_in_range {
+        return None;
+    }
     let mut else_block = None;
     let if_exprs = successors(Some(if_expr.clone()), |expr| match expr.else_branch()? {
         ast::ElseBranch::IfExpr(expr) => Some(expr),
@@ -79,11 +87,10 @@ pub(crate) fn replace_if_let_with_match(acc: &mut Assists, ctx: &AssistContext)
         return None;
     }
 
-    let target = if_expr.syntax().text_range();
     acc.add(
         AssistId("replace_if_let_with_match", AssistKind::RefactorRewrite),
         "Replace if let with match",
-        target,
+        available_range,
         move |edit| {
             let match_expr = {
                 let else_arm = make_else_arm(ctx, else_block, &cond_bodies);
@@ -126,7 +133,7 @@ fn make_else_arm(
     if let Some(else_block) = else_block {
         let pattern = if let [(Either::Left(pat), _)] = conditionals {
             ctx.sema
-                .type_of_pat(&pat)
+                .type_of_pat(pat)
                 .and_then(|ty| TryEnum::from_ty(&ctx.sema, &ty.adjusted()))
                 .zip(Some(pat))
         } else {
@@ -134,7 +141,7 @@ fn make_else_arm(
         };
         let pattern = match pattern {
             Some((it, pat)) => {
-                if does_pat_match_variant(&pat, &it.sad_pattern()) {
+                if does_pat_match_variant(pat, &it.sad_pattern()) {
                     it.happy_pattern()
                 } else {
                     it.sad_pattern()
@@ -144,7 +151,7 @@ fn make_else_arm(
         };
         make::match_arm(iter::once(pattern), None, unwrap_trivial_block(else_block))
     } else {
-        make::match_arm(iter::once(make::wildcard_pat().into()), None, make::expr_unit().into())
+        make::match_arm(iter::once(make::wildcard_pat().into()), None, make::expr_unit())
     }
 }
 
@@ -257,7 +264,7 @@ fn is_empty_expr(expr: &ast::Expr) -> bool {
 }
 
 fn binds_name(sema: &hir::Semantics<RootDatabase>, pat: &ast::Pat) -> bool {
-    let binds_name_v = |pat| binds_name(&sema, &pat);
+    let binds_name_v = |pat| binds_name(sema, &pat);
     match pat {
         ast::Pat::IdentPat(pat) => !matches!(
             pat.name().and_then(|name| NameClass::classify(sema, &name)),
@@ -330,6 +337,38 @@ pub fn foo(&self) {
         )
     }
 
+    #[test]
+    fn test_if_let_with_match_available_range_left() {
+        check_assist_not_applicable(
+            replace_if_let_with_match,
+            r#"
+impl VariantData {
+    pub fn foo(&self) {
+        $0 if let VariantData::Struct(..) = *self {
+            self.foo();
+        }
+    }
+}
+"#,
+        )
+    }
+
+    #[test]
+    fn test_if_let_with_match_available_range_right() {
+        check_assist_not_applicable(
+            replace_if_let_with_match,
+            r#"
+impl VariantData {
+    pub fn foo(&self) {
+        if let VariantData::Struct(..) = *self {$0
+            self.foo();
+        }
+    }
+}
+"#,
+        )
+    }
+
     #[test]
     fn test_if_let_with_match_basic() {
         check_assist(
diff --git a/crates/ide_assists/src/handlers/replace_impl_trait_with_generic.rs b/crates/ide_assists/src/handlers/replace_impl_trait_with_generic.rs
deleted file mode 100644 (file)
index a2af203..0000000
+++ /dev/null
@@ -1,147 +0,0 @@
-use syntax::{
-    ast::{self, edit_in_place::GenericParamsOwnerEdit, make, AstNode},
-    ted,
-};
-
-use crate::{utils::suggest_name, AssistContext, AssistId, AssistKind, Assists};
-
-// Assist: replace_impl_trait_with_generic
-//
-// Replaces `impl Trait` function argument with the named generic.
-//
-// ```
-// fn foo(bar: $0impl Bar) {}
-// ```
-// ->
-// ```
-// fn foo<B: Bar>(bar: B) {}
-// ```
-pub(crate) fn replace_impl_trait_with_generic(
-    acc: &mut Assists,
-    ctx: &AssistContext,
-) -> Option<()> {
-    let impl_trait_type = ctx.find_node_at_offset::<ast::ImplTraitType>()?;
-    let param = impl_trait_type.syntax().parent().and_then(ast::Param::cast)?;
-    let fn_ = param.syntax().ancestors().find_map(ast::Fn::cast)?;
-
-    let type_bound_list = impl_trait_type.type_bound_list()?;
-
-    let target = fn_.syntax().text_range();
-    acc.add(
-        AssistId("replace_impl_trait_with_generic", AssistKind::RefactorRewrite),
-        "Replace impl trait with generic",
-        target,
-        |edit| {
-            let impl_trait_type = edit.make_mut(impl_trait_type);
-            let fn_ = edit.make_mut(fn_);
-
-            let type_param_name = suggest_name::for_generic_parameter(&impl_trait_type);
-
-            let type_param = make::type_param(make::name(&type_param_name), Some(type_bound_list))
-                .clone_for_update();
-            let new_ty = make::ty(&type_param_name).clone_for_update();
-
-            ted::replace(impl_trait_type.syntax(), new_ty.syntax());
-            fn_.get_or_create_generic_param_list().add_generic_param(type_param.into())
-        },
-    )
-}
-
-#[cfg(test)]
-mod tests {
-    use super::*;
-
-    use crate::tests::check_assist;
-
-    #[test]
-    fn replace_impl_trait_with_generic_params() {
-        check_assist(
-            replace_impl_trait_with_generic,
-            r#"fn foo<G>(bar: $0impl Bar) {}"#,
-            r#"fn foo<G, B: Bar>(bar: B) {}"#,
-        );
-    }
-
-    #[test]
-    fn replace_impl_trait_without_generic_params() {
-        check_assist(
-            replace_impl_trait_with_generic,
-            r#"fn foo(bar: $0impl Bar) {}"#,
-            r#"fn foo<B: Bar>(bar: B) {}"#,
-        );
-    }
-
-    #[test]
-    fn replace_two_impl_trait_with_generic_params() {
-        check_assist(
-            replace_impl_trait_with_generic,
-            r#"fn foo<G>(foo: impl Foo, bar: $0impl Bar) {}"#,
-            r#"fn foo<G, B: Bar>(foo: impl Foo, bar: B) {}"#,
-        );
-    }
-
-    #[test]
-    fn replace_impl_trait_with_empty_generic_params() {
-        check_assist(
-            replace_impl_trait_with_generic,
-            r#"fn foo<>(bar: $0impl Bar) {}"#,
-            r#"fn foo<B: Bar>(bar: B) {}"#,
-        );
-    }
-
-    #[test]
-    fn replace_impl_trait_with_empty_multiline_generic_params() {
-        check_assist(
-            replace_impl_trait_with_generic,
-            r#"
-fn foo<
->(bar: $0impl Bar) {}
-"#,
-            r#"
-fn foo<B: Bar
->(bar: B) {}
-"#,
-        );
-    }
-
-    #[test]
-    fn replace_impl_trait_with_exist_generic_letter() {
-        // FIXME: This is wrong, we should pick a different name if the one we
-        // want is already bound.
-        check_assist(
-            replace_impl_trait_with_generic,
-            r#"fn foo<B>(bar: $0impl Bar) {}"#,
-            r#"fn foo<B, B: Bar>(bar: B) {}"#,
-        );
-    }
-
-    #[test]
-    fn replace_impl_trait_with_multiline_generic_params() {
-        check_assist(
-            replace_impl_trait_with_generic,
-            r#"
-fn foo<
-    G: Foo,
-    F,
-    H,
->(bar: $0impl Bar) {}
-"#,
-            r#"
-fn foo<
-    G: Foo,
-    F,
-    H, B: Bar,
->(bar: B) {}
-"#,
-        );
-    }
-
-    #[test]
-    fn replace_impl_trait_multiple() {
-        check_assist(
-            replace_impl_trait_with_generic,
-            r#"fn foo(bar: $0impl Foo + Bar) {}"#,
-            r#"fn foo<F: Foo + Bar>(bar: F) {}"#,
-        );
-    }
-}
index 13367df807f33e6f732aec1640f4c81b1f3b1f32..47600be763801eca39909d66affb5307d072dd0d 100644 (file)
@@ -141,10 +141,7 @@ fn path_eq_no_generics(lhs: ast::Path, rhs: ast::Path) -> bool {
                     && lhs
                         .name_ref()
                         .zip(rhs.name_ref())
-                        .map_or(false, |(lhs, rhs)| lhs.text() == rhs.text()) =>
-            {
-                ()
-            }
+                        .map_or(false, |(lhs, rhs)| lhs.text() == rhs.text()) => {}
             _ => return false,
         }
 
index 33e12a7d0ca0d35c3b6661b882cecdca767c7c0d..4da6089cacc1e5b087677a72b55217c9da995a88 100644 (file)
@@ -33,7 +33,7 @@ pub(crate) fn toggle_ignore(acc: &mut Assists, ctx: &AssistContext) -> Option<()
             AssistId("toggle_ignore", AssistKind::None),
             "Ignore this test",
             attr.syntax().text_range(),
-            |builder| builder.insert(attr.syntax().text_range().end(), &format!("\n#[ignore]")),
+            |builder| builder.insert(attr.syntax().text_range().end(), "\n#[ignore]"),
         ),
         Some(ignore_attr) => acc.add(
             AssistId("toggle_ignore", AssistKind::None),
index 7c074a4f63210a7ecc4729c69d3593f8a4429861..b2c8cd76941b405e34507471edbec72360ebd068 100644 (file)
@@ -1,10 +1,63 @@
-//! `assists` crate provides a bunch of code assists, also known as code
-//! actions (in LSP) or intentions (in IntelliJ).
+//! `assists` crate provides a bunch of code assists, also known as code actions
+//! (in LSP) or intentions (in IntelliJ).
 //!
 //! An assist is a micro-refactoring, which is automatically activated in
 //! certain context. For example, if the cursor is over `,`, a "swap `,`" assist
 //! becomes available.
-
+//!
+//! ## Assists Guidelines
+//!
+//! Assists are the main mechanism to deliver advanced IDE features to the user,
+//! so we should pay extra attention to the UX.
+//!
+//! The power of assists comes from their context-awareness. The main problem
+//! with IDE features is that there are a lot of them, and it's hard to teach
+//! the user what's available. Assists solve this problem nicely: 💡 signifies
+//! that *something* is possible, and clicking on it reveals a *short* list of
+//! actions. Contrast it with Emacs `M-x`, which just spits an infinite list of
+//! all the features.
+//!
+//! Here are some considerations when creating a new assist:
+//!
+//! * It's good to preserve semantics, and it's good to keep the code compiling,
+//!   but it isn't necessary. Example: "flip binary operation" might change
+//!   semantics.
+//! * Assist shouldn't necessary make the code "better". A lot of assist come in
+//!   pairs: "if let <-> match".
+//! * Assists should have as narrow scope as possible. Each new assists greatly
+//!   improves UX for cases where the user actually invokes it, but it makes UX
+//!   worse for every case where the user clicks 💡 to invoke some *other*
+//!   assist. So, a rarely useful assist which is always applicable can be a net
+//!   negative.
+//! * Rarely useful actions are tricky. Sometimes there are features which are
+//!   clearly useful to some users, but are just noise most of the time. We
+//!   don't have a good solution here, our current approach is to make this
+//!   functionality available only if assist is applicable to the whole
+//!   selection. Example: `sort_items` sorts items alphabetically. Naively, it
+//!   should be available more or less everywhere, which isn't useful. So
+//!   instead we only show it if the user *selects* the items they want to sort.
+//! * Consider grouping related assists together (see [`Assists::add_group`]).
+//! * Make assists robust. If the assist depends on results of type-inference to
+//!   much, it might only fire in fully-correct code. This makes assist less
+//!   useful and (worse) less predictable. The user should have a clear
+//!   intuition when each particular assist is available.
+//! * Make small assists, which compose. Example: rather than auto-importing
+//!   enums in `fill_match_arms`, we use fully-qualified names. There's a
+//!   separate assist to shorten a fully-qualified name.
+//! * Distinguish between assists and fixits for diagnostics. Internally, fixits
+//!   and assists are equivalent. They have the same "show a list + invoke a
+//!   single element" workflow, and both use [`Assist`] data structure. The main
+//!   difference is in the UX: while 💡 looks only at the cursor position,
+//!   diagnostics squigglies and fixits are calculated for the whole file and
+//!   are presented to the user eagerly. So, diagnostics should be fixable
+//!   errors, while assists can be just suggestions for an alternative way to do
+//!   something. If something *could* be a diagnostic, it should be a
+//!   diagnostic. Conversely, it might be valuable to turn a diagnostic with a
+//!   lot of false errors into an assist.
+//! *
+//!
+//! See also this post:
+//! <https://rust-analyzer.github.io/blog/2020/09/28/how-to-make-a-light-bulb.html>
 #[allow(unused)]
 macro_rules! eprintln {
     ($($tt:tt)*) => { stdx::eprintln!($($tt)*) };
@@ -28,6 +81,9 @@ macro_rules! eprintln {
 };
 
 /// Return all the assists applicable at the given position.
+///
+// NOTE: We don't have a `Feature: ` section for assists, they are special-cased
+// in the manual.
 pub fn assists(
     db: &RootDatabase,
     config: &AssistConfig,
@@ -62,13 +118,14 @@ mod handlers {
     mod convert_iter_for_each_to_for;
     mod convert_tuple_struct_to_named_struct;
     mod convert_to_guarded_return;
+    mod convert_while_to_loop;
     mod destructure_tuple_binding;
     mod expand_glob_import;
     mod extract_function;
     mod extract_struct_from_enum_variant;
     mod extract_type_alias;
     mod extract_variable;
-    mod fill_match_arms;
+    mod add_missing_match_arms;
     mod fix_visibility;
     mod flip_binexpr;
     mod flip_comma;
@@ -86,7 +143,7 @@ mod handlers {
     mod generate_is_empty_from_len;
     mod generate_new;
     mod generate_setter;
-    mod infer_function_return_type;
+    mod add_return_type;
     mod inline_call;
     mod inline_local_variable;
     mod introduce_named_lifetime;
@@ -105,9 +162,8 @@ mod handlers {
     mod reorder_fields;
     mod reorder_impl;
     mod replace_derive_with_manual_impl;
-    mod replace_for_loop_with_for_each;
     mod replace_if_let_with_match;
-    mod replace_impl_trait_with_generic;
+    mod introduce_named_generic;
     mod replace_let_with_if_let;
     mod replace_qualified_name_with_use;
     mod replace_string_with_char;
@@ -122,7 +178,9 @@ pub(crate) fn all() -> &'static [Handler] {
         &[
             // These are alphabetic for the foolish consistency
             add_explicit_type::add_explicit_type,
+            add_missing_match_arms::add_missing_match_arms,
             add_lifetime_to_type::add_lifetime_to_type,
+            add_return_type::add_return_type,
             add_turbo_fish::add_turbo_fish,
             apply_demorgan::apply_demorgan,
             auto_import::auto_import,
@@ -133,13 +191,14 @@ pub(crate) fn all() -> &'static [Handler] {
             convert_integer_literal::convert_integer_literal,
             convert_into_to_from::convert_into_to_from,
             convert_iter_for_each_to_for::convert_iter_for_each_to_for,
+            convert_iter_for_each_to_for::convert_for_loop_with_for_each,
             convert_to_guarded_return::convert_to_guarded_return,
             convert_tuple_struct_to_named_struct::convert_tuple_struct_to_named_struct,
+            convert_while_to_loop::convert_while_to_loop,
             destructure_tuple_binding::destructure_tuple_binding,
             expand_glob_import::expand_glob_import,
             extract_struct_from_enum_variant::extract_struct_from_enum_variant,
             extract_type_alias::extract_type_alias,
-            fill_match_arms::fill_match_arms,
             fix_visibility::fix_visibility,
             flip_binexpr::flip_binexpr,
             flip_comma::flip_comma,
@@ -156,9 +215,9 @@ pub(crate) fn all() -> &'static [Handler] {
             generate_impl::generate_impl,
             generate_is_empty_from_len::generate_is_empty_from_len,
             generate_new::generate_new,
-            infer_function_return_type::infer_function_return_type,
             inline_call::inline_call,
             inline_local_variable::inline_local_variable,
+            introduce_named_generic::introduce_named_generic,
             introduce_named_lifetime::introduce_named_lifetime,
             invert_if::invert_if,
             merge_imports::merge_imports,
@@ -178,10 +237,8 @@ pub(crate) fn all() -> &'static [Handler] {
             reorder_fields::reorder_fields,
             reorder_impl::reorder_impl,
             replace_derive_with_manual_impl::replace_derive_with_manual_impl,
-            replace_for_loop_with_for_each::replace_for_loop_with_for_each,
             replace_if_let_with_match::replace_if_let_with_match,
             replace_if_let_with_match::replace_match_with_if_let,
-            replace_impl_trait_with_generic::replace_impl_trait_with_generic,
             replace_let_with_if_let::replace_let_with_if_let,
             replace_qualified_name_with_use::replace_qualified_name_with_use,
             sort_items::sort_items,
index 39ab8c7b74e0276bfe7e9b072d17ceddaa51e78c..95a68ca9893e67688636495bafe353a266cfaaeb 100644 (file)
@@ -121,6 +121,45 @@ struct Point<'a> {
     )
 }
 
+#[test]
+fn doctest_add_missing_match_arms() {
+    check_doc_test(
+        "add_missing_match_arms",
+        r#####"
+enum Action { Move { distance: u32 }, Stop }
+
+fn handle(action: Action) {
+    match action {
+        $0
+    }
+}
+"#####,
+        r#####"
+enum Action { Move { distance: u32 }, Stop }
+
+fn handle(action: Action) {
+    match action {
+        $0Action::Move { distance } => todo!(),
+        Action::Stop => todo!(),
+    }
+}
+"#####,
+    )
+}
+
+#[test]
+fn doctest_add_return_type() {
+    check_doc_test(
+        "add_return_type",
+        r#####"
+fn foo() { 4$02i32 }
+"#####,
+        r#####"
+fn foo() -> i32 { 42i32 }
+"#####,
+    )
+}
+
 #[test]
 fn doctest_add_turbo_fish() {
     check_doc_test(
@@ -213,6 +252,29 @@ fn main() {
     )
 }
 
+#[test]
+fn doctest_convert_for_loop_with_for_each() {
+    check_doc_test(
+        "convert_for_loop_with_for_each",
+        r#####"
+fn main() {
+    let x = vec![1, 2, 3];
+    for$0 v in x {
+        let y = v * 2;
+    }
+}
+"#####,
+        r#####"
+fn main() {
+    let x = vec![1, 2, 3];
+    x.into_iter().for_each(|v| {
+        let y = v * 2;
+    });
+}
+"#####,
+    )
+}
+
 #[test]
 fn doctest_convert_if_to_bool_then() {
     check_doc_test(
@@ -367,6 +429,30 @@ pub fn y(&self) -> f32 {
     )
 }
 
+#[test]
+fn doctest_convert_while_to_loop() {
+    check_doc_test(
+        "convert_while_to_loop",
+        r#####"
+fn main() {
+    $0while cond {
+        foo();
+    }
+}
+"#####,
+        r#####"
+fn main() {
+    loop {
+        if !cond {
+            break;
+        }
+        foo();
+    }
+}
+"#####,
+    )
+}
+
 #[test]
 fn doctest_destructure_tuple_binding() {
     check_doc_test(
@@ -492,32 +578,6 @@ fn main() {
     )
 }
 
-#[test]
-fn doctest_fill_match_arms() {
-    check_doc_test(
-        "fill_match_arms",
-        r#####"
-enum Action { Move { distance: u32 }, Stop }
-
-fn handle(action: Action) {
-    match action {
-        $0
-    }
-}
-"#####,
-        r#####"
-enum Action { Move { distance: u32 }, Stop }
-
-fn handle(action: Action) {
-    match action {
-        $0Action::Move { distance } => todo!(),
-        Action::Stop => todo!(),
-    }
-}
-"#####,
-    )
-}
-
 #[test]
 fn doctest_fix_visibility() {
     check_doc_test(
@@ -970,19 +1030,6 @@ fn set_name(&mut self, name: String) {
     )
 }
 
-#[test]
-fn doctest_infer_function_return_type() {
-    check_doc_test(
-        "infer_function_return_type",
-        r#####"
-fn foo() { 4$02i32 }
-"#####,
-        r#####"
-fn foo() -> i32 { 42i32 }
-"#####,
-    )
-}
-
 #[test]
 fn doctest_inline_call() {
     check_doc_test(
@@ -1022,6 +1069,19 @@ fn main() {
     )
 }
 
+#[test]
+fn doctest_introduce_named_generic() {
+    check_doc_test(
+        "introduce_named_generic",
+        r#####"
+fn foo(bar: $0impl Bar) {}
+"#####,
+        r#####"
+fn foo<B: Bar>(bar: B) {}
+"#####,
+    )
+}
+
 #[test]
 fn doctest_introduce_named_lifetime() {
     check_doc_test(
@@ -1064,6 +1124,23 @@ fn main() {
     )
 }
 
+#[test]
+fn doctest_line_to_block() {
+    check_doc_test(
+        "line_to_block",
+        r#####"
+   // Multi-line$0
+   // comment
+"#####,
+        r#####"
+  /*
+  Multi-line
+  comment
+  */
+"#####,
+    )
+}
+
 #[test]
 fn doctest_make_raw_string() {
     check_doc_test(
@@ -1436,29 +1513,6 @@ impl Debug for S {
     )
 }
 
-#[test]
-fn doctest_replace_for_loop_with_for_each() {
-    check_doc_test(
-        "replace_for_loop_with_for_each",
-        r#####"
-fn main() {
-    let x = vec![1, 2, 3];
-    for$0 v in x {
-        let y = v * 2;
-    }
-}
-"#####,
-        r#####"
-fn main() {
-    let x = vec![1, 2, 3];
-    x.into_iter().for_each(|v| {
-        let y = v * 2;
-    });
-}
-"#####,
-    )
-}
-
 #[test]
 fn doctest_replace_if_let_with_match() {
     check_doc_test(
@@ -1487,19 +1541,6 @@ fn handle(action: Action) {
     )
 }
 
-#[test]
-fn doctest_replace_impl_trait_with_generic() {
-    check_doc_test(
-        "replace_impl_trait_with_generic",
-        r#####"
-fn foo(bar: $0impl Bar) {}
-"#####,
-        r#####"
-fn foo<B: Bar>(bar: B) {}
-"#####,
-    )
-}
-
 #[test]
 fn doctest_replace_let_with_if_let() {
     check_doc_test(
index 67c86216f1f3e0f60cd8309df96f3d8367d3e5ad..8d9100156023a3db2b1fe9526742bfdd415f37f5 100644 (file)
@@ -11,7 +11,6 @@ doctest = false
 [dependencies]
 cov-mark = "2.0.0-pre.1"
 itertools = "0.10.0"
-tracing = "0.1"
 rustc-hash = "1.1.0"
 either = "1.6.1"
 once_cell = "1.7"
index 8acb26ffa16c036c1730425d39dc67817a65126f..2b130cecf5cbe116d1511bee76181d47a4637fd4 100644 (file)
 
 pub(crate) fn complete_attribute(acc: &mut Completions, ctx: &CompletionContext) -> Option<()> {
     let attribute = ctx.attribute_under_caret.as_ref()?;
-    match (attribute.path().and_then(|p| p.as_single_name_ref()), attribute.token_tree()) {
+    let name_ref = match attribute.path() {
+        Some(p) => Some(p.as_single_name_ref()?),
+        None => None,
+    };
+    match (name_ref, attribute.token_tree()) {
         (Some(path), Some(token_tree)) => match path.text().as_str() {
             "derive" => derive::complete_derive(acc, ctx, token_tree),
             "repr" => repr::complete_repr(acc, ctx, token_tree),
index 1eb45036a9dca4516b2dd7d9d859593815fc2ae5..9e6d26640e7eeac748f64fb3e5261a7847c3b3f2 100644 (file)
@@ -1,95 +1,4 @@
-//! Feature: completion with imports-on-the-fly
-//!
-//! When completing names in the current scope, proposes additional imports from other modules or crates,
-//! if they can be qualified in the scope, and their name contains all symbols from the completion input.
-//!
-//! To be considered applicable, the name must contain all input symbols in the given order, not necessarily adjacent.
-//! If any input symbol is not lowercased, the name must contain all symbols in exact case; otherwise the containing is checked case-insensitively.
-//!
-//! ```
-//! fn main() {
-//!     pda$0
-//! }
-//! # pub mod std { pub mod marker { pub struct PhantomData { } } }
-//! ```
-//! ->
-//! ```
-//! use std::marker::PhantomData;
-//!
-//! fn main() {
-//!     PhantomData
-//! }
-//! # pub mod std { pub mod marker { pub struct PhantomData { } } }
-//! ```
-//!
-//! Also completes associated items, that require trait imports.
-//! If any unresolved and/or partially-qualified path precedes the input, it will be taken into account.
-//! Currently, only the imports with their import path ending with the whole qualifier will be proposed
-//! (no fuzzy matching for qualifier).
-//!
-//! ```
-//! mod foo {
-//!     pub mod bar {
-//!         pub struct Item;
-//!
-//!         impl Item {
-//!             pub const TEST_ASSOC: usize = 3;
-//!         }
-//!     }
-//! }
-//!
-//! fn main() {
-//!     bar::Item::TEST_A$0
-//! }
-//! ```
-//! ->
-//! ```
-//! use foo::bar;
-//!
-//! mod foo {
-//!     pub mod bar {
-//!         pub struct Item;
-//!
-//!         impl Item {
-//!             pub const TEST_ASSOC: usize = 3;
-//!         }
-//!     }
-//! }
-//!
-//! fn main() {
-//!     bar::Item::TEST_ASSOC
-//! }
-//! ```
-//!
-//! NOTE: currently, if an assoc item comes from a trait that's not currently imported, and it also has an unresolved and/or partially-qualified path,
-//! no imports will be proposed.
-//!
-//! .Fuzzy search details
-//!
-//! To avoid an excessive amount of the results returned, completion input is checked for inclusion in the names only
-//! (i.e. in `HashMap` in the `std::collections::HashMap` path).
-//! For the same reasons, avoids searching for any path imports for inputs with their length less than 2 symbols
-//! (but shows all associated items for any input length).
-//!
-//! .Import configuration
-//!
-//! It is possible to configure how use-trees are merged with the `importMergeBehavior` setting.
-//! Mimics the corresponding behavior of the `Auto Import` feature.
-//!
-//! .LSP and performance implications
-//!
-//! The feature is enabled only if the LSP client supports LSP protocol version 3.16+ and reports the `additionalTextEdits`
-//! (case-sensitive) resolve client capability in its client capabilities.
-//! This way the server is able to defer the costly computations, doing them for a selected completion item only.
-//! For clients with no such support, all edits have to be calculated on the completion request, including the fuzzy search completion ones,
-//! which might be slow ergo the feature is automatically disabled.
-//!
-//! .Feature toggle
-//!
-//! The feature can be forcefully turned off in the settings with the `rust-analyzer.completion.autoimport.enable` flag.
-//! Note that having this flag set to `true` does not guarantee that the feature is enabled: your client needs to have the corresponding
-//! capability enabled.
-
+//! See [`import_on_the_fly`].
 use ide_db::helpers::{
     import_assets::{ImportAssets, ImportCandidate},
     insert_use::ImportScope,
 
 use super::Completions;
 
+// Feature: Completion With Autoimport
+//
+// When completing names in the current scope, proposes additional imports from other modules or crates,
+// if they can be qualified in the scope, and their name contains all symbols from the completion input.
+//
+// To be considered applicable, the name must contain all input symbols in the given order, not necessarily adjacent.
+// If any input symbol is not lowercased, the name must contain all symbols in exact case; otherwise the containing is checked case-insensitively.
+//
+// ```
+// fn main() {
+//     pda$0
+// }
+// # pub mod std { pub mod marker { pub struct PhantomData { } } }
+// ```
+// ->
+// ```
+// use std::marker::PhantomData;
+//
+// fn main() {
+//     PhantomData
+// }
+// # pub mod std { pub mod marker { pub struct PhantomData { } } }
+// ```
+//
+// Also completes associated items, that require trait imports.
+// If any unresolved and/or partially-qualified path precedes the input, it will be taken into account.
+// Currently, only the imports with their import path ending with the whole qualifier will be proposed
+// (no fuzzy matching for qualifier).
+//
+// ```
+// mod foo {
+//     pub mod bar {
+//         pub struct Item;
+//
+//         impl Item {
+//             pub const TEST_ASSOC: usize = 3;
+//         }
+//     }
+// }
+//
+// fn main() {
+//     bar::Item::TEST_A$0
+// }
+// ```
+// ->
+// ```
+// use foo::bar;
+//
+// mod foo {
+//     pub mod bar {
+//         pub struct Item;
+//
+//         impl Item {
+//             pub const TEST_ASSOC: usize = 3;
+//         }
+//     }
+// }
+//
+// fn main() {
+//     bar::Item::TEST_ASSOC
+// }
+// ```
+//
+// NOTE: currently, if an assoc item comes from a trait that's not currently imported, and it also has an unresolved and/or partially-qualified path,
+// no imports will be proposed.
+//
+// .Fuzzy search details
+//
+// To avoid an excessive amount of the results returned, completion input is checked for inclusion in the names only
+// (i.e. in `HashMap` in the `std::collections::HashMap` path).
+// For the same reasons, avoids searching for any path imports for inputs with their length less than 2 symbols
+// (but shows all associated items for any input length).
+//
+// .Import configuration
+//
+// It is possible to configure how use-trees are merged with the `importMergeBehavior` setting.
+// Mimics the corresponding behavior of the `Auto Import` feature.
+//
+// .LSP and performance implications
+//
+// The feature is enabled only if the LSP client supports LSP protocol version 3.16+ and reports the `additionalTextEdits`
+// (case-sensitive) resolve client capability in its client capabilities.
+// This way the server is able to defer the costly computations, doing them for a selected completion item only.
+// For clients with no such support, all edits have to be calculated on the completion request, including the fuzzy search completion ones,
+// which might be slow ergo the feature is automatically disabled.
+//
+// .Feature toggle
+//
+// The feature can be forcefully turned off in the settings with the `rust-analyzer.completion.autoimport.enable` flag.
+// Note that having this flag set to `true` does not guarantee that the feature is enabled: your client needs to have the corresponding
+// capability enabled.
 pub(crate) fn import_on_the_fly(acc: &mut Completions, ctx: &CompletionContext) -> Option<()> {
     if !ctx.config.enable_imports_on_the_fly {
         return None;
index d8d3b8e85b2c0b494dac6159043e29e207e7918b..28da6d69c8c906721f794889ddbba92ba96d58fc 100644 (file)
@@ -1,4 +1,4 @@
-//! See `complete_fn_param`.
+//! See [`complete_fn_param`].
 
 use rustc_hash::FxHashMap;
 use syntax::{
index 4273129dff249b9527a78d303935a15d0fc7cdfb..abc021acf11245fd8b4207669a03a906dbc4e9dc 100644 (file)
@@ -107,7 +107,6 @@ pub(crate) struct CompletionContext<'a> {
 
     pub(super) pattern_ctx: Option<PatternContext>,
     pub(super) path_context: Option<PathCompletionContext>,
-    pub(super) active_parameter: Option<ActiveParameter>,
     pub(super) locals: Vec<(String, Local)>,
 
     pub(super) incomplete_let: bool,
@@ -170,53 +169,97 @@ pub(super) fn new(
             attribute_under_caret: None,
             previous_token: None,
             path_context: None,
-            active_parameter: ActiveParameter::at(db, position),
             locals,
             incomplete_let: false,
             no_completion_required: false,
         };
+        ctx.expand_and_fill(
+            original_file.syntax().clone(),
+            file_with_fake_ident.syntax().clone(),
+            position.offset,
+            fake_ident_token,
+        );
+        Some(ctx)
+    }
 
-        let mut original_file = original_file.syntax().clone();
-        let mut speculative_file = file_with_fake_ident.syntax().clone();
-        let mut offset = position.offset;
-        let mut fake_ident_token = fake_ident_token;
-
-        // Are we inside a macro call?
-        while let (Some(actual_macro_call), Some(macro_call_with_fake_ident)) = (
-            find_node_at_offset::<ast::MacroCall>(&original_file, offset),
-            find_node_at_offset::<ast::MacroCall>(&speculative_file, offset),
-        ) {
-            if actual_macro_call.path().as_ref().map(|s| s.syntax().text())
-                != macro_call_with_fake_ident.path().as_ref().map(|s| s.syntax().text())
-            {
-                break;
+    fn expand_and_fill(
+        &mut self,
+        mut original_file: SyntaxNode,
+        mut speculative_file: SyntaxNode,
+        mut offset: TextSize,
+        mut fake_ident_token: SyntaxToken,
+    ) {
+        loop {
+            // Expand attributes
+            if let (Some(actual_item), Some(item_with_fake_ident)) = (
+                find_node_at_offset::<ast::Item>(&original_file, offset),
+                find_node_at_offset::<ast::Item>(&speculative_file, offset),
+            ) {
+                match (
+                    self.sema.expand_attr_macro(&actual_item),
+                    self.sema.speculative_expand_attr_macro(
+                        &actual_item,
+                        &item_with_fake_ident,
+                        fake_ident_token.clone(),
+                    ),
+                ) {
+                    (Some(actual_expansion), Some(speculative_expansion)) => {
+                        let new_offset = speculative_expansion.1.text_range().start();
+                        if new_offset > actual_expansion.text_range().end() {
+                            break;
+                        }
+                        original_file = actual_expansion;
+                        speculative_file = speculative_expansion.0;
+                        fake_ident_token = speculative_expansion.1;
+                        offset = new_offset;
+                        continue;
+                    }
+                    (None, None) => (),
+                    _ => break,
+                }
             }
-            let speculative_args = match macro_call_with_fake_ident.token_tree() {
-                Some(tt) => tt,
-                None => break,
-            };
-            if let (Some(actual_expansion), Some(speculative_expansion)) = (
-                ctx.sema.expand(&actual_macro_call),
-                ctx.sema.speculative_expand(
-                    &actual_macro_call,
-                    &speculative_args,
-                    fake_ident_token,
-                ),
+
+            // Expand fn-like macro calls
+            if let (Some(actual_macro_call), Some(macro_call_with_fake_ident)) = (
+                find_node_at_offset::<ast::MacroCall>(&original_file, offset),
+                find_node_at_offset::<ast::MacroCall>(&speculative_file, offset),
             ) {
-                let new_offset = speculative_expansion.1.text_range().start();
-                if new_offset > actual_expansion.text_range().end() {
+                let mac_call_path0 = actual_macro_call.path().as_ref().map(|s| s.syntax().text());
+                let mac_call_path1 =
+                    macro_call_with_fake_ident.path().as_ref().map(|s| s.syntax().text());
+                if mac_call_path0 != mac_call_path1 {
+                    break;
+                }
+                let speculative_args = match macro_call_with_fake_ident.token_tree() {
+                    Some(tt) => tt,
+                    None => break,
+                };
+
+                if let (Some(actual_expansion), Some(speculative_expansion)) = (
+                    self.sema.expand(&actual_macro_call),
+                    self.sema.speculative_expand(
+                        &actual_macro_call,
+                        &speculative_args,
+                        fake_ident_token,
+                    ),
+                ) {
+                    let new_offset = speculative_expansion.1.text_range().start();
+                    if new_offset > actual_expansion.text_range().end() {
+                        break;
+                    }
+                    original_file = actual_expansion;
+                    speculative_file = speculative_expansion.0;
+                    fake_ident_token = speculative_expansion.1;
+                    offset = new_offset;
+                } else {
                     break;
                 }
-                original_file = actual_expansion;
-                speculative_file = speculative_expansion.0;
-                fake_ident_token = speculative_expansion.1;
-                offset = new_offset;
             } else {
                 break;
             }
         }
-        ctx.fill(&original_file, speculative_file, offset);
-        Some(ctx)
+
+        self.fill(&original_file, speculative_file, offset);
     }
 
     /// Checks whether completions in that particular case don't make much sense.
@@ -393,6 +436,10 @@ pub(crate) fn is_item_hidden(&self, item: &hir::ItemInNs) -> bool {
         }
     }
 
+    pub(crate) fn is_immediately_after_macro_bang(&self) -> bool {
+        self.token.kind() == BANG && self.token.parent().map_or(false, |it| it.kind() == MACRO_CALL)
+    }
+
     /// A version of [`SemanticsScope::process_all_names`] that filters out `#[doc(hidden)]` items.
     pub(crate) fn process_all_names(&self, f: &mut dyn FnMut(Name, ScopeDef)) {
         self.scope.process_all_names(&mut |name, def| {
index 44a5ceb25ad60c45cebbb584bae9218747e42518..d5cfd8bba46d49629d7748aa68c2cc72888fb35a 100644 (file)
@@ -26,7 +26,6 @@ pub(crate) fn render_variant(
 #[derive(Debug)]
 struct EnumRender<'a> {
     ctx: RenderContext<'a>,
-    name: hir::Name,
     variant: hir::Variant,
     path: Option<hir::ModPath>,
     qualified_name: hir::ModPath,
@@ -58,7 +57,7 @@ fn new(
             ),
         };
 
-        EnumRender { ctx, name, variant, path, qualified_name, short_qualified_name, variant_kind }
+        EnumRender { ctx, variant, path, qualified_name, short_qualified_name, variant_kind }
     }
     fn render(self, import_to_add: Option<ImportEdit>) -> CompletionItem {
         let mut item = CompletionItem::new(
index eb9fb4d8bf0a8a4aec33c33175b14b1098d2dff2..d1b549df1bb9df0339d48e47ffb5755c39001a1c 100644 (file)
@@ -41,8 +41,13 @@ fn new(ctx: RenderContext<'a>, name: hir::Name, macro_: hir::MacroDef) -> MacroR
     }
 
     fn render(&self, import_to_add: Option<ImportEdit>) -> Option<CompletionItem> {
-        let mut item =
-            CompletionItem::new(CompletionKind::Reference, self.ctx.source_range(), &self.label());
+        let source_range = if self.ctx.completion.is_immediately_after_macro_bang() {
+            cov_mark::hit!(completes_macro_call_if_cursor_at_bang_token);
+            self.ctx.completion.token.parent().map(|it| it.text_range())
+        } else {
+            Some(self.ctx.source_range())
+        }?;
+        let mut item = CompletionItem::new(CompletionKind::Reference, source_range, &self.label());
         item.kind(SymbolKind::Macro)
             .set_documentation(self.docs.clone())
             .set_deprecated(self.ctx.is_deprecated(self.macro_))
@@ -230,4 +235,31 @@ fn main() { foo! {$0} }
 "#,
         )
     }
+
+    #[test]
+    fn completes_macro_call_if_cursor_at_bang_token() {
+        // Regression test for https://github.com/rust-analyzer/rust-analyzer/issues/9904
+        cov_mark::check!(completes_macro_call_if_cursor_at_bang_token);
+        check_edit(
+            "foo!",
+            r#"
+macro_rules! foo {
+    () => {}
+}
+
+fn main() {
+    foo!$0
+}
+"#,
+            r#"
+macro_rules! foo {
+    () => {}
+}
+
+fn main() {
+    foo!($0)
+}
+"#,
+        );
+    }
 }
index 5ef6829a05cc1dab8a4f026857a2a4883227013d..6872e3b8dc13273d572def53d0ab17c3e564c185 100644 (file)
@@ -15,6 +15,7 @@
 mod item;
 mod pattern;
 mod predicate;
+mod proc_macros;
 mod record;
 mod sourcegen;
 mod type_pos;
@@ -23,7 +24,7 @@
 
 use std::mem;
 
-use hir::{PrefixKind, Semantics};
+use hir::{db::DefDatabase, PrefixKind, Semantics};
 use ide_db::{
     base_db::{fixture::ChangeFixture, FileLoader, FilePosition},
     helpers::{
@@ -96,6 +97,7 @@ fn completion_list_with_config(config: CompletionConfig, ra_fixture: &str) -> St
 pub(crate) fn position(ra_fixture: &str) -> (RootDatabase, FilePosition) {
     let change_fixture = ChangeFixture::parse(ra_fixture);
     let mut database = RootDatabase::default();
+    database.set_enable_proc_attr_macros(true);
     database.apply_change(change_fixture.change);
     let (file_id, range_or_offset) = change_fixture.file_position.expect("expected a marker ($0)");
     let offset = range_or_offset.expect_offset();
index 72c47fe96e9dba9a6681cd8cfb529a5646fb99f3..7d0bdc58210dd7674f6a5fe5596b7b564ade99b4 100644 (file)
@@ -33,6 +33,18 @@ fn doesnt_complete_items() {
     )
 }
 
+#[test]
+fn doesnt_complete_qualified() {
+    check(
+        r#"
+struct Foo;
+#[foo::$0]
+use self as this;
+"#,
+        expect![[r#""#]],
+    )
+}
+
 #[test]
 fn inside_nested_attr() {
     check(r#"#[cfg($0)]"#, expect![[]])
diff --git a/crates/ide_completion/src/tests/proc_macros.rs b/crates/ide_completion/src/tests/proc_macros.rs
new file mode 100644 (file)
index 0000000..73fc293
--- /dev/null
@@ -0,0 +1,145 @@
+//! Completion tests for expressions.
+use expect_test::{expect, Expect};
+
+use crate::tests::completion_list;
+
+fn check(ra_fixture: &str, expect: Expect) {
+    let actual = completion_list(ra_fixture);
+    expect.assert_eq(&actual)
+}
+
+#[test]
+fn complete_dot_in_attr() {
+    check(
+        r#"
+//- proc_macros: identity
+pub struct Foo;
+impl Foo {
+    fn foo(&self) {}
+}
+
+#[proc_macros::identity]
+fn main() {
+    Foo.$0
+}
+"#,
+        expect![[r#"
+            me foo() fn(&self)
+            sn ref   &expr
+            sn refm  &mut expr
+            sn match match expr {}
+            sn box   Box::new(expr)
+            sn ok    Ok(expr)
+            sn err   Err(expr)
+            sn some  Some(expr)
+            sn dbg   dbg!(expr)
+            sn dbgr  dbg!(&expr)
+            sn call  function(expr)
+            sn let   let
+            sn letm  let mut
+        "#]],
+    )
+}
+
+#[test]
+fn complete_dot_in_attr2() {
+    check(
+        r#"
+//- proc_macros: identity
+pub struct Foo;
+impl Foo {
+    fn foo(&self) {}
+}
+
+#[proc_macros::identity]
+fn main() {
+    Foo.f$0
+}
+"#,
+        expect![[r#"
+            me foo() fn(&self)
+            sn ref   &expr
+            sn refm  &mut expr
+            sn match match expr {}
+            sn box   Box::new(expr)
+            sn ok    Ok(expr)
+            sn err   Err(expr)
+            sn some  Some(expr)
+            sn dbg   dbg!(expr)
+            sn dbgr  dbg!(&expr)
+            sn call  function(expr)
+            sn let   let
+            sn letm  let mut
+        "#]],
+    )
+}
+
+#[test]
+fn complete_dot_in_attr_input() {
+    check(
+        r#"
+//- proc_macros: input_replace
+pub struct Foo;
+impl Foo {
+    fn foo(&self) {}
+}
+
+#[proc_macros::input_replace(
+    fn suprise() {
+        Foo.$0
+    }
+)]
+fn main() {}
+"#,
+        expect![[r#"
+            me foo() fn(&self)
+            sn ref   &expr
+            sn refm  &mut expr
+            sn match match expr {}
+            sn box   Box::new(expr)
+            sn ok    Ok(expr)
+            sn err   Err(expr)
+            sn some  Some(expr)
+            sn dbg   dbg!(expr)
+            sn dbgr  dbg!(&expr)
+            sn call  function(expr)
+            sn let   let
+            sn letm  let mut
+        "#]],
+    )
+}
+
+#[test]
+fn complete_dot_in_attr_input2() {
+    check(
+        r#"
+//- proc_macros: input_replace
+pub struct Foo;
+impl Foo {
+    fn foo(&self) {}
+}
+
+#[proc_macros::input_replace(
+    fn suprise() {
+        Foo.f$0
+    }
+)]
+fn main() {}
+"#,
+        expect![[r#"
+            me foo() fn(&self)
+            sn ref   &expr
+            sn refm  &mut expr
+            sn match match expr {}
+            sn box   Box::new(expr)
+            sn ok    Ok(expr)
+            sn err   Err(expr)
+            sn some  Some(expr)
+            sn dbg   dbg!(expr)
+            sn dbgr  dbg!(&expr)
+            sn call  function(expr)
+            sn let   let
+            sn letm  let mut
+        "#]],
+    )
+}
index 981a58347a515fa7cb1e16523a52e7c43fe61d28..cdaff0ce3977a7706f7b8b4f1cfbba60f229fd99 100644 (file)
@@ -17,6 +17,7 @@ rustc-hash = "1.1.0"
 once_cell = "1.3.1"
 either = "1.6.1"
 itertools = "0.10.0"
+arrayvec = "0.7"
 
 stdx = { path = "../stdx", version = "0.0.0" }
 syntax = { path = "../syntax", version = "0.0.0" }
index 016b00143967e95690108bff81cb84365d905cdc..3d32fddd9397a1e4524caecfabc9c7e39407fbe7 100644 (file)
@@ -154,15 +154,6 @@ pub struct ActiveParameter {
 }
 
 impl ActiveParameter {
-    pub fn at(db: &RootDatabase, position: FilePosition) -> Option<Self> {
-        let sema = Semantics::new(db);
-        let file = sema.parse(position.file_id);
-        let file = file.syntax();
-        let token = file.token_at_offset(position.offset).next()?;
-        let token = sema.descend_into_macros(token);
-        Self::at_token(&sema, token)
-    }
-
     pub fn at_token(sema: &Semantics<RootDatabase>, token: SyntaxToken) -> Option<Self> {
         let (signature, active_parameter) = call_info_impl(sema, token)?;
 
index 719f424fd2dc0c9d23e1704ee544664f3c2ad765..effa694aaed6b2f7fa2fa474c490a4453ff322d6 100644 (file)
@@ -5,16 +5,17 @@
 
 // FIXME: this badly needs rename/rewrite (matklad, 2020-02-06).
 
+use arrayvec::ArrayVec;
 use hir::{
     Field, GenericParam, HasVisibility, Impl, Label, Local, MacroDef, Module, ModuleDef, Name,
     PathResolution, Semantics, Visibility,
 };
 use syntax::{
     ast::{self, AstNode},
-    match_ast, SyntaxKind,
+    match_ast, SyntaxKind, SyntaxNode, SyntaxToken,
 };
 
-use crate::RootDatabase;
+use crate::{helpers::try_resolve_derive_input_at, RootDatabase};
 
 // FIXME: a more precise name would probably be `Symbol`?
 #[derive(Debug, PartialEq, Eq, Copy, Clone, Hash)]
@@ -29,6 +30,73 @@ pub enum Definition {
 }
 
 impl Definition {
+    pub fn from_token(
+        sema: &Semantics<RootDatabase>,
+        token: &SyntaxToken,
+    ) -> ArrayVec<Definition, 2> {
+        let parent = match token.parent() {
+            Some(parent) => parent,
+            None => return Default::default(),
+        };
+        let attr = parent
+            .ancestors()
+            .find_map(ast::TokenTree::cast)
+            .and_then(|tt| tt.parent_meta())
+            .and_then(|meta| meta.parent_attr());
+        if let Some(attr) = attr {
+            try_resolve_derive_input_at(&sema, &attr, &token)
+                .map(Definition::Macro)
+                .into_iter()
+                .collect()
+        } else {
+            Self::from_node(sema, &parent)
+        }
+    }
+
+    pub fn from_node(sema: &Semantics<RootDatabase>, node: &SyntaxNode) -> ArrayVec<Definition, 2> {
+        let mut res = ArrayVec::new();
+        (|| {
+            match_ast! {
+                match node {
+                    ast::Name(name) => {
+                        match NameClass::classify(&sema, &name)? {
+                            NameClass::Definition(it) | NameClass::ConstReference(it) => res.push(it),
+                            NameClass::PatFieldShorthand { local_def, field_ref } => {
+                                res.push(Definition::Local(local_def));
+                                res.push(Definition::Field(field_ref));
+                            }
+                        }
+                    },
+                    ast::NameRef(name_ref) => {
+                        match NameRefClass::classify(sema, &name_ref)? {
+                            NameRefClass::Definition(it) => res.push(it),
+                            NameRefClass::FieldShorthand { local_ref, field_ref } => {
+                                res.push(Definition::Local(local_ref));
+                                res.push(Definition::Field(field_ref));
+                            }
+                        }
+                    },
+                    ast::Lifetime(lifetime) => {
+                        let def = if let Some(x) = NameClass::classify_lifetime(&sema, &lifetime) {
+                            NameClass::defined(x)
+                        } else {
+                            NameRefClass::classify_lifetime(&sema, &lifetime).and_then(|class| match class {
+                                NameRefClass::Definition(it) => Some(it),
+                                _ => None,
+                            })
+                        };
+                        if let Some(def) = def {
+                            res.push(def);
+                        }
+                    },
+                    _ => (),
+                }
+            }
+            Some(())
+        })();
+        res
+    }
+
     pub fn module(&self, db: &RootDatabase) -> Option<Module> {
         match self {
             Definition::Macro(it) => it.module(db),
index 8b8c91b2fce32b236c4a5247022f9151c9d5be0f..a84e6b3ba40d2123668dca8a6c8b538d063c1421 100644 (file)
@@ -232,50 +232,36 @@ fn search_scope(&self, db: &RootDatabase) -> SearchScope {
         let file_id = file_id.original_file(db);
 
         if let Definition::Local(var) = self {
-            let range = match var.parent(db) {
-                DefWithBody::Function(f) => f.source(db).map(|src| src.value.syntax().text_range()),
-                DefWithBody::Const(c) => c.source(db).map(|src| src.value.syntax().text_range()),
-                DefWithBody::Static(s) => s.source(db).map(|src| src.value.syntax().text_range()),
+            let def = match var.parent(db) {
+                DefWithBody::Function(f) => f.source(db).map(|src| src.syntax().cloned()),
+                DefWithBody::Const(c) => c.source(db).map(|src| src.syntax().cloned()),
+                DefWithBody::Static(s) => s.source(db).map(|src| src.syntax().cloned()),
             };
-            return match range {
-                Some(range) => SearchScope::file_range(FileRange { file_id, range }),
+            return match def {
+                Some(def) => SearchScope::file_range(def.as_ref().original_file_range(db)),
                 None => SearchScope::single_file(file_id),
             };
         }
 
         if let Definition::SelfType(impl_) = self {
-            return match impl_.source(db).map(|src| src.value.syntax().text_range()) {
-                Some(range) => SearchScope::file_range(FileRange { file_id, range }),
+            return match impl_.source(db).map(|src| src.syntax().cloned()) {
+                Some(def) => SearchScope::file_range(def.as_ref().original_file_range(db)),
                 None => SearchScope::single_file(file_id),
             };
         }
 
         if let Definition::GenericParam(hir::GenericParam::LifetimeParam(param)) = self {
-            let range = match param.parent(db) {
-                hir::GenericDef::Function(it) => {
-                    it.source(db).map(|src| src.value.syntax().text_range())
-                }
-                hir::GenericDef::Adt(it) => {
-                    it.source(db).map(|src| src.value.syntax().text_range())
-                }
-                hir::GenericDef::Trait(it) => {
-                    it.source(db).map(|src| src.value.syntax().text_range())
-                }
-                hir::GenericDef::TypeAlias(it) => {
-                    it.source(db).map(|src| src.value.syntax().text_range())
-                }
-                hir::GenericDef::Impl(it) => {
-                    it.source(db).map(|src| src.value.syntax().text_range())
-                }
-                hir::GenericDef::Variant(it) => {
-                    it.source(db).map(|src| src.value.syntax().text_range())
-                }
-                hir::GenericDef::Const(it) => {
-                    it.source(db).map(|src| src.value.syntax().text_range())
-                }
+            let def = match param.parent(db) {
+                hir::GenericDef::Function(it) => it.source(db).map(|src| src.syntax().cloned()),
+                hir::GenericDef::Adt(it) => it.source(db).map(|src| src.syntax().cloned()),
+                hir::GenericDef::Trait(it) => it.source(db).map(|src| src.syntax().cloned()),
+                hir::GenericDef::TypeAlias(it) => it.source(db).map(|src| src.syntax().cloned()),
+                hir::GenericDef::Impl(it) => it.source(db).map(|src| src.syntax().cloned()),
+                hir::GenericDef::Variant(it) => it.source(db).map(|src| src.syntax().cloned()),
+                hir::GenericDef::Const(it) => it.source(db).map(|src| src.syntax().cloned()),
             };
-            return match range {
-                Some(range) => SearchScope::file_range(FileRange { file_id, range }),
+            return match def {
+                Some(def) => SearchScope::file_range(def.as_ref().original_file_range(db)),
                 None => SearchScope::single_file(file_id),
             };
         }
index 06c1f2cf820c5ba77bdadca9e1d4c88f4afbc39d..9eafd42d86e78dc2f7855c001ddf0f6965321622 100644 (file)
@@ -38,21 +38,35 @@ fn fixes(ctx: &DiagnosticsContext, file_id: FileId) -> Option<Vec<Assist>> {
 
     let source_root = ctx.sema.db.source_root(ctx.sema.db.file_source_root(file_id));
     let our_path = source_root.path_for_file(&file_id)?;
-    let (module_name, _) = our_path.name_and_extension()?;
+    let (mut module_name, _) = our_path.name_and_extension()?;
 
     // Candidates to look for:
-    // - `mod.rs` in the same folder
-    //   - we also check `main.rs` and `lib.rs`
+    // - `mod.rs`, `main.rs` and `lib.rs` in the same folder
     // - `$dir.rs` in the parent folder, where `$dir` is the directory containing `self.file_id`
     let parent = our_path.parent()?;
-    let mut paths = vec![parent.join("mod.rs")?, parent.join("lib.rs")?, parent.join("main.rs")?];
-
-    // `submod/bla.rs` -> `submod.rs`
-    let parent_mod = (|| {
-        let (name, _) = parent.name_and_extension()?;
-        parent.parent()?.join(&format!("{}.rs", name))
-    })();
-    paths.extend(parent_mod);
+    let paths = {
+        let temp;
+        let parent = if module_name == "mod" {
+            // for mod.rs we need to actually look up one higher
+            // and take the parent as our to be module name
+            let (name, _) = parent.name_and_extension()?;
+            module_name = name;
+            temp = parent.parent()?;
+            &temp
+        } else {
+            &parent
+        };
+        let mut paths =
+            vec![parent.join("mod.rs")?, parent.join("lib.rs")?, parent.join("main.rs")?];
+
+        // `submod/bla.rs` -> `submod.rs`
+        let parent_mod = (|| {
+            let (name, _) = parent.name_and_extension()?;
+            parent.parent()?.join(&format!("{}.rs", name))
+        })();
+        paths.extend(parent_mod);
+        paths
+    };
 
     for &parent_id in paths.iter().filter_map(|path| source_root.file_for_path(path)) {
         for &krate in ctx.sema.db.relevant_crates(parent_id).iter() {
@@ -156,6 +170,7 @@ fn is_outline_mod(item: &ast::Item) -> bool {
 
 #[cfg(test)]
 mod tests {
+
     use crate::tests::{check_diagnostics, check_fix, check_fixes, check_no_fix};
 
     #[test]
@@ -232,6 +247,34 @@ fn unlinked_file_insert_in_empty_file() {
         );
     }
 
+    #[test]
+    fn unlinked_file_insert_in_empty_file_mod_file() {
+        check_fix(
+            r#"
+//- /main.rs
+//- /foo/mod.rs
+$0
+"#,
+            r#"
+mod foo;
+"#,
+        );
+        check_fix(
+            r#"
+//- /main.rs
+mod bar;
+//- /bar.rs
+// bar module
+//- /bar/foo/mod.rs
+$0
+"#,
+            r#"
+// bar module
+mod foo;
+"#,
+        );
+    }
+
     #[test]
     fn unlinked_file_old_style_modrs() {
         check_fix(
index 47434f4af84b6e7ee8f62d8e0a726f0b677bb32c..2fe1f5b616ed91ecc4fe8ed0328f4d733a60f4b1 100644 (file)
@@ -103,7 +103,6 @@ pub struct SsrRule {
 
 #[derive(Debug)]
 pub struct SsrPattern {
-    raw: parsing::RawPattern,
     parsed_rules: Vec<parsing::ParsedRule>,
 }
 
@@ -173,7 +172,7 @@ pub fn edits(&self) -> FxHashMap<FileId, TextEdit> {
         for m in self.matches().matches {
             matches_by_file
                 .entry(m.range.file_id)
-                .or_insert_with(|| SsrMatches::default())
+                .or_insert_with(SsrMatches::default)
                 .matches
                 .push(m);
         }
@@ -332,7 +331,7 @@ pub fn flattened(self) -> SsrMatches {
     fn flatten_into(self, out: &mut SsrMatches) {
         for mut m in self.matches {
             for p in m.placeholder_values.values_mut() {
-                std::mem::replace(&mut p.inner_matches, SsrMatches::default()).flatten_into(out);
+                std::mem::take(&mut p.inner_matches).flatten_into(out);
             }
             out.matches.push(m);
         }
index 55147674d25b9cc340f652c675d65d9e6ce3d0f9..68129472185cf207d009ce4ff1a77d4acab18379 100644 (file)
@@ -61,9 +61,6 @@ pub struct Match {
 /// Information about a placeholder bound in a match.
 #[derive(Debug)]
 pub(crate) struct PlaceholderMatch {
-    /// The node that the placeholder matched to. If set, then we'll search for further matches
-    /// within this node. It isn't set when we match tokens within a macro call's token tree.
-    pub(crate) node: Option<SyntaxNode>,
     pub(crate) range: FileRange,
     /// More matches, found within `node`.
     pub(crate) inner_matches: SsrMatches,
@@ -186,7 +183,7 @@ fn attempt_match_node(
                 self.validate_range(&original_range)?;
                 matches_out.placeholder_values.insert(
                     placeholder.ident.clone(),
-                    PlaceholderMatch::new(Some(code), original_range),
+                    PlaceholderMatch::from_range(original_range),
                 );
             }
             return Ok(());
@@ -465,7 +462,7 @@ fn attempt_match_token_tree(
                 let mut last_matched_token = child;
                 // Read code tokens util we reach one equal to the next token from our pattern
                 // or we reach the end of the token tree.
-                while let Some(next) = children.next() {
+                for next in &mut children {
                     match &next {
                         SyntaxElement::Token(t) => {
                             if Some(t.to_string()) == next_pattern_token {
@@ -715,19 +712,14 @@ fn recording_match_fail_reasons() -> bool {
 }
 
 impl PlaceholderMatch {
-    fn new(node: Option<&SyntaxNode>, range: FileRange) -> Self {
+    fn from_range(range: FileRange) -> Self {
         Self {
-            node: node.cloned(),
             range,
             inner_matches: SsrMatches::default(),
             autoderef_count: 0,
             autoref_kind: ast::SelfParamKind::Owned,
         }
     }
-
-    fn from_range(range: FileRange) -> Self {
-        Self::new(None, range)
-    }
 }
 
 impl NodeKind {
@@ -771,7 +763,7 @@ impl Iterator for PatternIterator {
     type Item = SyntaxElement;
 
     fn next(&mut self) -> Option<SyntaxElement> {
-        while let Some(element) = self.iter.next() {
+        for element in &mut self.iter {
             if !element.kind().is_trivia() {
                 return Some(element);
             }
@@ -788,7 +780,6 @@ fn new(parent: &SyntaxNode) -> Self {
 
 #[cfg(test)]
 mod tests {
-    use super::*;
     use crate::{MatchFinder, SsrRule};
 
     #[test]
@@ -803,14 +794,6 @@ fn parse_match_replace() {
         assert_eq!(matches.matches.len(), 1);
         assert_eq!(matches.matches[0].matched_node.text(), "foo(1+2)");
         assert_eq!(matches.matches[0].placeholder_values.len(), 1);
-        assert_eq!(
-            matches.matches[0].placeholder_values[&Var("x".to_string())]
-                .node
-                .as_ref()
-                .unwrap()
-                .text(),
-            "1+2"
-        );
 
         let edits = match_finder.edits();
         assert_eq!(edits.len(), 1);
index 6ac355dfc22b47db29349f465f97391daaf60f35..e734125ec8dbc58cb913277e81fae883c99469d6 100644 (file)
@@ -65,7 +65,7 @@ fn try_add_sub_match(m: Match, existing: &mut Match, sema: &hir::Semantics<ide_d
             // will have 0 and a few will have 1. More than that should hopefully be
             // exceptional.
             let mut collector = MatchCollector::default();
-            for m in std::mem::replace(&mut p.inner_matches.matches, Vec::new()) {
+            for m in std::mem::take(&mut p.inner_matches.matches) {
                 collector.matches_by_node.insert(m.matched_node.clone(), m);
             }
             collector.add_match(m, sema);
index 5e757e70191a1142254db6eb83ba303a08336693..ed7c033e275b3c9340934de49a2571ccc091a2a5 100644 (file)
@@ -204,7 +204,7 @@ impl FromStr for SsrPattern {
     fn from_str(pattern_str: &str) -> Result<SsrPattern, SsrError> {
         let raw_pattern = pattern_str.parse()?;
         let parsed_rules = ParsedRule::new(&raw_pattern, None)?;
-        Ok(SsrPattern { raw: raw_pattern, parsed_rules })
+        Ok(SsrPattern { parsed_rules })
     }
 }
 
index a66a7a4a8400d769fbb0522270f5018436ac63ce..df20ab375d2299b42fd38cfa2390b94b8ea0aee4 100644 (file)
@@ -243,10 +243,10 @@ fn qualifier_type(&self, path: &SyntaxNode) -> Option<hir::Type> {
         use syntax::ast::AstNode;
         if let Some(path) = ast::Path::cast(path.clone()) {
             if let Some(qualifier) = path.qualifier() {
-                if let Some(resolved_qualifier) = self.resolve_path(&qualifier) {
-                    if let hir::PathResolution::Def(hir::ModuleDef::Adt(adt)) = resolved_qualifier {
-                        return Some(adt.ty(self.scope.db));
-                    }
+                if let Some(hir::PathResolution::Def(hir::ModuleDef::Adt(adt))) =
+                    self.resolve_path(&qualifier)
+                {
+                    return Some(adt.ty(self.scope.db));
                 }
             }
         }
index f2056919ed0e168105560570a652dbcad4715844..42ce85eebe62e6f20fe98a776dfbe32691de568e 100644 (file)
@@ -80,7 +80,7 @@ fn find_node_to_match(
         if let Some(path) =
             self.sema.find_node_at_offset_with_descend::<ast::Path>(file.syntax(), offset)
         {
-            self.sema.ancestors_with_macros(path.syntax().clone()).skip(depth).next()
+            self.sema.ancestors_with_macros(path.syntax().clone()).nth(depth)
         } else if let Some(path) =
             self.sema.find_node_at_offset_with_descend::<ast::MethodCallExpr>(file.syntax(), offset)
         {
@@ -96,8 +96,7 @@ fn find_node_to_match(
             }
             self.sema
                 .ancestors_with_macros(path.syntax().clone())
-                .skip(depth - PATH_DEPTH_IN_CALL_EXPR)
-                .next()
+                .nth(depth - PATH_DEPTH_IN_CALL_EXPR)
         } else {
             None
         }
index 1a522e907044a884720ddf30f0aca226d1ecab7d..22bda57669054c78e05b86e7b8e776109f886c57 100644 (file)
@@ -21,5 +21,4 @@ tt = { path = "../tt", version = "0.0.0" }
 stdx = { path = "../stdx", version = "0.0.0" }
 
 [dev-dependencies]
-profile = { path = "../profile" }
 test_utils = { path = "../test_utils" }
index fb082a4a05a6e2ed9bdd6492868efceeb7176e29..7f68543310994c60bb6fc98818dd3f2d9ef77175 100644 (file)
@@ -1,14 +1,13 @@
 //! Conversions between [`SyntaxNode`] and [`tt::TokenTree`].
 
-use std::iter;
-
 use parser::{ParseError, TreeSink};
-use rustc_hash::FxHashMap;
+use rustc_hash::{FxHashMap, FxHashSet};
 use syntax::{
     ast::{self, make::tokens::doc_comment},
-    tokenize, AstToken, Parse, SmolStr, SyntaxKind,
+    tokenize, AstToken, Parse, PreorderWithTokens, SmolStr, SyntaxElement, SyntaxKind,
     SyntaxKind::*,
-    SyntaxNode, SyntaxToken, SyntaxTreeBuilder, TextRange, TextSize, Token as RawToken, T,
+    SyntaxNode, SyntaxToken, SyntaxTreeBuilder, TextRange, TextSize, Token as RawToken, WalkEvent,
+    T,
 };
 use tt::buffer::{Cursor, TokenBuffer};
 
 /// Convert the syntax node to a `TokenTree` (what macro
 /// will consume).
 pub fn syntax_node_to_token_tree(node: &SyntaxNode) -> (tt::Subtree, TokenMap) {
-    syntax_node_to_token_tree_censored(node, None)
+    syntax_node_to_token_tree_censored(node, &Default::default())
 }
 
 /// Convert the syntax node to a `TokenTree` (what macro will consume)
 /// with the censored range excluded.
 pub fn syntax_node_to_token_tree_censored(
     node: &SyntaxNode,
-    censor: Option<TextRange>,
+    censor: &FxHashSet<SyntaxNode>,
 ) -> (tt::Subtree, TokenMap) {
     let global_offset = node.text_range().start();
     let mut c = Convertor::new(node, global_offset, censor);
@@ -90,7 +89,7 @@ pub fn parse_to_token_tree(text: &str) -> Option<(tt::Subtree, TokenMap)> {
     Some((subtree, conv.id_alloc.map))
 }
 
-/// Split token tree with seperate expr: $($e:expr)SEP*
+/// Split token tree with separate expr: $($e:expr)SEP*
 pub fn parse_exprs_with_sep(tt: &tt::Subtree, sep: char) -> Vec<tt::Subtree> {
     if tt.token_trees.is_empty() {
         return Vec::new();
@@ -101,9 +100,6 @@ pub fn parse_exprs_with_sep(tt: &tt::Subtree, sep: char) -> Vec<tt::Subtree> {
 
     while iter.peek_n(0).is_some() {
         let expanded = iter.expect_fragment(ParserEntryPoint::Expr);
-        if expanded.err.is_some() {
-            break;
-        }
 
         res.push(match expanded.value {
             None => break,
@@ -153,7 +149,18 @@ struct StackEntry {
         let k: SyntaxKind = token.kind();
         if k == COMMENT {
             if let Some(tokens) = conv.convert_doc_comment(&token) {
-                result.extend(tokens);
+                // FIXME: There has to be a better way to do this
+                // Add the comments token id to the converted doc string
+                let id = conv.id_alloc().alloc(range);
+                result.extend(tokens.into_iter().map(|mut tt| {
+                    if let tt::TokenTree::Subtree(sub) = &mut tt {
+                        if let tt::TokenTree::Leaf(tt::Leaf::Literal(lit)) = &mut sub.token_trees[2]
+                        {
+                            lit.id = id
+                        }
+                    }
+                    tt
+                }));
             }
             continue;
         }
@@ -424,8 +431,6 @@ fn to_text(&self) -> SmolStr {
     }
 }
 
-impl RawConvertor<'_> {}
-
 impl<'a> TokenConvertor for RawConvertor<'a> {
     type Token = (&'a RawToken, &'a str);
 
@@ -455,30 +460,51 @@ fn id_alloc(&mut self) -> &mut TokenIdAlloc {
     }
 }
 
-struct Convertor {
+struct Convertor<'c> {
     id_alloc: TokenIdAlloc,
     current: Option<SyntaxToken>,
-    censor: Option<TextRange>,
+    preorder: PreorderWithTokens,
+    censor: &'c FxHashSet<SyntaxNode>,
     range: TextRange,
     punct_offset: Option<(SyntaxToken, TextSize)>,
 }
 
-impl Convertor {
-    fn new(node: &SyntaxNode, global_offset: TextSize, censor: Option<TextRange>) -> Convertor {
-        let first = node.first_token();
-        let current = match censor {
-            Some(censor) => iter::successors(first, |token| token.next_token())
-                .find(|token| !censor.contains_range(token.text_range())),
-            None => first,
-        };
+impl<'c> Convertor<'c> {
+    fn new(
+        node: &SyntaxNode,
+        global_offset: TextSize,
+        censor: &'c FxHashSet<SyntaxNode>,
+    ) -> Convertor<'c> {
+        let range = node.text_range();
+        let mut preorder = node.preorder_with_tokens();
+        let first = Self::next_token(&mut preorder, censor);
         Convertor {
             id_alloc: { TokenIdAlloc { map: TokenMap::default(), global_offset, next_id: 0 } },
-            current,
-            range: node.text_range(),
+            current: first,
+            preorder,
+            range,
             censor,
             punct_offset: None,
         }
     }
+
+    fn next_token(
+        preorder: &mut PreorderWithTokens,
+        censor: &FxHashSet<SyntaxNode>,
+    ) -> Option<SyntaxToken> {
+        while let Some(ev) = preorder.next() {
+            let ele = match ev {
+                WalkEvent::Enter(ele) => ele,
+                _ => continue,
+            };
+            match ele {
+                SyntaxElement::Token(t) => return Some(t),
+                SyntaxElement::Node(node) if censor.contains(&node) => preorder.skip_subtree(),
+                SyntaxElement::Node(_) => (),
+            }
+        }
+        None
+    }
 }
 
 #[derive(Debug)]
@@ -511,7 +537,7 @@ fn to_text(&self) -> SmolStr {
     }
 }
 
-impl TokenConvertor for Convertor {
+impl TokenConvertor for Convertor<'_> {
     type Token = SynToken;
     fn convert_doc_comment(&self, token: &Self::Token) -> Option<Vec<tt::TokenTree>> {
         convert_doc_comment(token.token())
@@ -532,11 +558,7 @@ fn bump(&mut self) -> Option<(Self::Token, TextRange)> {
         if !&self.range.contains_range(curr.text_range()) {
             return None;
         }
-        self.current = match self.censor {
-            Some(censor) => iter::successors(curr.next_token(), |token| token.next_token())
-                .find(|token| !censor.contains_range(token.text_range())),
-            None => curr.next_token(),
-        };
+        self.current = Self::next_token(&mut self.preorder, self.censor);
         let token = if curr.kind().is_punct() {
             let range = curr.text_range();
             let range = TextRange::at(range.start(), TextSize::of('.'));
index 0b80c8f3ae9746500930bfc8416d0587897467ba..e6ac9754da5610b16ccb079ddac2347dc43f561a 100644 (file)
@@ -1,7 +1,7 @@
 mod expand;
 mod rule;
 
-use std::fmt::Write;
+use std::{fmt::Write, iter};
 
 use syntax::{ast, AstNode, NodeOrToken, SyntaxNode, WalkEvent};
 use test_utils::assert_eq_text;
@@ -252,27 +252,36 @@ struct Struct {
     let item = source_file.items().next().unwrap();
     let attr = item.attrs().nth(1).unwrap();
 
-    let (tt, _) =
-        syntax_node_to_token_tree_censored(item.syntax(), Some(attr.syntax().text_range()));
+    let (tt, _) = syntax_node_to_token_tree_censored(
+        item.syntax(),
+        &iter::once(attr.syntax().clone()).collect(),
+    );
     expect_test::expect![[r##"# [attr0] # [attr2] struct Struct {field : ()}"##]]
         .assert_eq(&tt.to_string());
 
     let source = r##"
+#[attr0]
 #[derive(Derive0)]
+#[attr1]
 #[derive(Derive1)]
+#[attr2]
 #[derive(Derive2)]
+#[attr3]
 struct Struct {
     field: ()
 }
 "##;
     let source_file = ast::SourceFile::parse(source).ok().unwrap();
     let item = source_file.items().next().unwrap();
-    let attr = item.attrs().nth(1).unwrap();
-
-    let (tt, _) = syntax_node_to_token_tree_censored(
-        item.syntax(),
-        Some(attr.syntax().text_range().cover_offset(0.into())),
-    );
-    expect_test::expect![[r##"# [derive (Derive2)] struct Struct {field : ()}"##]]
+    let derive_attr_index = 3;
+    let censor = item
+        .attrs()
+        .take(derive_attr_index as usize + 1)
+        .filter(|attr| attr.simple_name().as_deref() == Some("derive"))
+        .map(|it| it.syntax().clone())
+        .collect();
+
+    let (tt, _) = syntax_node_to_token_tree_censored(item.syntax(), &censor);
+    expect_test::expect![[r##"# [attr0] # [attr1] # [attr2] # [derive (Derive2)] # [attr3] struct Struct {field : ()}"##]]
         .assert_eq(&tt.to_string());
 }
index 382cc4fcc5171d4a2c8d7753d82eeaeeb7aede73..d0b07f59314339069ba3b0bd5c08c3af1f1a7dbe 100644 (file)
@@ -34,8 +34,8 @@
 mod params;
 mod paths;
 mod patterns;
-mod type_args;
-mod type_params;
+mod generic_args;
+mod generic_params;
 mod types;
 
 use crate::{
@@ -166,22 +166,16 @@ fn opt_visibility(p: &mut Parser) -> bool {
                     // struct B(pub (super::A));
                     // struct B(pub (crate::A,));
                     T![crate] | T![self] | T![super] | T![ident] if p.nth(2) != T![:] => {
-                        p.bump_any();
-                        let path_m = p.start();
-                        let path_segment_m = p.start();
-                        let name_ref_m = p.start();
-                        p.bump_any();
-                        name_ref_m.complete(p, NAME_REF);
-                        path_segment_m.complete(p, PATH_SEGMENT);
-                        path_m.complete(p, PATH);
+                        p.bump(T!['(']);
+                        paths::use_path(p);
                         p.expect(T![')']);
                     }
                     // test crate_visibility_in
                     // pub(in super::A) struct S;
                     // pub(in crate) struct S;
                     T![in] => {
-                        p.bump_any();
-                        p.bump_any();
+                        p.bump(T!['(']);
+                        p.bump(T![in]);
                         paths::use_path(p);
                         p.expect(T![')']);
                     }
@@ -189,22 +183,25 @@ fn opt_visibility(p: &mut Parser) -> bool {
                 }
             }
             m.complete(p, VISIBILITY);
+            true
         }
         // test crate_keyword_vis
         // crate fn main() { }
         // struct S { crate field: u32 }
         // struct T(crate u32);
-        //
-        // test crate_keyword_path
-        // fn foo() { crate::foo(); }
-        T![crate] if !p.nth_at(1, T![::]) => {
+        T![crate] => {
+            if p.nth_at(1, T![::]) {
+                // test crate_keyword_path
+                // fn foo() { crate::foo(); }
+                return false;
+            }
             let m = p.start();
             p.bump(T![crate]);
             m.complete(p, VISIBILITY);
+            true
         }
-        _ => return false,
+        _ => false,
     }
-    true
 }
 
 fn opt_rename(p: &mut Parser) {
index a44c5e4841f0dda34ce8f7a311c500e412de972f..80d7b09b3e1069a99a0b770573d759eb1f7c3896 100644 (file)
@@ -12,31 +12,13 @@ pub(super) fn outer_attrs(p: &mut Parser) {
     }
 }
 
-pub(super) fn meta(p: &mut Parser) {
-    let meta = p.start();
-    paths::use_path(p);
-
-    match p.current() {
-        T![=] => {
-            p.bump(T![=]);
-            if expressions::expr(p).0.is_none() {
-                p.error("expected expression");
-            }
-        }
-        T!['('] | T!['['] | T!['{'] => items::token_tree(p),
-        _ => {}
-    }
-
-    meta.complete(p, META);
-}
-
 fn attr(p: &mut Parser, inner: bool) {
-    let attr = p.start();
     assert!(p.at(T![#]));
+
+    let attr = p.start();
     p.bump(T![#]);
 
     if inner {
-        assert!(p.at(T![!]));
         p.bump(T![!]);
     }
 
@@ -51,3 +33,21 @@ fn attr(p: &mut Parser, inner: bool) {
     }
     attr.complete(p, ATTR);
 }
+
+pub(super) fn meta(p: &mut Parser) {
+    let meta = p.start();
+    paths::use_path(p);
+
+    match p.current() {
+        T![=] => {
+            p.bump(T![=]);
+            if expressions::expr(p).0.is_none() {
+                p.error("expected expression");
+            }
+        }
+        T!['('] | T!['['] | T!['{'] => items::token_tree(p),
+        _ => {}
+    }
+
+    meta.complete(p, META);
+}
index 686a6434567e7a689939e18412c93ac4e50056ce..29310b71bd8ce74f58d256c8c46556dcdcce9e98 100644 (file)
@@ -71,7 +71,7 @@ pub(super) fn stmt(p: &mut Parser, with_semi: StmtWithSemi, prefer_expr: bool) {
 
     // test block_items
     // fn a() { fn b() {} }
-    let m = match items::maybe_item(p, m) {
+    let m = match items::opt_item(p, m) {
         Ok(()) => return,
         Err(m) => m,
     };
@@ -374,7 +374,6 @@ fn lhs(p: &mut Parser, r: Restrictions) -> Option<(CompletedMarker, BlockLike)>
             //    let mut p = F{x: 5};
             //    {p}.x = 10;
             // }
-            //
             let (lhs, blocklike) = atom::atom_expr(p, r)?;
             return Some(postfix_expr(p, lhs, blocklike, !(r.prefer_stmt && blocklike.is_block())));
         }
@@ -487,7 +486,7 @@ fn method_call_expr(p: &mut Parser, lhs: CompletedMarker) -> CompletedMarker {
     let m = lhs.precede(p);
     p.bump_any();
     name_ref(p);
-    type_args::opt_generic_arg_list(p, true);
+    generic_args::opt_generic_arg_list(p, true);
     if p.at(T!['(']) {
         arg_list(p);
     }
diff --git a/crates/parser/src/grammar/generic_args.rs b/crates/parser/src/grammar/generic_args.rs
new file mode 100644 (file)
index 0000000..b47912d
--- /dev/null
@@ -0,0 +1,119 @@
+use super::*;
+
+pub(super) fn opt_generic_arg_list(p: &mut Parser, colon_colon_required: bool) {
+    let m;
+    if p.at(T![::]) && p.nth(2) == T![<] {
+        m = p.start();
+        p.bump(T![::]);
+        p.bump(T![<]);
+    } else if !colon_colon_required && p.at(T![<]) && p.nth(1) != T![=] {
+        m = p.start();
+        p.bump(T![<]);
+    } else {
+        return;
+    }
+
+    while !p.at(EOF) && !p.at(T![>]) {
+        generic_arg(p);
+        if !p.at(T![>]) && !p.expect(T![,]) {
+            break;
+        }
+    }
+    p.expect(T![>]);
+    m.complete(p, GENERIC_ARG_LIST);
+}
+
+// test generic_arg
+// type T = S<i32>;
+fn generic_arg(p: &mut Parser) {
+    match p.current() {
+        LIFETIME_IDENT => lifetime_arg(p),
+        T!['{'] | T![true] | T![false] | T![-] => const_arg(p),
+        k if k.is_literal() => const_arg(p),
+        // test associated_type_bounds
+        // fn print_all<T: Iterator<Item, Item::Item, Item::<true>, Item: Display, Item<'a> = Item>>(printables: T) {}
+        IDENT if [T![<], T![=], T![:]].contains(&p.nth(1)) => {
+            let m = p.start();
+            name_ref(p);
+            opt_generic_arg_list(p, false);
+            match p.current() {
+                // test assoc_type_eq
+                // type T = StreamingIterator<Item<'a> = &'a T>;
+                T![=] => {
+                    p.bump_any();
+                    types::type_(p);
+                    m.complete(p, ASSOC_TYPE_ARG);
+                }
+                // test assoc_type_bound
+                // type T = StreamingIterator<Item<'a>: Clone>;
+                T![:] if !p.at(T![::]) => {
+                    generic_params::bounds(p);
+                    m.complete(p, ASSOC_TYPE_ARG);
+                }
+                _ => {
+                    let m = m.complete(p, PATH_SEGMENT).precede(p).complete(p, PATH);
+                    let m = paths::type_path_for_qualifier(p, m);
+                    m.precede(p).complete(p, PATH_TYPE).precede(p).complete(p, TYPE_ARG);
+                }
+            }
+        }
+        _ => type_arg(p),
+    }
+}
+
+// test lifetime_arg
+// type T = S<'static>;
+fn lifetime_arg(p: &mut Parser) {
+    let m = p.start();
+    lifetime(p);
+    m.complete(p, LIFETIME_ARG);
+}
+
+// test const_arg
+// type T = S<92>;
+pub(super) fn const_arg(p: &mut Parser) {
+    let m = p.start();
+    match p.current() {
+        // test const_arg_block
+        // type T = S<{90 + 2}>;
+        T!['{'] => {
+            expressions::block_expr(p);
+            m.complete(p, CONST_ARG);
+        }
+        // test const_arg_literal
+        // type T = S<"hello", 0xdeadbeef>;
+        k if k.is_literal() => {
+            expressions::literal(p);
+            m.complete(p, CONST_ARG);
+        }
+        // test const_arg_bool_literal
+        // type T = S<true>;
+        T![true] | T![false] => {
+            expressions::literal(p);
+            m.complete(p, CONST_ARG);
+        }
+        // test const_arg_negative_number
+        // type T = S<-92>;
+        T![-] => {
+            let lm = p.start();
+            p.bump(T![-]);
+            expressions::literal(p);
+            lm.complete(p, PREFIX_EXPR);
+            m.complete(p, CONST_ARG);
+        }
+        // test const_arg_path
+        // struct S<const N: u32 = u32::MAX>;
+        _ => {
+            let lm = p.start();
+            paths::use_path(p);
+            lm.complete(p, PATH_EXPR);
+            m.complete(p, CONST_ARG);
+        }
+    }
+}
+
+fn type_arg(p: &mut Parser) {
+    let m = p.start();
+    types::type_(p);
+    m.complete(p, TYPE_ARG);
+}
diff --git a/crates/parser/src/grammar/generic_params.rs b/crates/parser/src/grammar/generic_params.rs
new file mode 100644 (file)
index 0000000..5414b3b
--- /dev/null
@@ -0,0 +1,220 @@
+use super::*;
+
+pub(super) fn opt_generic_param_list(p: &mut Parser) {
+    if p.at(T![<]) {
+        generic_param_list(p);
+    }
+}
+
+// test generic_param_list
+// fn f<T: Clone>() {}
+fn generic_param_list(p: &mut Parser) {
+    assert!(p.at(T![<]));
+    let m = p.start();
+    p.bump(T![<]);
+
+    while !p.at(EOF) && !p.at(T![>]) {
+        generic_param(p);
+        if !p.at(T![>]) && !p.expect(T![,]) {
+            break;
+        }
+    }
+    p.expect(T![>]);
+    m.complete(p, GENERIC_PARAM_LIST);
+}
+
+fn generic_param(p: &mut Parser) {
+    let m = p.start();
+    // test generic_param_attribute
+    // fn foo<#[lt_attr] 'a, #[t_attr] T>() {}
+    attributes::outer_attrs(p);
+    match p.current() {
+        LIFETIME_IDENT => lifetime_param(p, m),
+        IDENT => type_param(p, m),
+        T![const] => const_param(p, m),
+        _ => {
+            m.abandon(p);
+            p.err_and_bump("expected type parameter")
+        }
+    }
+}
+
+// test lifetime_param
+// fn f<'a: 'b>() {}
+fn lifetime_param(p: &mut Parser, m: Marker) {
+    assert!(p.at(LIFETIME_IDENT));
+    lifetime(p);
+    if p.at(T![:]) {
+        lifetime_bounds(p);
+    }
+    m.complete(p, LIFETIME_PARAM);
+}
+
+// test type_param
+// fn f<T: Clone>() {}
+fn type_param(p: &mut Parser, m: Marker) {
+    assert!(p.at(IDENT));
+    name(p);
+    if p.at(T![:]) {
+        bounds(p);
+    }
+    if p.at(T![=]) {
+        // test type_param_default
+        // struct S<T = i32>;
+        p.bump(T![=]);
+        types::type_(p)
+    }
+    m.complete(p, TYPE_PARAM);
+}
+
+// test const_param
+// struct S<const N: u32>;
+fn const_param(p: &mut Parser, m: Marker) {
+    p.bump(T![const]);
+    name(p);
+    if p.at(T![:]) {
+        types::ascription(p);
+    } else {
+        p.error("missing type for const parameter");
+    }
+
+    if p.at(T![=]) {
+        // test const_param_defaults
+        // struct A<const N: i32 = -1>;
+        p.bump(T![=]);
+        generic_args::const_arg(p);
+    }
+
+    m.complete(p, CONST_PARAM);
+}
+
+fn lifetime_bounds(p: &mut Parser) {
+    assert!(p.at(T![:]));
+    p.bump(T![:]);
+    while p.at(LIFETIME_IDENT) {
+        lifetime(p);
+        if !p.eat(T![+]) {
+            break;
+        }
+    }
+}
+
+// test type_param_bounds
+// struct S<T: 'a + ?Sized + (Copy)>;
+pub(super) fn bounds(p: &mut Parser) {
+    assert!(p.at(T![:]));
+    p.bump(T![:]);
+    bounds_without_colon(p);
+}
+
+pub(super) fn bounds_without_colon(p: &mut Parser) {
+    let m = p.start();
+    bounds_without_colon_m(p, m);
+}
+
+pub(super) fn bounds_without_colon_m(p: &mut Parser, marker: Marker) -> CompletedMarker {
+    while type_bound(p) {
+        if !p.eat(T![+]) {
+            break;
+        }
+    }
+    marker.complete(p, TYPE_BOUND_LIST)
+}
+
+fn type_bound(p: &mut Parser) -> bool {
+    let m = p.start();
+    let has_paren = p.eat(T!['(']);
+    p.eat(T![?]);
+    match p.current() {
+        LIFETIME_IDENT => lifetime(p),
+        T![for] => types::for_type(p, false),
+        _ if paths::is_use_path_start(p) => types::path_type_(p, false),
+        _ => {
+            m.abandon(p);
+            return false;
+        }
+    }
+    if has_paren {
+        p.expect(T![')']);
+    }
+    m.complete(p, TYPE_BOUND);
+
+    true
+}
+
+// test where_clause
+// fn foo()
+// where
+//    'a: 'b + 'c,
+//    T: Clone + Copy + 'static,
+//    Iterator::Item: 'a,
+//    <T as Iterator>::Item: 'a
+// {}
+pub(super) fn opt_where_clause(p: &mut Parser) {
+    if !p.at(T![where]) {
+        return;
+    }
+    let m = p.start();
+    p.bump(T![where]);
+
+    while is_where_predicate(p) {
+        where_predicate(p);
+
+        let comma = p.eat(T![,]);
+
+        match p.current() {
+            T!['{'] | T![;] | T![=] => break,
+            _ => (),
+        }
+
+        if !comma {
+            p.error("expected comma");
+        }
+    }
+
+    m.complete(p, WHERE_CLAUSE);
+
+    fn is_where_predicate(p: &mut Parser) -> bool {
+        match p.current() {
+            LIFETIME_IDENT => true,
+            T![impl] => false,
+            token => types::TYPE_FIRST.contains(token),
+        }
+    }
+}
+
+fn where_predicate(p: &mut Parser) {
+    let m = p.start();
+    match p.current() {
+        LIFETIME_IDENT => {
+            lifetime(p);
+            if p.at(T![:]) {
+                bounds(p);
+            } else {
+                p.error("expected colon");
+            }
+        }
+        T![impl] => {
+            p.error("expected lifetime or type");
+        }
+        _ => {
+            if p.at(T![for]) {
+                // test where_pred_for
+                // fn for_trait<F>()
+                // where
+                //    for<'a> F: Fn(&'a str)
+                // { }
+                types::for_binder(p);
+            }
+
+            types::type_(p);
+
+            if p.at(T![:]) {
+                bounds(p);
+            } else {
+                p.error("expected colon");
+            }
+        }
+    }
+    m.complete(p, WHERE_PRED);
+}
index 3421078bba6a3914b6e5f2325e9282f141234691..517da6e95c491da1cb17365f2d08ad01bca66070 100644 (file)
@@ -19,7 +19,7 @@
 // struct S;
 pub(super) fn mod_contents(p: &mut Parser, stop_on_r_curly: bool) {
     attributes::inner_attrs(p);
-    while !(stop_on_r_curly && p.at(T!['}']) || p.at(EOF)) {
+    while !p.at(EOF) && !(p.at(T!['}']) && stop_on_r_curly) {
         item_or_macro(p, stop_on_r_curly)
     }
 }
@@ -44,7 +44,8 @@ pub(super) fn mod_contents(p: &mut Parser, stop_on_r_curly: bool) {
 pub(super) fn item_or_macro(p: &mut Parser, stop_on_r_curly: bool) {
     let m = p.start();
     attributes::outer_attrs(p);
-    let m = match maybe_item(p, m) {
+
+    let m = match opt_item(p, m) {
         Ok(()) => {
             if p.at(T![;]) {
                 p.err_and_bump(
@@ -56,6 +57,7 @@ pub(super) fn item_or_macro(p: &mut Parser, stop_on_r_curly: bool) {
         }
         Err(m) => m,
     };
+
     if paths::is_use_path_start(p) {
         match macro_call(p) {
             BlockLike::Block => (),
@@ -64,30 +66,30 @@ pub(super) fn item_or_macro(p: &mut Parser, stop_on_r_curly: bool) {
             }
         }
         m.complete(p, MACRO_CALL);
-    } else {
-        m.abandon(p);
-        if p.at(T!['{']) {
-            error_block(p, "expected an item");
-        } else if p.at(T!['}']) && !stop_on_r_curly {
+        return;
+    }
+
+    m.abandon(p);
+    match p.current() {
+        T!['{'] => error_block(p, "expected an item"),
+        T!['}'] if !stop_on_r_curly => {
             let e = p.start();
             p.error("unmatched `}`");
             p.bump(T!['}']);
             e.complete(p, ERROR);
-        } else if !p.at(EOF) && !p.at(T!['}']) {
-            p.err_and_bump("expected an item");
-        } else {
-            p.error("expected an item");
         }
+        EOF | T!['}'] => p.error("expected an item"),
+        _ => p.err_and_bump("expected an item"),
     }
 }
 
 /// Try to parse an item, completing `m` in case of success.
-pub(super) fn maybe_item(p: &mut Parser, m: Marker) -> Result<(), Marker> {
+pub(super) fn opt_item(p: &mut Parser, m: Marker) -> Result<(), Marker> {
     // test_err pub_expr
     // fn foo() { pub 92; }
     let has_visibility = opt_visibility(p);
 
-    let m = match items_without_modifiers(p, m) {
+    let m = match opt_item_without_modifiers(p, m) {
         Ok(()) => return Ok(()),
         Err(m) => m,
     };
@@ -133,29 +135,26 @@ pub(super) fn maybe_item(p: &mut Parser, m: Marker) -> Result<(), Marker> {
                 p.bump_remap(T![default]);
                 has_mods = true;
             }
-            T![unsafe] => {
-                // test default_unsafe_item
-                // default unsafe impl T for Foo {
-                //     default unsafe fn foo() {}
-                // }
-                if matches!(p.nth(2), T![impl] | T![fn]) {
-                    p.bump_remap(T![default]);
-                    p.bump(T![unsafe]);
-                    has_mods = true;
-                }
+            // test default_unsafe_item
+            // default unsafe impl T for Foo {
+            //     default unsafe fn foo() {}
+            // }
+            T![unsafe] if matches!(p.nth(2), T![impl] | T![fn]) => {
+                p.bump_remap(T![default]);
+                p.bump(T![unsafe]);
+                has_mods = true;
             }
+            // test default_async_fn
+            // impl T for Foo {
+            //     default async fn foo() {}
+            // }
             T![async] => {
-                // test default_async_fn
-                // impl T for Foo {
-                //     default async fn foo() {}
-                // }
-
-                // test default_async_unsafe_fn
-                // impl T for Foo {
-                //     default async unsafe fn foo() {}
-                // }
                 let mut maybe_fn = p.nth(2);
                 let is_unsafe = if matches!(maybe_fn, T![unsafe]) {
+                    // test default_async_unsafe_fn
+                    // impl T for Foo {
+                    //     default async unsafe fn foo() {}
+                    // }
                     maybe_fn = p.nth(3);
                     true
                 } else {
@@ -184,34 +183,14 @@ pub(super) fn maybe_item(p: &mut Parser, m: Marker) -> Result<(), Marker> {
 
     // items
     match p.current() {
-        // test fn
-        // fn foo() {}
-        T![fn] => {
-            fn_(p);
-            m.complete(p, FN);
-        }
-
-        // test trait
-        // trait T {}
-        T![trait] => {
-            traits::trait_(p);
-            m.complete(p, TRAIT);
-        }
+        T![fn] => fn_(p, m),
 
-        T![const] if p.nth(1) != T!['{'] => {
-            consts::konst(p, m);
-        }
+        T![const] if p.nth(1) != T!['{'] => consts::konst(p, m),
 
-        // test impl
-        // impl T for S {}
-        T![impl] => {
-            traits::impl_(p);
-            m.complete(p, IMPL);
-        }
+        T![trait] => traits::trait_(p, m),
+        T![impl] => traits::impl_(p, m),
 
-        T![type] => {
-            type_alias(p, m);
-        }
+        T![type] => type_alias(p, m),
 
         // test extern_block
         // unsafe extern "C" {}
@@ -235,48 +214,20 @@ pub(super) fn maybe_item(p: &mut Parser, m: Marker) -> Result<(), Marker> {
     Ok(())
 }
 
-fn items_without_modifiers(p: &mut Parser, m: Marker) -> Result<(), Marker> {
+fn opt_item_without_modifiers(p: &mut Parser, m: Marker) -> Result<(), Marker> {
     let la = p.nth(1);
     match p.current() {
-        // test extern_crate
-        // extern crate foo;
         T![extern] if la == T![crate] => extern_crate(p, m),
         T![use] => use_item::use_(p, m),
         T![mod] => mod_item(p, m),
 
         T![type] => type_alias(p, m),
-
-        T![struct] => {
-            // test struct_items
-            // struct Foo;
-            // struct Foo {}
-            // struct Foo();
-            // struct Foo(String, usize);
-            // struct Foo {
-            //     a: i32,
-            //     b: f32,
-            // }
-            adt::strukt(p, m);
-        }
+        T![struct] => adt::strukt(p, m),
         T![enum] => adt::enum_(p, m),
-        IDENT if p.at_contextual_kw("union") && p.nth(1) == IDENT => {
-            // test union_items
-            // union Foo {}
-            // union Foo {
-            //     a: i32,
-            //     b: f32,
-            // }
-            adt::union(p, m);
-        }
+        IDENT if p.at_contextual_kw("union") && p.nth(1) == IDENT => adt::union(p, m),
 
-        // test pub_macro_def
-        // pub macro m($:ident) {}
-        T![macro] => {
-            macro_def(p, m);
-        }
-        IDENT if p.at_contextual_kw("macro_rules") && p.nth(1) == BANG => {
-            macro_rules(p, m);
-        }
+        T![macro] => macro_def(p, m),
+        IDENT if p.at_contextual_kw("macro_rules") && p.nth(1) == BANG => macro_rules(p, m),
 
         T![const] if (la == IDENT || la == T![_] || la == T![mut]) => consts::konst(p, m),
         T![static] => consts::static_(p, m),
@@ -286,14 +237,15 @@ fn items_without_modifiers(p: &mut Parser, m: Marker) -> Result<(), Marker> {
     Ok(())
 }
 
+// test extern_crate
+// extern crate foo;
 fn extern_crate(p: &mut Parser, m: Marker) {
-    assert!(p.at(T![extern]));
     p.bump(T![extern]);
-
-    assert!(p.at(T![crate]));
     p.bump(T![crate]);
 
     if p.at(T![self]) {
+        // test extern_crate_self
+        // extern crate self;
         let m = p.start();
         p.bump(T![self]);
         m.complete(p, NAME_REF);
@@ -301,71 +253,46 @@ fn extern_crate(p: &mut Parser, m: Marker) {
         name_ref(p);
     }
 
+    // test extern_crate_rename
+    // extern crate foo as bar;
     opt_rename(p);
     p.expect(T![;]);
     m.complete(p, EXTERN_CRATE);
 }
 
-pub(crate) fn extern_item_list(p: &mut Parser) {
-    assert!(p.at(T!['{']));
-    let m = p.start();
-    p.bump(T!['{']);
-    mod_contents(p, true);
-    p.expect(T!['}']);
-    m.complete(p, EXTERN_ITEM_LIST);
-}
-
-fn fn_(p: &mut Parser) {
-    assert!(p.at(T![fn]));
-    p.bump(T![fn]);
-
-    name_r(p, ITEM_RECOVERY_SET);
-    // test function_type_params
-    // fn foo<T: Clone + Copy>(){}
-    type_params::opt_generic_param_list(p);
-
-    if p.at(T!['(']) {
-        params::param_list_fn_def(p);
-    } else {
-        p.error("expected function arguments");
-    }
-    // test function_ret_type
-    // fn foo() {}
-    // fn bar() -> () {}
-    opt_ret_type(p);
-
-    // test function_where_clause
-    // fn foo<T>() where T: Copy {}
-    type_params::opt_where_clause(p);
-
-    // test fn_decl
-    // trait T { fn foo(); }
-    if p.at(T![;]) {
-        p.bump(T![;]);
-    } else {
-        expressions::block_expr(p)
+// test mod_item
+// mod a;
+pub(crate) fn mod_item(p: &mut Parser, m: Marker) {
+    p.bump(T![mod]);
+    name(p);
+    if p.at(T!['{']) {
+        // test mod_item_curly
+        // mod b { }
+        item_list(p);
+    } else if !p.eat(T![;]) {
+        p.error("expected `;` or `{`");
     }
+    m.complete(p, MODULE);
 }
 
-// test type_item
+// test type_alias
 // type Foo = Bar;
 fn type_alias(p: &mut Parser, m: Marker) {
-    assert!(p.at(T![type]));
     p.bump(T![type]);
 
     name(p);
 
     // test type_item_type_params
     // type Result<T> = ();
-    type_params::opt_generic_param_list(p);
+    generic_params::opt_generic_param_list(p);
 
     if p.at(T![:]) {
-        type_params::bounds(p);
+        generic_params::bounds(p);
     }
 
     // test type_item_where_clause
     // type Foo where Foo: Copy = ();
-    type_params::opt_where_clause(p);
+    generic_params::opt_where_clause(p);
     if p.eat(T![=]) {
         types::type_(p);
     }
@@ -373,19 +300,6 @@ fn type_alias(p: &mut Parser, m: Marker) {
     m.complete(p, TYPE_ALIAS);
 }
 
-pub(crate) fn mod_item(p: &mut Parser, m: Marker) {
-    assert!(p.at(T![mod]));
-    p.bump(T![mod]);
-
-    name(p);
-    if p.at(T!['{']) {
-        item_list(p);
-    } else if !p.eat(T![;]) {
-        p.error("expected `;` or `{`");
-    }
-    m.complete(p, MODULE);
-}
-
 pub(crate) fn item_list(p: &mut Parser) {
     assert!(p.at(T!['{']));
     let m = p.start();
@@ -395,6 +309,15 @@ pub(crate) fn item_list(p: &mut Parser) {
     m.complete(p, ITEM_LIST);
 }
 
+pub(crate) fn extern_item_list(p: &mut Parser) {
+    assert!(p.at(T!['{']));
+    let m = p.start();
+    p.bump(T!['{']);
+    mod_contents(p, true);
+    p.expect(T!['}']);
+    m.complete(p, EXTERN_ITEM_LIST);
+}
+
 fn macro_rules(p: &mut Parser, m: Marker) {
     assert!(p.at_contextual_kw("macro_rules"));
     p.bump_remap(T![macro_rules]);
@@ -429,16 +352,15 @@ fn macro_rules(p: &mut Parser, m: Marker) {
 }
 
 // test macro_def
-// macro m { ($i:ident) => {} }
 // macro m($i:ident) {}
 fn macro_def(p: &mut Parser, m: Marker) {
     p.expect(T![macro]);
     name_r(p, ITEM_RECOVERY_SET);
     if p.at(T!['{']) {
+        // test macro_def_curly
+        // macro m { ($i:ident) => {} }
         token_tree(p);
-    } else if !p.at(T!['(']) {
-        p.error("unmatched `(`");
-    } else {
+    } else if p.at(T!['(']) {
         let m = p.start();
         token_tree(p);
         match p.current() {
@@ -446,11 +368,47 @@ fn macro_def(p: &mut Parser, m: Marker) {
             _ => p.error("expected `{`, `[`, `(`"),
         }
         m.complete(p, TOKEN_TREE);
+    } else {
+        p.error("unmatched `(`");
     }
 
     m.complete(p, MACRO_DEF);
 }
 
+// test fn
+// fn foo() {}
+fn fn_(p: &mut Parser, m: Marker) {
+    p.bump(T![fn]);
+
+    name_r(p, ITEM_RECOVERY_SET);
+    // test function_type_params
+    // fn foo<T: Clone + Copy>(){}
+    generic_params::opt_generic_param_list(p);
+
+    if p.at(T!['(']) {
+        params::param_list_fn_def(p);
+    } else {
+        p.error("expected function arguments");
+    }
+    // test function_ret_type
+    // fn foo() {}
+    // fn bar() -> () {}
+    opt_ret_type(p);
+
+    // test function_where_clause
+    // fn foo<T>() where T: Copy {}
+    generic_params::opt_where_clause(p);
+
+    if p.at(T![;]) {
+        // test fn_decl
+        // trait T { fn foo(); }
+        p.bump(T![;]);
+    } else {
+        expressions::block_expr(p)
+    }
+    m.complete(p, FN);
+}
+
 fn macro_call(p: &mut Parser) -> BlockLike {
     assert!(paths::is_use_path_start(p));
     paths::use_path(p);
index 386d3806c32e5f06fdf6b72c8ddbfe5631e4db69..e4b1116958a9b43ffee6b30ef6c20909490faeb2 100644 (file)
@@ -1,27 +1,28 @@
 use super::*;
 
+// test struct_item
+// struct S {}
 pub(super) fn strukt(p: &mut Parser, m: Marker) {
-    assert!(p.at(T![struct]));
     p.bump(T![struct]);
-    struct_or_union(p, m, T![struct], STRUCT);
+    struct_or_union(p, m, true);
 }
 
+// test union_item
+// struct U { i: i32, f: f32 }
 pub(super) fn union(p: &mut Parser, m: Marker) {
     assert!(p.at_contextual_kw("union"));
     p.bump_remap(T![union]);
-    struct_or_union(p, m, T![union], UNION);
+    struct_or_union(p, m, false);
 }
 
-fn struct_or_union(p: &mut Parser, m: Marker, kw: SyntaxKind, def: SyntaxKind) {
+fn struct_or_union(p: &mut Parser, m: Marker, is_struct: bool) {
     name_r(p, ITEM_RECOVERY_SET);
-    type_params::opt_generic_param_list(p);
+    generic_params::opt_generic_param_list(p);
     match p.current() {
         T![where] => {
-            type_params::opt_where_clause(p);
+            generic_params::opt_where_clause(p);
             match p.current() {
-                T![;] => {
-                    p.bump(T![;]);
-                }
+                T![;] => p.bump(T![;]),
                 T!['{'] => record_field_list(p),
                 _ => {
                     //FIXME: special case `(` error message
@@ -29,34 +30,31 @@ fn struct_or_union(p: &mut Parser, m: Marker, kw: SyntaxKind, def: SyntaxKind) {
                 }
             }
         }
-        T![;] if kw == T![struct] => {
+        T!['{'] => record_field_list(p),
+        // test unit_struct
+        // struct S;
+        T![;] if is_struct => {
             p.bump(T![;]);
         }
-        T!['{'] => record_field_list(p),
-        T!['('] if kw == T![struct] => {
+        // test tuple_struct
+        // struct S(String, usize);
+        T!['('] if is_struct => {
             tuple_field_list(p);
             // test tuple_struct_where
-            // struct Test<T>(T) where T: Clone;
-            // struct Test<T>(T);
-            type_params::opt_where_clause(p);
+            // struct S<T>(T) where T: Clone;
+            generic_params::opt_where_clause(p);
             p.expect(T![;]);
         }
-        _ if kw == T![struct] => {
-            p.error("expected `;`, `{`, or `(`");
-        }
-        _ => {
-            p.error("expected `{`");
-        }
+        _ => p.error(if is_struct { "expected `;`, `{`, or `(`" } else { "expected `{`" }),
     }
-    m.complete(p, def);
+    m.complete(p, if is_struct { STRUCT } else { UNION });
 }
 
 pub(super) fn enum_(p: &mut Parser, m: Marker) {
-    assert!(p.at(T![enum]));
     p.bump(T![enum]);
     name_r(p, ITEM_RECOVERY_SET);
-    type_params::opt_generic_param_list(p);
-    type_params::opt_where_clause(p);
+    generic_params::opt_generic_param_list(p);
+    generic_params::opt_where_clause(p);
     if p.at(T!['{']) {
         variant_list(p);
     } else {
@@ -74,7 +72,16 @@ pub(crate) fn variant_list(p: &mut Parser) {
             error_block(p, "expected enum variant");
             continue;
         }
-        let var = p.start();
+        variant(p);
+        if !p.at(T!['}']) {
+            p.expect(T![,]);
+        }
+    }
+    p.expect(T!['}']);
+    m.complete(p, VARIANT_LIST);
+
+    fn variant(p: &mut Parser) {
+        let m = p.start();
         attributes::outer_attrs(p);
         if p.at(IDENT) {
             name(p);
@@ -89,19 +96,16 @@ pub(crate) fn variant_list(p: &mut Parser) {
             if p.eat(T![=]) {
                 expressions::expr(p);
             }
-            var.complete(p, VARIANT);
+            m.complete(p, VARIANT);
         } else {
-            var.abandon(p);
+            m.abandon(p);
             p.err_and_bump("expected enum variant");
         }
-        if !p.at(T!['}']) {
-            p.expect(T![,]);
-        }
     }
-    p.expect(T!['}']);
-    m.complete(p, VARIANT_LIST);
 }
 
+// test record_field_list
+// struct S { a: i32, b: f32 }
 pub(crate) fn record_field_list(p: &mut Parser) {
     assert!(p.at(T!['{']));
     let m = p.start();
@@ -111,7 +115,7 @@ pub(crate) fn record_field_list(p: &mut Parser) {
             error_block(p, "expected field");
             continue;
         }
-        record_field_def(p);
+        record_field(p);
         if !p.at(T!['}']) {
             p.expect(T![,]);
         }
@@ -119,13 +123,10 @@ pub(crate) fn record_field_list(p: &mut Parser) {
     p.expect(T!['}']);
     m.complete(p, RECORD_FIELD_LIST);
 
-    fn record_field_def(p: &mut Parser) {
+    fn record_field(p: &mut Parser) {
         let m = p.start();
         // test record_field_attrs
-        // struct S {
-        //     #[serde(with = "url_serde")]
-        //     pub uri: Uri,
-        // }
+        // struct S { #[attr] f: f32 }
         attributes::outer_attrs(p);
         opt_visibility(p);
         if p.at(IDENT) {
@@ -143,20 +144,11 @@ fn record_field_def(p: &mut Parser) {
 fn tuple_field_list(p: &mut Parser) {
     assert!(p.at(T!['(']));
     let m = p.start();
-    if !p.expect(T!['(']) {
-        return;
-    }
+    p.bump(T!['(']);
     while !p.at(T![')']) && !p.at(EOF) {
         let m = p.start();
         // test tuple_field_attrs
-        // struct S (
-        //     #[serde(with = "url_serde")]
-        //     pub Uri,
-        // );
-        //
-        // enum S {
-        //     Uri(#[serde(with = "url_serde")] Uri),
-        // }
+        // struct S (#[attr] f32);
         attributes::outer_attrs(p);
         opt_visibility(p);
         if !p.at_ts(types::TYPE_FIRST) {
index 1b317dd84a94b76a54962c7c43772c432214e487..93ba7d05fe7bfe050973221cd5a336dc40cd65ef 100644 (file)
@@ -1,26 +1,29 @@
 use super::*;
 
-pub(super) fn static_(p: &mut Parser, m: Marker) {
-    const_or_static(p, m, T![static], STATIC)
+// test const_item
+// const C: u32 = 92;
+pub(super) fn konst(p: &mut Parser, m: Marker) {
+    p.bump(T![const]);
+    const_or_static(p, m, true)
 }
 
-pub(super) fn konst(p: &mut Parser, m: Marker) {
-    const_or_static(p, m, T![const], CONST)
+pub(super) fn static_(p: &mut Parser, m: Marker) {
+    p.bump(T![static]);
+    const_or_static(p, m, false)
 }
 
-fn const_or_static(p: &mut Parser, m: Marker, kw: SyntaxKind, def: SyntaxKind) {
-    assert!(p.at(kw));
-    p.bump(kw);
+fn const_or_static(p: &mut Parser, m: Marker, is_const: bool) {
     p.eat(T![mut]);
 
-    // Allow `_` in place of an identifier in a `const`.
-    let is_const_underscore = kw == T![const] && p.eat(T![_]);
-    if !is_const_underscore {
+    if is_const && p.eat(T![_]) {
+        // test anonymous_const
+        // const _: u32 = 0;
+    } else {
+        // test_err anonymous_static
+        // static _: i32 = 5;
         name(p);
     }
 
-    // test_err static_underscore
-    // static _: i32 = 5;
     if p.at(T![:]) {
         types::ascription(p);
     } else {
@@ -30,5 +33,5 @@ fn const_or_static(p: &mut Parser, m: Marker, kw: SyntaxKind, def: SyntaxKind) {
         expressions::expr(p);
     }
     p.expect(T![;]);
-    m.complete(p, def);
+    m.complete(p, if is_const { CONST } else { STATIC });
 }
index 74f11b45a18dd7a6613372dd7f9f7129c9806a1e..d6bb3b9b621fc7b90a898a22e76208807de5eca1 100644 (file)
@@ -1,66 +1,79 @@
 use super::*;
 
 // test trait_item
-// trait T<U>: Hash + Clone where U: Copy {}
-// trait X<U: Debug + Display>: Hash + Clone where U: Copy {}
-pub(super) fn trait_(p: &mut Parser) {
-    assert!(p.at(T![trait]));
+// trait T { fn new() -> Self; }
+pub(super) fn trait_(p: &mut Parser, m: Marker) {
     p.bump(T![trait]);
     name_r(p, ITEM_RECOVERY_SET);
-    type_params::opt_generic_param_list(p);
-    // test trait_alias
-    // trait Z<U> = T<U>;
-    // trait Z<U> = T<U> where U: Copy;
-    // trait Z<U> = where Self: T<U>;
+
+    // test trait_item_generic_params
+    // trait X<U: Debug + Display> {}
+    generic_params::opt_generic_param_list(p);
+
     if p.eat(T![=]) {
-        type_params::bounds_without_colon(p);
-        type_params::opt_where_clause(p);
+        // test trait_alias
+        // trait Z<U> = T<U>;
+        generic_params::bounds_without_colon(p);
+
+        // test trait_alias_where_clause
+        // trait Z<U> = T<U> where U: Copy;
+        // trait Z<U> = where Self: T<U>;
+        generic_params::opt_where_clause(p);
         p.expect(T![;]);
+        m.complete(p, TRAIT);
         return;
     }
+
     if p.at(T![:]) {
-        type_params::bounds(p);
+        // test trait_item_bounds
+        // trait T: Hash + Clone {}
+        generic_params::bounds(p);
     }
-    type_params::opt_where_clause(p);
+
+    // test trait_item_where_clause
+    // trait T where Self: Copy {}
+    generic_params::opt_where_clause(p);
+
     if p.at(T!['{']) {
         assoc_item_list(p);
     } else {
         p.error("expected `{`");
     }
+    m.complete(p, TRAIT);
 }
 
-// test impl_def
-// impl Foo {}
-pub(super) fn impl_(p: &mut Parser) {
-    assert!(p.at(T![impl]));
+// test impl_item
+// impl S {}
+pub(super) fn impl_(p: &mut Parser, m: Marker) {
     p.bump(T![impl]);
-    if choose_type_params_over_qpath(p) {
-        type_params::opt_generic_param_list(p);
+    if p.at(T![<]) && not_a_qualified_path(p) {
+        generic_params::opt_generic_param_list(p);
     }
 
-    // test impl_def_const
-    // impl const Send for X {}
+    // test impl_item_const
+    // impl const Send for S {}
     p.eat(T![const]);
 
     // FIXME: never type
     // impl ! {}
 
-    // test impl_def_neg
-    // impl !Send for X {}
+    // test impl_item_neg
+    // impl !Send for S {}
     p.eat(T![!]);
     impl_type(p);
     if p.eat(T![for]) {
         impl_type(p);
     }
-    type_params::opt_where_clause(p);
+    generic_params::opt_where_clause(p);
     if p.at(T!['{']) {
         assoc_item_list(p);
     } else {
         p.error("expected `{`");
     }
+    m.complete(p, IMPL);
 }
 
-// test impl_item_list
+// test assoc_item_list
 // impl F {
 //     type A = i32;
 //     const B: i32 = 92;
@@ -69,14 +82,11 @@ pub(super) fn impl_(p: &mut Parser) {
 // }
 pub(crate) fn assoc_item_list(p: &mut Parser) {
     assert!(p.at(T!['{']));
+
     let m = p.start();
     p.bump(T!['{']);
-    // test impl_inner_attributes
-    // enum F{}
-    // impl F {
-    //      //! This is a doc comment
-    //      #![doc("This is also a doc comment")]
-    // }
+    // test assoc_item_list_inner_attrs
+    // impl S { #![attr] }
     attributes::inner_attrs(p);
 
     while !p.at(EOF) && !p.at(T!['}']) {
@@ -92,7 +102,7 @@ pub(crate) fn assoc_item_list(p: &mut Parser) {
 
 // test impl_type_params
 // impl<const N: u32> Bar<N> {}
-fn choose_type_params_over_qpath(p: &Parser) -> bool {
+fn not_a_qualified_path(p: &Parser) -> bool {
     // There's an ambiguity between generic parameters and qualified paths in impls.
     // If we see `<` it may start both, so we have to inspect some following tokens.
     // The following combinations can only start generics,
@@ -109,9 +119,6 @@ fn choose_type_params_over_qpath(p: &Parser) -> bool {
     // we disambiguate it in favor of generics (`impl<T> ::absolute::Path<T> { ... }`)
     // because this is what almost always expected in practice, qualified paths in impls
     // (`impl <Type>::AssocTy { ... }`) aren't even allowed by type checker at the moment.
-    if !p.at(T![<]) {
-        return false;
-    }
     if p.nth(1) == T![#] || p.nth(1) == T![>] || p.nth(1) == T![const] {
         return true;
     }
index 2339d0c69cc3688a97984d0704388fc34f07d62d..ffb147b1fd2a72b8cbf1be7f78746b5f9b4fe623 100644 (file)
@@ -1,99 +1,60 @@
 use super::*;
 
+// test use_item
+// use std::collections;
 pub(super) fn use_(p: &mut Parser, m: Marker) {
-    assert!(p.at(T![use]));
     p.bump(T![use]);
     use_tree(p, true);
     p.expect(T![;]);
     m.complete(p, USE);
 }
 
-/// Parse a use 'tree', such as `some::path` in `use some::path;`
-/// Note that this is called both by `use_item` and `use_tree_list`,
-/// so handles both `some::path::{inner::path}` and `inner::path` in
-/// `use some::path::{inner::path};`
+// test use_tree
+// use outer::tree::{inner::tree};
 fn use_tree(p: &mut Parser, top_level: bool) {
     let m = p.start();
     match p.current() {
-        // Finish the use_tree for cases of e.g.
-        // `use some::path::{self, *};` or `use *;`
-        // This does not handle cases such as `use some::path::*`
-        // N.B. in Rust 2015 `use *;` imports all from crate root
-        // however in Rust 2018 `use *;` errors: ('cannot glob-import all possible crates')
-        // FIXME: Add this error (if not out of scope)
-
-        // test use_star
+        // test use_tree_star
         // use *;
-        // use ::*;
-        // use some::path::{*};
-        // use some::path::{::*};
+        // use std::{*};
         T![*] => p.bump(T![*]),
+        // test use_tree_abs_star
+        // use ::*;
+        // use std::{::*};
         T![:] if p.at(T![::]) && p.nth(2) == T![*] => {
-            // Parse `use ::*;`, which imports all from the crate root in Rust 2015
-            // This is invalid inside a use_tree_list, (e.g. `use some::path::{::*}`)
-            // but still parses and errors later: ('crate root in paths can only be used in start position')
-            // FIXME: Add this error (if not out of scope)
-            // In Rust 2018, it is always invalid (see above)
             p.bump(T![::]);
             p.bump(T![*]);
         }
-        // Open a use tree list
-        // Handles cases such as `use {some::path};` or `{inner::path}` in
-        // `use some::path::{{inner::path}, other::path}`
-
-        // test use_tree_list
-        // use {crate::path::from::root, or::path::from::crate_name}; // Rust 2018 (with a crate named `or`)
-        // use {path::from::root}; // Rust 2015
-        // use ::{some::arbitrary::path}; // Rust 2015
-        // use ::{{{root::export}}}; // Nonsensical but perfectly legal nesting
-        T!['{'] => {
-            use_tree_list(p);
-        }
+        T!['{'] => use_tree_list(p),
         T![:] if p.at(T![::]) && p.nth(2) == T!['{'] => {
             p.bump(T![::]);
             use_tree_list(p);
         }
-        // Parse a 'standard' path.
-        // Also handles aliases (e.g. `use something as something_else`)
 
-        // test use_path
-        // use ::crate_name; // Rust 2018 - All flavours
-        // use crate_name; // Rust 2018 - Anchored paths
-        // use item_in_scope_or_crate_name; // Rust 2018 - Uniform Paths
+        // test use_tree_path
+        // use ::std;
+        // use std::collections;
         //
-        // use self::module::Item;
-        // use crate::Item;
-        // use self::some::Struct;
-        // use crate_name::some_item;
+        // use self::m;
+        // use super::m;
+        // use crate::m;
         _ if paths::is_use_path_start(p) => {
             paths::use_path(p);
             match p.current() {
-                T![as] => {
-                    // test use_alias
-                    // use some::path as some_name;
-                    // use some::{
-                    //  other::path as some_other_name,
-                    //  different::path as different_name,
-                    //  yet::another::path,
-                    //  running::out::of::synonyms::for_::different::*
-                    // };
-                    // use Trait as _;
-                    opt_rename(p);
-                }
+                // test use_tree_alias
+                // use std as stdlib;
+                // use Trait as _;
+                T![as] => opt_rename(p),
                 T![:] if p.at(T![::]) => {
                     p.bump(T![::]);
                     match p.current() {
-                        T![*] => {
-                            p.bump(T![*]);
-                        }
-                        // test use_tree_list_after_path
-                        // use crate::{Item};
-                        // use self::{Item};
+                        // test use_tree_path_star
+                        // use std::*;
+                        T![*] => p.bump(T![*]),
+                        // test use_tree_path_use_tree
+                        // use std::{collections};
                         T!['{'] => use_tree_list(p),
-                        _ => {
-                            // is this unreachable?
-                            p.error("expected `{` or `*`");
-                        }
+                        _ => p.error("expected `{` or `*`"),
                     }
                 }
                 _ => (),
@@ -115,6 +76,8 @@ fn use_tree(p: &mut Parser, top_level: bool) {
     m.complete(p, USE_TREE);
 }
 
+// test use_tree_list
+// use {a, b, c};
 pub(crate) fn use_tree_list(p: &mut Parser) {
     assert!(p.at(T!['{']));
     let m = p.start();
index c4e4f010ffbeb44f781f9632dc2dd1d8e3b71d12..05a52c984a37fee2d34746085dca54c3e7e43c29 100644 (file)
@@ -27,7 +27,7 @@ pub(super) fn expr_path(p: &mut Parser) {
     path(p, Mode::Expr)
 }
 
-pub(crate) fn type_path_for_qualifier(p: &mut Parser, qual: CompletedMarker) {
+pub(crate) fn type_path_for_qualifier(p: &mut Parser, qual: CompletedMarker) -> CompletedMarker {
     path_for_qualifier(p, Mode::Type, qual)
 }
 
@@ -42,10 +42,10 @@ fn path(p: &mut Parser, mode: Mode) {
     let path = p.start();
     path_segment(p, mode, true);
     let qual = path.complete(p, PATH);
-    path_for_qualifier(p, mode, qual)
+    path_for_qualifier(p, mode, qual);
 }
 
-fn path_for_qualifier(p: &mut Parser, mode: Mode, mut qual: CompletedMarker) {
+fn path_for_qualifier(p: &mut Parser, mode: Mode, mut qual: CompletedMarker) -> CompletedMarker {
     loop {
         let use_tree = matches!(p.nth(2), T![*] | T!['{']);
         if p.at(T![::]) && !use_tree {
@@ -55,7 +55,7 @@ fn path_for_qualifier(p: &mut Parser, mode: Mode, mut qual: CompletedMarker) {
             let path = path.complete(p, PATH);
             qual = path;
         } else {
-            break;
+            return qual;
         }
     }
 }
@@ -117,9 +117,9 @@ fn opt_path_type_args(p: &mut Parser, mode: Mode) {
                 params::param_list_fn_trait(p);
                 opt_ret_type(p);
             } else {
-                type_args::opt_generic_arg_list(p, false)
+                generic_args::opt_generic_arg_list(p, false)
             }
         }
-        Mode::Expr => type_args::opt_generic_arg_list(p, true),
+        Mode::Expr => generic_args::opt_generic_arg_list(p, true),
     }
 }
index fed5cca512d0e576671d04165ba17b98cdbfd0f3..81e2051abb58134638954dd0cb8909b404ce3d0e 100644 (file)
@@ -17,7 +17,7 @@ pub(crate) fn pattern(p: &mut Parser) {
     pattern_r(p, PAT_RECOVERY_SET);
 }
 
-/// Parses a pattern list separated by pipes `|`
+/// Parses a pattern list separated by pipes `|`.
 pub(super) fn pattern_top(p: &mut Parser) {
     pattern_top_r(p, PAT_RECOVERY_SET)
 }
@@ -27,14 +27,15 @@ pub(crate) fn pattern_single(p: &mut Parser) {
 }
 
 /// Parses a pattern list separated by pipes `|`
-/// using the given `recovery_set`
+/// using the given `recovery_set`.
 pub(super) fn pattern_top_r(p: &mut Parser, recovery_set: TokenSet) {
     p.eat(T![|]);
     pattern_r(p, recovery_set);
 }
 
 /// Parses a pattern list separated by pipes `|`, with no leading `|`,using the
-/// given `recovery_set`
+/// given `recovery_set`.
+
 // test or_pattern
 // fn main() {
 //     match () {
diff --git a/crates/parser/src/grammar/type_args.rs b/crates/parser/src/grammar/type_args.rs
deleted file mode 100644 (file)
index f09a28c..0000000
+++ /dev/null
@@ -1,139 +0,0 @@
-use super::*;
-
-pub(super) fn opt_generic_arg_list(p: &mut Parser, colon_colon_required: bool) {
-    let m;
-    if p.at(T![::]) && p.nth(2) == T![<] {
-        m = p.start();
-        p.bump(T![::]);
-        p.bump(T![<]);
-    } else if !colon_colon_required && p.at(T![<]) && p.nth(1) != T![=] {
-        m = p.start();
-        p.bump(T![<]);
-    } else {
-        return;
-    }
-
-    while !p.at(EOF) && !p.at(T![>]) {
-        generic_arg(p);
-        if !p.at(T![>]) && !p.expect(T![,]) {
-            break;
-        }
-    }
-    p.expect(T![>]);
-    m.complete(p, GENERIC_ARG_LIST);
-}
-
-pub(super) fn const_arg(p: &mut Parser) {
-    let m = p.start();
-    // FIXME: duplicates the code below
-    match p.current() {
-        T!['{'] => {
-            expressions::block_expr(p);
-            m.complete(p, CONST_ARG);
-        }
-        k if k.is_literal() => {
-            expressions::literal(p);
-            m.complete(p, CONST_ARG);
-        }
-        T![true] | T![false] => {
-            expressions::literal(p);
-            m.complete(p, CONST_ARG);
-        }
-        T![-] => {
-            let lm = p.start();
-            p.bump(T![-]);
-            expressions::literal(p);
-            lm.complete(p, PREFIX_EXPR);
-            m.complete(p, CONST_ARG);
-        }
-        _ => {
-            let lm = p.start();
-            paths::use_path(p);
-            lm.complete(p, PATH_EXPR);
-            m.complete(p, CONST_ARG);
-        }
-    }
-}
-
-// test type_arg
-// type A = B<'static, i32, 1, { 2 }, Item=u64, true, false>;
-fn generic_arg(p: &mut Parser) {
-    let m = p.start();
-    match p.current() {
-        LIFETIME_IDENT => {
-            lifetime(p);
-            m.complete(p, LIFETIME_ARG);
-        }
-        // test associated_type_bounds
-        // fn print_all<T: Iterator<Item, Item::Item, Item::<true>, Item: Display, Item<'a> = Item>>(printables: T) {}
-        IDENT if [T![<], T![=], T![:]].contains(&p.nth(1)) => {
-            let path_ty = p.start();
-            let path = p.start();
-            let path_seg = p.start();
-            name_ref(p);
-            opt_generic_arg_list(p, false);
-            match p.current() {
-                // NameRef<...> =
-                T![=] => {
-                    p.bump_any();
-                    types::type_(p);
-
-                    path_seg.abandon(p);
-                    path.abandon(p);
-                    path_ty.abandon(p);
-                    m.complete(p, ASSOC_TYPE_ARG);
-                }
-                T![:] if p.nth(1) == T![:] => {
-                    // NameRef::, this is a path type
-                    path_seg.complete(p, PATH_SEGMENT);
-                    let qual = path.complete(p, PATH);
-                    opt_generic_arg_list(p, false);
-                    paths::type_path_for_qualifier(p, qual);
-                    path_ty.complete(p, PATH_TYPE);
-                    m.complete(p, TYPE_ARG);
-                }
-                // NameRef<...>:
-                T![:] => {
-                    type_params::bounds(p);
-
-                    path_seg.abandon(p);
-                    path.abandon(p);
-                    path_ty.abandon(p);
-                    m.complete(p, ASSOC_TYPE_ARG);
-                }
-                // NameRef, this is a single segment path type
-                _ => {
-                    path_seg.complete(p, PATH_SEGMENT);
-                    path.complete(p, PATH);
-                    path_ty.complete(p, PATH_TYPE);
-                    m.complete(p, TYPE_ARG);
-                }
-            }
-        }
-        T!['{'] => {
-            expressions::block_expr(p);
-            m.complete(p, CONST_ARG);
-        }
-        k if k.is_literal() => {
-            expressions::literal(p);
-            m.complete(p, CONST_ARG);
-        }
-        T![true] | T![false] => {
-            expressions::literal(p);
-            m.complete(p, CONST_ARG);
-        }
-        // test const_generic_negated_literal
-        // fn f() { S::<-1> }
-        T![-] => {
-            let lm = p.start();
-            p.bump(T![-]);
-            expressions::literal(p);
-            lm.complete(p, PREFIX_EXPR);
-            m.complete(p, CONST_ARG);
-        }
-        _ => {
-            types::type_(p);
-            m.complete(p, TYPE_ARG);
-        }
-    }
-}
diff --git a/crates/parser/src/grammar/type_params.rs b/crates/parser/src/grammar/type_params.rs
deleted file mode 100644 (file)
index a7a0109..0000000
+++ /dev/null
@@ -1,221 +0,0 @@
-use super::*;
-
-pub(super) fn opt_generic_param_list(p: &mut Parser) {
-    if !p.at(T![<]) {
-        return;
-    }
-    generic_param_list(p);
-}
-
-fn generic_param_list(p: &mut Parser) {
-    assert!(p.at(T![<]));
-    let m = p.start();
-    p.bump(T![<]);
-
-    while !p.at(EOF) && !p.at(T![>]) {
-        let m = p.start();
-
-        // test generic_lifetime_type_attribute
-        // fn foo<#[derive(Lifetime)] 'a, #[derive(Type)] T>(_: &'a T) {
-        // }
-        attributes::outer_attrs(p);
-
-        match p.current() {
-            LIFETIME_IDENT => lifetime_param(p, m),
-            IDENT => type_param(p, m),
-            T![const] => const_param(p, m),
-            _ => {
-                m.abandon(p);
-                p.err_and_bump("expected type parameter")
-            }
-        }
-        if !p.at(T![>]) && !p.expect(T![,]) {
-            break;
-        }
-    }
-    p.expect(T![>]);
-    m.complete(p, GENERIC_PARAM_LIST);
-}
-
-fn lifetime_param(p: &mut Parser, m: Marker) {
-    assert!(p.at(LIFETIME_IDENT));
-    lifetime(p);
-    if p.at(T![:]) {
-        lifetime_bounds(p);
-    }
-    m.complete(p, LIFETIME_PARAM);
-}
-
-fn type_param(p: &mut Parser, m: Marker) {
-    assert!(p.at(IDENT));
-    name(p);
-    if p.at(T![:]) {
-        bounds(p);
-    }
-    // test type_param_default
-    // struct S<T = i32>;
-    if p.at(T![=]) {
-        p.bump(T![=]);
-        types::type_(p)
-    }
-    m.complete(p, TYPE_PARAM);
-}
-
-// test const_param
-// struct S<const N: u32>;
-fn const_param(p: &mut Parser, m: Marker) {
-    assert!(p.at(T![const]));
-    p.bump(T![const]);
-    name(p);
-    if p.at(T![:]) {
-        types::ascription(p);
-    } else {
-        p.error("missing type for const parameter");
-    }
-
-    // test const_param_defaults
-    // struct A<const N: i32 = -1>;
-    // struct B<const N: i32 = {}>;
-    // struct C<const N: i32 = some::CONST>;
-    if p.at(T![=]) {
-        p.bump(T![=]);
-        type_args::const_arg(p);
-    }
-
-    m.complete(p, CONST_PARAM);
-}
-
-// test type_param_bounds
-// struct S<T: 'a + ?Sized + (Copy)>;
-pub(super) fn bounds(p: &mut Parser) {
-    assert!(p.at(T![:]));
-    p.bump(T![:]);
-    bounds_without_colon(p);
-}
-
-fn lifetime_bounds(p: &mut Parser) {
-    assert!(p.at(T![:]));
-    p.bump(T![:]);
-    while p.at(LIFETIME_IDENT) {
-        lifetime(p);
-        if !p.eat(T![+]) {
-            break;
-        }
-    }
-}
-
-pub(super) fn bounds_without_colon_m(p: &mut Parser, marker: Marker) -> CompletedMarker {
-    while type_bound(p) {
-        if !p.eat(T![+]) {
-            break;
-        }
-    }
-
-    marker.complete(p, TYPE_BOUND_LIST)
-}
-
-pub(super) fn bounds_without_colon(p: &mut Parser) {
-    let m = p.start();
-    bounds_without_colon_m(p, m);
-}
-
-fn type_bound(p: &mut Parser) -> bool {
-    let m = p.start();
-    let has_paren = p.eat(T!['(']);
-    p.eat(T![?]);
-    match p.current() {
-        LIFETIME_IDENT => lifetime(p),
-        T![for] => types::for_type(p, false),
-        _ if paths::is_use_path_start(p) => types::path_type_(p, false),
-        _ => {
-            m.abandon(p);
-            return false;
-        }
-    }
-    if has_paren {
-        p.expect(T![')']);
-    }
-    m.complete(p, TYPE_BOUND);
-
-    true
-}
-
-// test where_clause
-// fn foo()
-// where
-//    'a: 'b + 'c,
-//    T: Clone + Copy + 'static,
-//    Iterator::Item: 'a,
-//    <T as Iterator>::Item: 'a
-// {}
-pub(super) fn opt_where_clause(p: &mut Parser) {
-    if !p.at(T![where]) {
-        return;
-    }
-    let m = p.start();
-    p.bump(T![where]);
-
-    while is_where_predicate(p) {
-        where_predicate(p);
-
-        let comma = p.eat(T![,]);
-
-        if is_where_clause_end(p) {
-            break;
-        }
-
-        if !comma {
-            p.error("expected comma");
-        }
-    }
-
-    m.complete(p, WHERE_CLAUSE);
-}
-
-fn is_where_predicate(p: &mut Parser) -> bool {
-    match p.current() {
-        LIFETIME_IDENT => true,
-        T![impl] => false,
-        token => types::TYPE_FIRST.contains(token),
-    }
-}
-
-fn is_where_clause_end(p: &mut Parser) -> bool {
-    matches!(p.current(), T!['{'] | T![;] | T![=])
-}
-
-fn where_predicate(p: &mut Parser) {
-    let m = p.start();
-    match p.current() {
-        LIFETIME_IDENT => {
-            lifetime(p);
-            if p.at(T![:]) {
-                bounds(p);
-            } else {
-                p.error("expected colon");
-            }
-        }
-        T![impl] => {
-            p.error("expected lifetime or type");
-        }
-        _ => {
-            // test where_pred_for
-            // fn for_trait<F>()
-            // where
-            //    for<'a> F: Fn(&'a str)
-            // { }
-            if p.at(T![for]) {
-                types::for_binder(p);
-            }
-
-            types::type_(p);
-
-            if p.at(T![:]) {
-                bounds(p);
-            } else {
-                p.error("expected colon");
-            }
-        }
-    }
-    m.complete(p, WHERE_PRED);
-}
index e0b3c1bf0cb0b55e7d2b402e3ed76402b98cf1d3..c2aa9ffc3bdb372f214698b0bfbb81dbb90a1d3b 100644 (file)
@@ -216,7 +216,7 @@ pub(super) fn for_binder(p: &mut Parser) {
     assert!(p.at(T![for]));
     p.bump(T![for]);
     if p.at(T![<]) {
-        type_params::opt_generic_param_list(p);
+        generic_params::opt_generic_param_list(p);
     } else {
         p.error("expected `<`");
     }
@@ -254,7 +254,7 @@ fn impl_trait_type(p: &mut Parser) {
     assert!(p.at(T![impl]));
     let m = p.start();
     p.bump(T![impl]);
-    type_params::bounds_without_colon(p);
+    generic_params::bounds_without_colon(p);
     m.complete(p, IMPL_TRAIT_TYPE);
 }
 
@@ -264,7 +264,7 @@ fn dyn_trait_type(p: &mut Parser) {
     assert!(p.at(T![dyn]));
     let m = p.start();
     p.bump(T![dyn]);
-    type_params::bounds_without_colon(p);
+    generic_params::bounds_without_colon(p);
     m.complete(p, DYN_TRAIT_TYPE);
 }
 
@@ -339,7 +339,7 @@ fn opt_type_bounds_as_dyn_trait_type(p: &mut Parser, type_marker: CompletedMarke
     p.eat(T![+]);
 
     // Parse rest of the bounds into the TYPE_BOUND_LIST
-    let m = type_params::bounds_without_colon_m(p, m);
+    let m = generic_params::bounds_without_colon_m(p, m);
 
     // Finally precede everything with DYN_TRAIT_TYPE
     m.precede(p).complete(p, DYN_TRAIT_TYPE);
index e59b903bc5bae6b3d70d8613748b9a0f2a6de95f..99b9ead0fc098a83057a9590f45df0fc955d8a1f 100644 (file)
@@ -12,8 +12,6 @@ doctest = false
 serde = { version = "1.0", features = ["derive"] }
 serde_json = { version = "1.0", features = ["unbounded_depth"] }
 tracing = "0.1"
-crossbeam-channel = "0.5.0"
-jod-thread = "0.1.1"
 memmap2 = "0.3.0"
 snap = "1.0"
 
index 20c5ffaebd1c338155e038407b7e8781d78991e3..b03a029f8ade04c222962e55eccefc4c4855a6ab 100644 (file)
@@ -130,7 +130,7 @@ pub fn load_dylib(
                 .into_iter()
                 .map(|(name, kind)| ProcMacro {
                     process: self.process.clone(),
-                    name: name.into(),
+                    name,
                     kind,
                     dylib_path: dylib.path.clone(),
                 })
index d427fa87d23a8a1f088efaceb3039dcd52900f0f..3201394f7f647e76761cc88e028ba17cb194103b 100644 (file)
@@ -246,7 +246,7 @@ fn subtree(&mut self, idx: usize, subtree: &'a tt::Subtree) {
 
     fn enqueue(&mut self, subtree: &'a tt::Subtree) -> u32 {
         let idx = self.subtree.len();
-        let delimiter_id = subtree.delimiter.map(|it| it.id).unwrap_or(TokenId::unspecified());
+        let delimiter_id = subtree.delimiter.map(|it| it.id).unwrap_or_else(TokenId::unspecified);
         let delimiter_kind = subtree.delimiter.map(|it| it.kind);
         self.subtree.push(SubtreeRepr { id: delimiter_id, kind: delimiter_kind, tt: [!0, !0] });
         self.work.push_back((idx, subtree));
@@ -320,7 +320,7 @@ pub(crate) fn read(self) -> tt::Subtree {
                     })
                     .collect(),
             };
-            res[i] = Some(s.into())
+            res[i] = Some(s)
         }
 
         res[0].take().unwrap()
index 16741fc0ad1931c807cefc992eae67b9980ac224..ff4c59447d879e3b9815663554dbfd03b746facd 100644 (file)
@@ -16,7 +16,7 @@
 
 #[derive(Debug)]
 pub(crate) struct ProcMacroProcessSrv {
-    process: Process,
+    _process: Process,
     stdin: ChildStdin,
     stdout: BufReader<ChildStdout>,
 }
@@ -29,7 +29,7 @@ pub(crate) fn run(
         let mut process = Process::run(process_path, args)?;
         let (stdin, stdout) = process.stdio().expect("couldn't access child stdio");
 
-        let srv = ProcMacroProcessSrv { process, stdin, stdout };
+        let srv = ProcMacroProcessSrv { _process: process, stdin, stdout };
 
         Ok(srv)
     }
index 939e41f8455a4e070acf90dc29eacd12178ea848..159e2bfa40f59af13c504e671a1760958078f609 100644 (file)
@@ -25,9 +25,6 @@ paths = { path = "../paths", version = "0.0.0" }
 proc_macro_api = { path = "../proc_macro_api", version = "0.0.0" }
 
 [dev-dependencies]
-test_utils = { path = "../test_utils" }
-toolchain = { path = "../toolchain" }
-cargo_metadata = "0.14"
 expect-test = "1.1.0"
 
 # used as proc macro test targets
index e942b13e474051f24cdf454c4237096f9fe7169c..1417ac2fa1d1b0f357768b8175d1a7ead90f8730 100644 (file)
@@ -106,7 +106,7 @@ pub fn cfg_overrides(&self) -> CfgOverrides {
             UnsetTestCrates::Only(unset_test_crates) => CfgOverrides::Selective(
                 unset_test_crates
                     .iter()
-                    .map(|name| name.clone())
+                    .cloned()
                     .zip(iter::repeat_with(|| {
                         cfg::CfgDiff::new(Vec::new(), vec![cfg::CfgAtom::Flag("test".into())])
                             .unwrap()
@@ -135,8 +135,8 @@ pub struct PackageData {
     pub manifest: ManifestPath,
     /// Targets provided by the crate (lib, bin, example, test, ...)
     pub targets: Vec<Target>,
-    /// Is this package a member of the current workspace
-    pub is_member: bool,
+    /// Does this package come from the local filesystem (and is editable)?
+    pub is_local: bool,
     /// List of packages this package depends on
     pub dependencies: Vec<PackageDependency>,
     /// Rust edition for this package
@@ -296,19 +296,19 @@ pub fn new(mut meta: cargo_metadata::Metadata) -> CargoWorkspace {
         let mut packages = Arena::default();
         let mut targets = Arena::default();
 
-        let ws_members = &meta.workspace_members;
-
         meta.packages.sort_by(|a, b| a.id.cmp(&b.id));
         for meta_pkg in &meta.packages {
             let cargo_metadata::Package {
                 id, edition, name, manifest_path, version, metadata, ..
             } = meta_pkg;
             let meta = from_value::<PackageMetadata>(metadata.clone()).unwrap_or_default();
-            let is_member = ws_members.contains(id);
             let edition = edition.parse::<Edition>().unwrap_or_else(|err| {
                 tracing::error!("Failed to parse edition {}", err);
                 Edition::CURRENT
             });
+            // We treat packages without source as "local" packages. That includes all members of
+            // the current workspace, as well as any path dependency outside the workspace.
+            let is_local = meta_pkg.source.is_none();
 
             let pkg = packages.alloc(PackageData {
                 id: id.repr.clone(),
@@ -316,7 +316,7 @@ pub fn new(mut meta: cargo_metadata::Metadata) -> CargoWorkspace {
                 version: version.clone(),
                 manifest: AbsPathBuf::assert(PathBuf::from(&manifest_path)).try_into().unwrap(),
                 targets: Vec::new(),
-                is_member,
+                is_local,
                 edition,
                 dependencies: Vec::new(),
                 features: meta_pkg.features.clone().into_iter().collect(),
index 840bc281fca771d8b978b6e4c39444065c873688..0e37624f314d5177e3cc9d040e21b8dd61f4fd6b 100644 (file)
@@ -47,13 +47,12 @@ fn get_test_json_file<T: DeserializeOwned>(file: &str) -> T {
     fixup_paths(&mut json);
     return serde_json::from_value(json).unwrap();
 
-    fn fixup_paths(val: &mut serde_json::Value) -> () {
+    fn fixup_paths(val: &mut serde_json::Value) {
         match val {
             serde_json::Value::String(s) => replace_root(s, true),
             serde_json::Value::Array(vals) => vals.iter_mut().for_each(fixup_paths),
             serde_json::Value::Object(kvals) => kvals.values_mut().for_each(fixup_paths),
             serde_json::Value::Null | serde_json::Value::Bool(_) | serde_json::Value::Number(_) => {
-                ()
             }
         }
     }
index a088bf3cad03a430001b7aab0feb50688513d60f..282116c297dc5971fab7ab62ca96bc15cf7ecb72 100644 (file)
@@ -53,8 +53,8 @@ pub fn len(&self) -> usize {
 /// the current workspace.
 #[derive(Debug, Clone, Eq, PartialEq, Hash)]
 pub struct PackageRoot {
-    /// Is a member of the current workspace
-    pub is_member: bool,
+    /// Is from the local filesystem and may be edited
+    pub is_local: bool,
     pub include: Vec<AbsPathBuf>,
     pub exclude: Vec<AbsPathBuf>,
 }
@@ -189,7 +189,7 @@ pub fn load(
                     Some(rustc_dir) => Some({
                         let meta = CargoWorkspace::fetch_metadata(&rustc_dir, config, progress)
                             .with_context(|| {
-                                format!("Failed to read Cargo metadata for Rust sources")
+                                "Failed to read Cargo metadata for Rust sources".to_string()
                             })?;
                         CargoWorkspace::new(meta)
                     }),
@@ -268,7 +268,7 @@ pub fn to_roots(&self) -> Vec<PackageRoot> {
             ProjectWorkspace::Json { project, sysroot, rustc_cfg: _ } => project
                 .crates()
                 .map(|(_, krate)| PackageRoot {
-                    is_member: krate.is_workspace_member,
+                    is_local: krate.is_workspace_member,
                     include: krate.include.clone(),
                     exclude: krate.exclude.clone(),
                 })
@@ -276,7 +276,7 @@ pub fn to_roots(&self) -> Vec<PackageRoot> {
                 .into_iter()
                 .chain(sysroot.as_ref().into_iter().flat_map(|sysroot| {
                     sysroot.crates().map(move |krate| PackageRoot {
-                        is_member: false,
+                        is_local: false,
                         include: vec![sysroot[krate].root.parent().to_path_buf()],
                         exclude: Vec::new(),
                     })
@@ -293,7 +293,7 @@ pub fn to_roots(&self) -> Vec<PackageRoot> {
                 cargo
                     .packages()
                     .map(|pkg| {
-                        let is_member = cargo[pkg].is_member;
+                        let is_local = cargo[pkg].is_local;
                         let pkg_root = cargo[pkg].manifest.parent().to_path_buf();
 
                         let mut include = vec![pkg_root.clone()];
@@ -319,23 +319,23 @@ pub fn to_roots(&self) -> Vec<PackageRoot> {
                         include.extend(extra_targets);
 
                         let mut exclude = vec![pkg_root.join(".git")];
-                        if is_member {
+                        if is_local {
                             exclude.push(pkg_root.join("target"));
                         } else {
                             exclude.push(pkg_root.join("tests"));
                             exclude.push(pkg_root.join("examples"));
                             exclude.push(pkg_root.join("benches"));
                         }
-                        PackageRoot { is_member, include, exclude }
+                        PackageRoot { is_local, include, exclude }
                     })
-                    .chain(sysroot.into_iter().map(|sysroot| PackageRoot {
-                        is_member: false,
+                    .chain(sysroot.iter().map(|sysroot| PackageRoot {
+                        is_local: false,
                         include: vec![sysroot.root().to_path_buf()],
                         exclude: Vec::new(),
                     }))
-                    .chain(rustc.into_iter().flat_map(|rustc| {
+                    .chain(rustc.iter().flat_map(|rustc| {
                         rustc.packages().map(move |krate| PackageRoot {
-                            is_member: false,
+                            is_local: false,
                             include: vec![rustc[krate].manifest.parent().to_path_buf()],
                             exclude: Vec::new(),
                         })
@@ -343,14 +343,14 @@ pub fn to_roots(&self) -> Vec<PackageRoot> {
                     .collect()
             }
             ProjectWorkspace::DetachedFiles { files, sysroot, .. } => files
-                .into_iter()
+                .iter()
                 .map(|detached_file| PackageRoot {
-                    is_member: true,
+                    is_local: true,
                     include: vec![detached_file.clone()],
                     exclude: Vec::new(),
                 })
                 .chain(sysroot.crates().map(|krate| PackageRoot {
-                    is_member: false,
+                    is_local: false,
                     include: vec![sysroot[krate].root.parent().to_path_buf()],
                     exclude: Vec::new(),
                 }))
@@ -553,7 +553,7 @@ fn cargo_to_crate_graph(
                     &mut crate_graph,
                     &cargo[pkg],
                     build_scripts.outputs.get(pkg),
-                    &cfg_options,
+                    cfg_options,
                     load_proc_macro,
                     file_id,
                     &cargo[tgt].name,
@@ -815,7 +815,7 @@ fn add_target_crate_root(
             .map(|feat| CfgFlag::KeyValue { key: "feature".into(), value: feat.0.into() }),
     );
 
-    let crate_id = crate_graph.add_crate_root(
+    crate_graph.add_crate_root(
         file_id,
         edition,
         Some(display_name),
@@ -823,9 +823,7 @@ fn add_target_crate_root(
         potential_cfg_options,
         env,
         proc_macro,
-    );
-
-    crate_id
+    )
 }
 
 fn sysroot_to_crate_graph(
@@ -893,27 +891,27 @@ fn inject_cargo_env(package: &PackageData, env: &mut Env) {
     // CARGO_BIN_NAME, CARGO_BIN_EXE_<name>
 
     let manifest_dir = package.manifest.parent();
-    env.set("CARGO_MANIFEST_DIR".into(), manifest_dir.as_os_str().to_string_lossy().into_owned());
+    env.set("CARGO_MANIFEST_DIR", manifest_dir.as_os_str().to_string_lossy().into_owned());
 
     // Not always right, but works for common cases.
-    env.set("CARGO".into(), "cargo".into());
+    env.set("CARGO", "cargo".into());
 
-    env.set("CARGO_PKG_VERSION".into(), package.version.to_string());
-    env.set("CARGO_PKG_VERSION_MAJOR".into(), package.version.major.to_string());
-    env.set("CARGO_PKG_VERSION_MINOR".into(), package.version.minor.to_string());
-    env.set("CARGO_PKG_VERSION_PATCH".into(), package.version.patch.to_string());
-    env.set("CARGO_PKG_VERSION_PRE".into(), package.version.pre.to_string());
+    env.set("CARGO_PKG_VERSION", package.version.to_string());
+    env.set("CARGO_PKG_VERSION_MAJOR", package.version.major.to_string());
+    env.set("CARGO_PKG_VERSION_MINOR", package.version.minor.to_string());
+    env.set("CARGO_PKG_VERSION_PATCH", package.version.patch.to_string());
+    env.set("CARGO_PKG_VERSION_PRE", package.version.pre.to_string());
 
-    env.set("CARGO_PKG_AUTHORS".into(), String::new());
+    env.set("CARGO_PKG_AUTHORS", String::new());
 
-    env.set("CARGO_PKG_NAME".into(), package.name.clone());
+    env.set("CARGO_PKG_NAME", package.name.clone());
     // FIXME: This isn't really correct (a package can have many crates with different names), but
     // it's better than leaving the variable unset.
-    env.set("CARGO_CRATE_NAME".into(), CrateName::normalize_dashes(&package.name).to_string());
-    env.set("CARGO_PKG_DESCRIPTION".into(), String::new());
-    env.set("CARGO_PKG_HOMEPAGE".into(), String::new());
-    env.set("CARGO_PKG_REPOSITORY".into(), String::new());
-    env.set("CARGO_PKG_LICENSE".into(), String::new());
+    env.set("CARGO_CRATE_NAME", CrateName::normalize_dashes(&package.name).to_string());
+    env.set("CARGO_PKG_DESCRIPTION", String::new());
+    env.set("CARGO_PKG_HOMEPAGE", String::new());
+    env.set("CARGO_PKG_REPOSITORY", String::new());
+    env.set("CARGO_PKG_LICENSE", String::new());
 
-    env.set("CARGO_PKG_LICENSE_FILE".into(), String::new());
+    env.set("CARGO_PKG_LICENSE_FILE", String::new());
 }
index 30056887f7fc7189616b5d0f258cfca20764ef40..1dfa23414deb5ae67b4397fc2518d29628ee303f 100644 (file)
@@ -46,7 +46,6 @@ ide_db = { path = "../ide_db", version = "0.0.0" }
 profile = { path = "../profile", version = "0.0.0" }
 project_model = { path = "../project_model", version = "0.0.0" }
 syntax = { path = "../syntax", version = "0.0.0" }
-text_edit = { path = "../text_edit", version = "0.0.0" }
 vfs = { path = "../vfs", version = "0.0.0" }
 vfs-notify = { path = "../vfs-notify", version = "0.0.0" }
 cfg = { path = "../cfg", version = "0.0.0" }
@@ -74,7 +73,6 @@ xshell = "0.1"
 test_utils = { path = "../test_utils" }
 sourcegen = { path = "../sourcegen" }
 mbe = { path = "../mbe" }
-tt = { path = "../tt" }
 
 [features]
 jemalloc = ["jemallocator", "profile/jemalloc"]
index f7e9415cc029d46b9ba6460ae90d6733ccdafad8..2390bee82436c5ff96bd907fb4a5dc7014437e44 100644 (file)
@@ -92,7 +92,9 @@ fn try_main() -> Result<()> {
 }
 
 fn setup_logging(log_file: Option<&Path>) -> Result<()> {
-    env::set_var("RUST_BACKTRACE", "short");
+    if env::var("RUST_BACKTRACE").is_err() {
+        env::set_var("RUST_BACKTRACE", "short");
+    }
 
     let log_file = match log_file {
         Some(path) => {
index c1795d90c47512e3ee4ebcf3186370a82d28fabc..8f02082f3e7f5dc2341348d850c442ffb96e60b1 100644 (file)
@@ -53,7 +53,7 @@ struct ConfigData {
         assist_importEnforceGranularity: bool              = "false",
         /// The path structure for newly inserted paths to use.
         assist_importPrefix: ImportPrefixDef               = "\"plain\"",
-        /// Group inserted imports by the [following order](https://rust-analyzer.github.io/manual.html#auto-import). Groups are separated by newlines.
+        /// Group inserted imports by the https://rust-analyzer.github.io/manual.html#auto-import[following order]. Groups are separated by newlines.
         assist_importGroup: bool                           = "true",
         /// Whether to allow import insertion to merge new imports into single path glob imports like `use std::fmt::*;`.
         assist_allowMergingIntoGlobImports: bool           = "true",
index 19ddd7c717d891f2c7b95119a18313a48e90f9ba..8e947b55402b56073991eaa3979ce6f02da9a8be 100644 (file)
@@ -25,7 +25,7 @@
     main_loop::Task,
     mem_docs::MemDocs,
     op_queue::OpQueue,
-    reload::SourceRootConfig,
+    reload::{self, SourceRootConfig},
     thread_pool::TaskPool,
     to_proto::url_from_abs_path,
     Result,
@@ -175,7 +175,8 @@ pub(crate) fn new(sender: Sender<lsp_server::Message>, config: Config) -> Global
     pub(crate) fn process_changes(&mut self) -> bool {
         let _p = profile::span("GlobalState::process_changes");
         let mut fs_changes = Vec::new();
-        let mut has_fs_changes = false;
+        // A file was added or deleted
+        let mut has_structure_changes = false;
 
         let change = {
             let mut change = Change::new();
@@ -186,10 +187,14 @@ pub(crate) fn process_changes(&mut self) -> bool {
             }
 
             for file in changed_files {
-                if file.is_created_or_deleted() {
-                    if let Some(path) = vfs.file_path(file.file_id).as_path() {
-                        fs_changes.push((path.to_path_buf(), file.change_kind));
-                        has_fs_changes = true;
+                if let Some(path) = vfs.file_path(file.file_id).as_path() {
+                    let path = path.to_path_buf();
+                    if reload::should_refresh_for_change(&path, file.change_kind) {
+                        self.fetch_workspaces_queue.request_op();
+                    }
+                    fs_changes.push((path, file.change_kind));
+                    if file.is_created_or_deleted() {
+                        has_structure_changes = true;
                     }
                 }
 
@@ -208,7 +213,7 @@ pub(crate) fn process_changes(&mut self) -> bool {
                 };
                 change.change_file(file.file_id, text);
             }
-            if has_fs_changes {
+            if has_structure_changes {
                 let roots = self.source_root_config.partition(vfs);
                 change.set_roots(roots);
             }
@@ -216,7 +221,6 @@ pub(crate) fn process_changes(&mut self) -> bool {
         };
 
         self.analysis_host.apply_change(change);
-        self.maybe_refresh(&fs_changes);
         true
     }
 
index 9de059a2c388b44eca09b016fc0d801bdc5ba9ee..dbbe1841fc9b236606ae873a17061052804656a4 100644 (file)
@@ -27,7 +27,7 @@
 use project_model::TargetKind;
 use serde_json::json;
 use stdx::{format_to, never};
-use syntax::{algo, ast, AstNode, TextRange, TextSize};
+use syntax::{algo, ast, AstNode, TextRange, TextSize, T};
 
 use crate::{
     cargo_target_spec::CargoTargetSpec,
@@ -727,16 +727,13 @@ pub(crate) fn handle_completion(
     let completion_triggered_after_single_colon = {
         let mut res = false;
         if let Some(ctx) = params.context {
-            if ctx.trigger_character.unwrap_or_default() == ":" {
+            if ctx.trigger_character.as_deref() == Some(":") {
                 let source_file = snap.analysis.parse(position.file_id)?;
-                let syntax = source_file.syntax();
-                let text = syntax.text();
-                if let Some(next_char) = text.char_at(position.offset) {
-                    let diff = TextSize::of(next_char) + TextSize::of(':');
-                    let prev_char = position.offset - diff;
-                    if text.char_at(prev_char) != Some(':') {
-                        res = true;
-                    }
+                let left_token =
+                    source_file.syntax().token_at_offset(position.offset).left_biased();
+                match left_token {
+                    Some(left_token) => res = left_token.kind() == T![:],
+                    None => res = true,
                 }
             }
         }
index 0d72f6ec97f623f03038d4e8c5af7ed954af64fe..0c48b22bdbaa58dbba03b4395b416828fb75a2f5 100644 (file)
@@ -21,7 +21,7 @@
     handlers, lsp_ext,
     lsp_utils::{apply_document_changes, is_cancelled, notification_is, Progress},
     mem_docs::DocumentData,
-    reload::{BuildDataProgress, ProjectWorkspaceProgress},
+    reload::{self, BuildDataProgress, ProjectWorkspaceProgress},
     Result,
 };
 
@@ -693,7 +693,9 @@ fn on_notification(&mut self, not: Notification) -> Result<()> {
                     flycheck.update();
                 }
                 if let Ok(abs_path) = from_proto::abs_path(&params.text_document.uri) {
-                    this.maybe_refresh(&[(abs_path, ChangeKind::Modify)]);
+                    if reload::should_refresh_for_change(&abs_path, ChangeKind::Modify) {
+                        this.fetch_workspaces_queue.request_op();
+                    }
                 }
                 Ok(())
             })?
index 2cb5eb46b25aaa4d43d1ffde5a9502dafd875629..f7ec3707c0aecb152e8bc304ac5bd7be3c04a6da 100644 (file)
@@ -58,58 +58,6 @@ pub(crate) fn update_configuration(&mut self, config: Config) {
             .raw_database_mut()
             .set_enable_proc_attr_macros(self.config.expand_proc_attr_macros());
     }
-    pub(crate) fn maybe_refresh(&mut self, changes: &[(AbsPathBuf, ChangeKind)]) {
-        if !changes.iter().any(|(path, kind)| is_interesting(path, *kind)) {
-            return;
-        }
-        tracing::info!(
-            "Requesting workspace reload because of the following changes: {}",
-            itertools::join(
-                changes
-                    .iter()
-                    .filter(|(path, kind)| is_interesting(path, *kind))
-                    .map(|(path, kind)| format!("{}: {:?}", path.display(), kind)),
-                ", "
-            )
-        );
-        self.fetch_workspaces_queue.request_op();
-
-        fn is_interesting(path: &AbsPath, change_kind: ChangeKind) -> bool {
-            const IMPLICIT_TARGET_FILES: &[&str] = &["build.rs", "src/main.rs", "src/lib.rs"];
-            const IMPLICIT_TARGET_DIRS: &[&str] = &["src/bin", "examples", "tests", "benches"];
-            let file_name = path.file_name().unwrap_or_default();
-
-            if file_name == "Cargo.toml" || file_name == "Cargo.lock" {
-                return true;
-            }
-            if change_kind == ChangeKind::Modify {
-                return false;
-            }
-            if path.extension().unwrap_or_default() != "rs" {
-                return false;
-            }
-            if IMPLICIT_TARGET_FILES.iter().any(|it| path.as_ref().ends_with(it)) {
-                return true;
-            }
-            let parent = match path.parent() {
-                Some(it) => it,
-                None => return false,
-            };
-            if IMPLICIT_TARGET_DIRS.iter().any(|it| parent.as_ref().ends_with(it)) {
-                return true;
-            }
-            if file_name == "main.rs" {
-                let grand_parent = match parent.parent() {
-                    Some(it) => it,
-                    None => return false,
-                };
-                if IMPLICIT_TARGET_DIRS.iter().any(|it| grand_parent.as_ref().ends_with(it)) {
-                    return true;
-                }
-            }
-            false
-        }
-    }
 
     pub(crate) fn current_status(&self) -> lsp_ext::ServerStatusParams {
         let mut status = lsp_ext::ServerStatusParams {
@@ -294,7 +242,7 @@ fn eq_ignore_build_data<'a>(
                         .workspaces
                         .iter()
                         .flat_map(|ws| ws.to_roots())
-                        .filter(|it| it.is_member)
+                        .filter(|it| it.is_local)
                         .flat_map(|root| {
                             root.include.into_iter().flat_map(|it| {
                                 [
@@ -514,12 +462,12 @@ pub(crate) fn new(
                 vfs::loader::Entry::Directories(dirs)
             };
 
-            if root.is_member {
+            if root.is_local {
                 res.watch.push(res.load.len());
             }
             res.load.push(entry);
 
-            if root.is_member {
+            if root.is_local {
                 local_filesets.push(fsc.len());
             }
             fsc.add_file_set(file_set_roots)
@@ -617,3 +565,39 @@ fn expand(
         }
     }
 }
+
+pub(crate) fn should_refresh_for_change(path: &AbsPath, change_kind: ChangeKind) -> bool {
+    const IMPLICIT_TARGET_FILES: &[&str] = &["build.rs", "src/main.rs", "src/lib.rs"];
+    const IMPLICIT_TARGET_DIRS: &[&str] = &["src/bin", "examples", "tests", "benches"];
+    let file_name = path.file_name().unwrap_or_default();
+
+    if file_name == "Cargo.toml" || file_name == "Cargo.lock" {
+        return true;
+    }
+    if change_kind == ChangeKind::Modify {
+        return false;
+    }
+    if path.extension().unwrap_or_default() != "rs" {
+        return false;
+    }
+    if IMPLICIT_TARGET_FILES.iter().any(|it| path.as_ref().ends_with(it)) {
+        return true;
+    }
+    let parent = match path.parent() {
+        Some(it) => it,
+        None => return false,
+    };
+    if IMPLICIT_TARGET_DIRS.iter().any(|it| parent.as_ref().ends_with(it)) {
+        return true;
+    }
+    if file_name == "main.rs" {
+        let grand_parent = match parent.parent() {
+            Some(it) => it,
+            None => return false,
+        };
+        if IMPLICIT_TARGET_DIRS.iter().any(|it| grand_parent.as_ref().ends_with(it)) {
+            return true;
+        }
+    }
+    false
+}
index 5cf9158082d48da0146aa935cd74a0ec7009a20e..99ae418f7c11104fc04b61c71b57f9c2a5e4aa78 100644 (file)
@@ -78,7 +78,8 @@ pub(crate) fn server(self) -> Server {
             profile::init_from(crate::PROFILE);
         });
 
-        let (mini_core, fixtures) = Fixture::parse(self.fixture);
+        let (mini_core, proc_macros, fixtures) = Fixture::parse(self.fixture);
+        assert!(proc_macros.is_empty());
         assert!(mini_core.is_none());
         for entry in fixtures {
             let path = tmp_dir.path().join(&entry.path['/'.len_utf8()..]);
index 26eed7ea2f386614e0e8f401c3e0394de8f49377..3071da45fa8aecd7e140bdf07ecdc46af2d7dc46 100644 (file)
@@ -191,7 +191,7 @@ fn deny_clippy(path: &Path, text: &str) {
         // The documentation in string literals may contain anything for its own purposes
         "ide_db/src/helpers/generated_lints.rs",
         // The tests test clippy lint hovers
-        "ide/src/hover.rs",
+        "ide/src/hover/tests.rs",
     ];
     if ignore.iter().any(|p| path.ends_with(p)) {
         return;
@@ -273,7 +273,7 @@ fn check_todo(path: &Path, text: &str) {
         // Some of our assists generate `todo!()`.
         "handlers/add_turbo_fish.rs",
         "handlers/generate_function.rs",
-        "handlers/fill_match_arms.rs",
+        "handlers/add_missing_match_arms.rs",
         "handlers/replace_derive_with_manual_impl.rs",
         // To support generating `todo!()` in assists, we have `expr_todo()` in
         // `ast::make`.
@@ -311,6 +311,7 @@ fn check_dbg(path: &Path, text: &str) {
         "handlers/remove_dbg.rs",
         // We have .dbg postfix
         "ide_completion/src/completions/postfix.rs",
+        "ide_completion/src/tests/proc_macros.rs",
         // The documentation in string literals may contain anything for its own purposes
         "ide_completion/src/lib.rs",
         "ide_db/src/helpers/generated_lints.rs",
index 2d2806634262744cf79f0ba94f3e03feca7c878c..6a332bce85d84110b7b312947e6deaed0bc9ead4 100644 (file)
@@ -43,10 +43,12 @@ pub fn list_files(dir: &Path) -> Vec<PathBuf> {
     res
 }
 
+#[derive(Clone)]
 pub struct CommentBlock {
     pub id: String,
     pub line: usize,
     pub contents: Vec<String>,
+    is_doc: bool,
 }
 
 impl CommentBlock {
@@ -54,59 +56,60 @@ pub fn extract(tag: &str, text: &str) -> Vec<CommentBlock> {
         assert!(tag.starts_with(char::is_uppercase));
 
         let tag = format!("{}:", tag);
-        let mut res = Vec::new();
-        for (line, mut block) in do_extract_comment_blocks(text, true) {
-            let first = block.remove(0);
-            if let Some(id) = first.strip_prefix(&tag) {
-                let id = id.trim().to_string();
-                let block = CommentBlock { id, line, contents: block };
-                res.push(block);
-            }
-        }
-        res
+        // Would be nice if we had `.retain_mut` here!
+        CommentBlock::extract_untagged(text)
+            .into_iter()
+            .filter_map(|mut block| {
+                let first = block.contents.remove(0);
+                first.strip_prefix(&tag).map(|id| {
+                    if block.is_doc {
+                        panic!(
+                            "Use plain (non-doc) comments with tags like {}:\n    {}",
+                            tag, first
+                        )
+                    }
+
+                    block.id = id.trim().to_string();
+                    block
+                })
+            })
+            .collect()
     }
 
     pub fn extract_untagged(text: &str) -> Vec<CommentBlock> {
         let mut res = Vec::new();
-        for (line, block) in do_extract_comment_blocks(text, false) {
-            let id = String::new();
-            let block = CommentBlock { id, line, contents: block };
-            res.push(block);
-        }
-        res
-    }
-}
-
-fn do_extract_comment_blocks(
-    text: &str,
-    allow_blocks_with_empty_lines: bool,
-) -> Vec<(usize, Vec<String>)> {
-    let mut res = Vec::new();
 
-    let prefix = "// ";
-    let lines = text.lines().map(str::trim_start);
-
-    let mut block = (0, vec![]);
-    for (line_num, line) in lines.enumerate() {
-        if line == "//" && allow_blocks_with_empty_lines {
-            block.1.push(String::new());
-            continue;
-        }
-
-        let is_comment = line.starts_with(prefix);
-        if is_comment {
-            block.1.push(line[prefix.len()..].to_string());
-        } else {
-            if !block.1.is_empty() {
-                res.push(mem::take(&mut block));
+        let lines = text.lines().map(str::trim_start);
+
+        let dummy_block =
+            CommentBlock { id: String::new(), line: 0, contents: Vec::new(), is_doc: false };
+        let mut block = dummy_block.clone();
+        for (line_num, line) in lines.enumerate() {
+            match line.strip_prefix("//") {
+                Some(mut contents) => {
+                    if let Some('/' | '!') = contents.chars().next() {
+                        contents = &contents[1..];
+                        block.is_doc = true;
+                    }
+                    if let Some(' ') = contents.chars().next() {
+                        contents = &contents[1..];
+                    }
+                    block.contents.push(contents.to_string());
+                }
+                None => {
+                    if !block.contents.is_empty() {
+                        let block = mem::replace(&mut block, dummy_block.clone());
+                        res.push(block);
+                    }
+                    block.line = line_num + 2;
+                }
             }
-            block.0 = line_num + 2;
         }
+        if !block.contents.is_empty() {
+            res.push(block)
+        }
+        res
     }
-    if !block.1.is_empty() {
-        res.push(block)
-    }
-    res
 }
 
 #[derive(Debug)]
index e83d5db437d1d218d5fe3c3696e23790366b9de1..e7d4753de007504530f20461f098d08d95f729fb 100644 (file)
@@ -19,13 +19,11 @@ pub fn timeit(label: &'static str) -> impl Drop {
 }
 
 /// Prints backtrace to stderr, useful for debugging.
-#[cfg(feature = "backtrace")]
-pub fn print_backtrace() {
-    let bt = backtrace::Backtrace::new();
-    eprintln!("{:?}", bt);
-}
-#[cfg(not(feature = "backtrace"))]
 pub fn print_backtrace() {
+    #[cfg(feature = "backtrace")]
+    eprintln!("{:?}", backtrace::Backtrace::new());
+
+    #[cfg(not(feature = "backtrace"))]
     eprintln!(
         r#"Enable the backtrace feature.
 Uncomment `default = [ "backtrace" ]` in `crates/stdx/Cargo.toml`.
index 0b2134705b6e5da8f7cbaa136e79579f9bb2f94d..6a48a322a666d4465089dc93cd2b733adc2b5e4c 100644 (file)
@@ -12,10 +12,9 @@ doctest = false
 [dependencies]
 cov-mark = "2.0.0-pre.1"
 itertools = "0.10.0"
-rowan = "0.13.0"
+rowan = "0.14.0"
 rustc_lexer = { version = "725.0.0", package = "rustc-ap-rustc_lexer" }
 rustc-hash = "1.1.0"
-arrayvec = "0.7"
 once_cell = "1.3.1"
 indexmap = "1.4.0"
 smol_str = "0.1.15"
index 88d24df8b82529d41002fd16ea8ec29414cb92cf..9c7f93e130637ba5b305e64ed566cde9c793bb2e 100644 (file)
@@ -212,7 +212,7 @@ fn go(diff: &mut TreeDiff, lhs: SyntaxElement, rhs: SyntaxElement) {
                     look_ahead_scratch.push(rhs_ele.clone());
                     let mut rhs_children_clone = rhs_children.clone();
                     let mut insert = false;
-                    while let Some(rhs_child) = rhs_children_clone.next() {
+                    for rhs_child in &mut rhs_children_clone {
                         if syntax_element_eq(&lhs_ele, &rhs_child) {
                             cov_mark::hit!(diff_insertions);
                             insert = true;
index e26c5b7ad90f8de78c9847528b3ebe8753db4319..7c9200f56888ed22ea75c852e2a11362e791dcba 100644 (file)
@@ -21,8 +21,8 @@
     expr_ext::{ArrayExprKind, Effect, ElseBranch, LiteralKind},
     generated::{nodes::*, tokens::*},
     node_ext::{
-        AttrKind, AttrsOwnerNode, FieldKind, Macro, NameLike, NameOrNameRef, PathSegmentKind,
-        SelfParamKind, SlicePatComponents, StructKind, TypeBoundKind, VisibilityKind,
+        AttrKind, FieldKind, Macro, NameLike, NameOrNameRef, PathSegmentKind, SelfParamKind,
+        SlicePatComponents, StructKind, TypeBoundKind, VisibilityKind,
     },
     operators::{ArithOp, BinaryOp, CmpOp, LogicOp, Ordering, RangeOp, UnaryOp},
     token_ext::{
index 205d01e974eab35d40203e486e1849bb18d25615..547e7546364e6a92881ec553c7cfda865a2e080d 100644 (file)
@@ -1445,6 +1445,46 @@ pub enum GenericParam {
     TypeParam(TypeParam),
 }
 impl ast::AttrsOwner for GenericParam {}
+#[derive(Debug, Clone, PartialEq, Eq, Hash)]
+pub struct DynArgListOwner {
+    pub(crate) syntax: SyntaxNode,
+}
+impl ast::ArgListOwner for DynArgListOwner {}
+#[derive(Debug, Clone, PartialEq, Eq, Hash)]
+pub struct DynAttrsOwner {
+    pub(crate) syntax: SyntaxNode,
+}
+impl ast::AttrsOwner for DynAttrsOwner {}
+#[derive(Debug, Clone, PartialEq, Eq, Hash)]
+pub struct DynGenericParamsOwner {
+    pub(crate) syntax: SyntaxNode,
+}
+impl ast::GenericParamsOwner for DynGenericParamsOwner {}
+#[derive(Debug, Clone, PartialEq, Eq, Hash)]
+pub struct DynLoopBodyOwner {
+    pub(crate) syntax: SyntaxNode,
+}
+impl ast::LoopBodyOwner for DynLoopBodyOwner {}
+#[derive(Debug, Clone, PartialEq, Eq, Hash)]
+pub struct DynModuleItemOwner {
+    pub(crate) syntax: SyntaxNode,
+}
+impl ast::ModuleItemOwner for DynModuleItemOwner {}
+#[derive(Debug, Clone, PartialEq, Eq, Hash)]
+pub struct DynNameOwner {
+    pub(crate) syntax: SyntaxNode,
+}
+impl ast::NameOwner for DynNameOwner {}
+#[derive(Debug, Clone, PartialEq, Eq, Hash)]
+pub struct DynTypeBoundsOwner {
+    pub(crate) syntax: SyntaxNode,
+}
+impl ast::TypeBoundsOwner for DynTypeBoundsOwner {}
+#[derive(Debug, Clone, PartialEq, Eq, Hash)]
+pub struct DynVisibilityOwner {
+    pub(crate) syntax: SyntaxNode,
+}
+impl ast::VisibilityOwner for DynVisibilityOwner {}
 impl AstNode for Name {
     fn can_cast(kind: SyntaxKind) -> bool { kind == NAME }
     fn cast(syntax: SyntaxNode) -> Option<Self> {
@@ -3564,6 +3604,219 @@ fn syntax(&self) -> &SyntaxNode {
         }
     }
 }
+impl DynArgListOwner {
+    #[inline]
+    pub fn new<T: ast::ArgListOwner>(node: T) -> DynArgListOwner {
+        DynArgListOwner { syntax: node.syntax().clone() }
+    }
+}
+impl AstNode for DynArgListOwner {
+    fn can_cast(kind: SyntaxKind) -> bool {
+        match kind {
+            CALL_EXPR | METHOD_CALL_EXPR => true,
+            _ => false,
+        }
+    }
+    fn cast(syntax: SyntaxNode) -> Option<Self> {
+        Self::can_cast(syntax.kind()).then(|| DynArgListOwner { syntax })
+    }
+    fn syntax(&self) -> &SyntaxNode { &self.syntax }
+}
+impl DynAttrsOwner {
+    #[inline]
+    pub fn new<T: ast::AttrsOwner>(node: T) -> DynAttrsOwner {
+        DynAttrsOwner { syntax: node.syntax().clone() }
+    }
+}
+impl AstNode for DynAttrsOwner {
+    fn can_cast(kind: SyntaxKind) -> bool {
+        match kind {
+            MACRO_CALL
+            | SOURCE_FILE
+            | CONST
+            | ENUM
+            | EXTERN_BLOCK
+            | EXTERN_CRATE
+            | FN
+            | IMPL
+            | MACRO_RULES
+            | MACRO_DEF
+            | MODULE
+            | STATIC
+            | STRUCT
+            | TRAIT
+            | TYPE_ALIAS
+            | UNION
+            | USE
+            | ITEM_LIST
+            | BLOCK_EXPR
+            | SELF_PARAM
+            | PARAM
+            | RECORD_FIELD
+            | TUPLE_FIELD
+            | VARIANT
+            | ASSOC_ITEM_LIST
+            | EXTERN_ITEM_LIST
+            | CONST_PARAM
+            | LIFETIME_PARAM
+            | TYPE_PARAM
+            | EXPR_STMT
+            | LET_STMT
+            | ARRAY_EXPR
+            | AWAIT_EXPR
+            | BIN_EXPR
+            | BOX_EXPR
+            | BREAK_EXPR
+            | CALL_EXPR
+            | CAST_EXPR
+            | CLOSURE_EXPR
+            | CONTINUE_EXPR
+            | EFFECT_EXPR
+            | FIELD_EXPR
+            | FOR_EXPR
+            | IF_EXPR
+            | INDEX_EXPR
+            | LITERAL
+            | LOOP_EXPR
+            | MATCH_EXPR
+            | METHOD_CALL_EXPR
+            | PAREN_EXPR
+            | PATH_EXPR
+            | PREFIX_EXPR
+            | RANGE_EXPR
+            | REF_EXPR
+            | RETURN_EXPR
+            | TRY_EXPR
+            | TUPLE_EXPR
+            | WHILE_EXPR
+            | YIELD_EXPR
+            | RECORD_EXPR_FIELD_LIST
+            | RECORD_EXPR_FIELD
+            | MATCH_ARM_LIST
+            | MATCH_ARM
+            | IDENT_PAT
+            | RECORD_PAT_FIELD => true,
+            _ => false,
+        }
+    }
+    fn cast(syntax: SyntaxNode) -> Option<Self> {
+        Self::can_cast(syntax.kind()).then(|| DynAttrsOwner { syntax })
+    }
+    fn syntax(&self) -> &SyntaxNode { &self.syntax }
+}
+impl DynGenericParamsOwner {
+    #[inline]
+    pub fn new<T: ast::GenericParamsOwner>(node: T) -> DynGenericParamsOwner {
+        DynGenericParamsOwner { syntax: node.syntax().clone() }
+    }
+}
+impl AstNode for DynGenericParamsOwner {
+    fn can_cast(kind: SyntaxKind) -> bool {
+        match kind {
+            ENUM | FN | IMPL | STRUCT | TRAIT | TYPE_ALIAS | UNION => true,
+            _ => false,
+        }
+    }
+    fn cast(syntax: SyntaxNode) -> Option<Self> {
+        Self::can_cast(syntax.kind()).then(|| DynGenericParamsOwner { syntax })
+    }
+    fn syntax(&self) -> &SyntaxNode { &self.syntax }
+}
+impl DynLoopBodyOwner {
+    #[inline]
+    pub fn new<T: ast::LoopBodyOwner>(node: T) -> DynLoopBodyOwner {
+        DynLoopBodyOwner { syntax: node.syntax().clone() }
+    }
+}
+impl AstNode for DynLoopBodyOwner {
+    fn can_cast(kind: SyntaxKind) -> bool {
+        match kind {
+            FOR_EXPR | LOOP_EXPR | WHILE_EXPR => true,
+            _ => false,
+        }
+    }
+    fn cast(syntax: SyntaxNode) -> Option<Self> {
+        Self::can_cast(syntax.kind()).then(|| DynLoopBodyOwner { syntax })
+    }
+    fn syntax(&self) -> &SyntaxNode { &self.syntax }
+}
+impl DynModuleItemOwner {
+    #[inline]
+    pub fn new<T: ast::ModuleItemOwner>(node: T) -> DynModuleItemOwner {
+        DynModuleItemOwner { syntax: node.syntax().clone() }
+    }
+}
+impl AstNode for DynModuleItemOwner {
+    fn can_cast(kind: SyntaxKind) -> bool {
+        match kind {
+            MACRO_ITEMS | SOURCE_FILE | ITEM_LIST => true,
+            _ => false,
+        }
+    }
+    fn cast(syntax: SyntaxNode) -> Option<Self> {
+        Self::can_cast(syntax.kind()).then(|| DynModuleItemOwner { syntax })
+    }
+    fn syntax(&self) -> &SyntaxNode { &self.syntax }
+}
+impl DynNameOwner {
+    #[inline]
+    pub fn new<T: ast::NameOwner>(node: T) -> DynNameOwner {
+        DynNameOwner { syntax: node.syntax().clone() }
+    }
+}
+impl AstNode for DynNameOwner {
+    fn can_cast(kind: SyntaxKind) -> bool {
+        match kind {
+            CONST | ENUM | FN | MACRO_RULES | MACRO_DEF | MODULE | STATIC | STRUCT | TRAIT
+            | TYPE_ALIAS | UNION | RENAME | SELF_PARAM | RECORD_FIELD | VARIANT | CONST_PARAM
+            | TYPE_PARAM | IDENT_PAT => true,
+            _ => false,
+        }
+    }
+    fn cast(syntax: SyntaxNode) -> Option<Self> {
+        Self::can_cast(syntax.kind()).then(|| DynNameOwner { syntax })
+    }
+    fn syntax(&self) -> &SyntaxNode { &self.syntax }
+}
+impl DynTypeBoundsOwner {
+    #[inline]
+    pub fn new<T: ast::TypeBoundsOwner>(node: T) -> DynTypeBoundsOwner {
+        DynTypeBoundsOwner { syntax: node.syntax().clone() }
+    }
+}
+impl AstNode for DynTypeBoundsOwner {
+    fn can_cast(kind: SyntaxKind) -> bool {
+        match kind {
+            ASSOC_TYPE_ARG | TRAIT | TYPE_ALIAS | LIFETIME_PARAM | TYPE_PARAM | WHERE_PRED => true,
+            _ => false,
+        }
+    }
+    fn cast(syntax: SyntaxNode) -> Option<Self> {
+        Self::can_cast(syntax.kind()).then(|| DynTypeBoundsOwner { syntax })
+    }
+    fn syntax(&self) -> &SyntaxNode { &self.syntax }
+}
+impl DynVisibilityOwner {
+    #[inline]
+    pub fn new<T: ast::VisibilityOwner>(node: T) -> DynVisibilityOwner {
+        DynVisibilityOwner { syntax: node.syntax().clone() }
+    }
+}
+impl AstNode for DynVisibilityOwner {
+    fn can_cast(kind: SyntaxKind) -> bool {
+        match kind {
+            CONST | ENUM | EXTERN_CRATE | FN | IMPL | MACRO_RULES | MACRO_DEF | MODULE | STATIC
+            | STRUCT | TRAIT | TYPE_ALIAS | UNION | USE | RECORD_FIELD | TUPLE_FIELD | VARIANT => {
+                true
+            }
+            _ => false,
+        }
+    }
+    fn cast(syntax: SyntaxNode) -> Option<Self> {
+        Self::can_cast(syntax.kind()).then(|| DynVisibilityOwner { syntax })
+    }
+    fn syntax(&self) -> &SyntaxNode { &self.syntax }
+}
 impl std::fmt::Display for GenericArg {
     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
         std::fmt::Display::fmt(self.syntax(), f)
index 93eca19c309266641f5d34d9f911579114ab456a..b01befebea120264f2793cd459c7883b6099e9be 100644 (file)
@@ -316,6 +316,11 @@ pub fn expr_if(
 pub fn expr_for_loop(pat: ast::Pat, expr: ast::Expr, block: ast::BlockExpr) -> ast::Expr {
     expr_from_text(&format!("for {} in {} {}", pat, expr, block))
 }
+
+pub fn expr_loop(block: ast::BlockExpr) -> ast::Expr {
+    expr_from_text(&format!("loop {}", block))
+}
+
 pub fn expr_prefix(op: SyntaxKind, expr: ast::Expr) -> ast::Expr {
     let token = token(op);
     expr_from_text(&format!("{}{}", token, expr))
@@ -551,7 +556,7 @@ pub fn param(pat: ast::Pat, ty: ast::Type) -> ast::Param {
 }
 
 pub fn self_param() -> ast::SelfParam {
-    ast_from_text(&format!("fn f(&self) {{ }}"))
+    ast_from_text("fn f(&self) { }")
 }
 
 pub fn ret_type(ty: ast::Type) -> ast::RetType {
index 99ef5c264fe9b4593d097ddf237477a99e9bcd92..40704067022ad9c81ea86d671dc7aef488bbce6b 100644 (file)
@@ -167,36 +167,6 @@ fn name(&self) -> Option<ast::Name> {
 
 impl AttrsOwner for Macro {}
 
-/// Basically an owned `dyn AttrsOwner` without extra boxing.
-pub struct AttrsOwnerNode {
-    node: SyntaxNode,
-}
-
-impl AttrsOwnerNode {
-    pub fn new<N: AttrsOwner>(node: N) -> Self {
-        AttrsOwnerNode { node: node.syntax().clone() }
-    }
-}
-
-impl AttrsOwner for AttrsOwnerNode {}
-impl AstNode for AttrsOwnerNode {
-    fn can_cast(_: SyntaxKind) -> bool
-    where
-        Self: Sized,
-    {
-        false
-    }
-    fn cast(_: SyntaxNode) -> Option<Self>
-    where
-        Self: Sized,
-    {
-        None
-    }
-    fn syntax(&self) -> &SyntaxNode {
-        &self.node
-    }
-}
-
 #[derive(Debug, Clone, PartialEq, Eq)]
 pub enum AttrKind {
     Inner,
@@ -602,16 +572,7 @@ pub fn kind(&self) -> StructKind {
 
 impl ast::Item {
     pub fn generic_param_list(&self) -> Option<ast::GenericParamList> {
-        match self {
-            ast::Item::Enum(it) => it.generic_param_list(),
-            ast::Item::Fn(it) => it.generic_param_list(),
-            ast::Item::Impl(it) => it.generic_param_list(),
-            ast::Item::Struct(it) => it.generic_param_list(),
-            ast::Item::Trait(it) => it.generic_param_list(),
-            ast::Item::TypeAlias(it) => it.generic_param_list(),
-            ast::Item::Union(it) => it.generic_param_list(),
-            _ => None,
-        }
+        ast::DynGenericParamsOwner::cast(self.syntax().clone())?.generic_param_list()
     }
 }
 
@@ -835,6 +796,16 @@ pub fn right_delimiter_token(&self) -> Option<SyntaxToken> {
             .into_token()
             .filter(|it| matches!(it.kind(), T!['}'] | T![')'] | T![']']))
     }
+
+    pub fn parent_meta(&self) -> Option<ast::Meta> {
+        self.syntax().parent().and_then(ast::Meta::cast)
+    }
+}
+
+impl ast::Meta {
+    pub fn parent_attr(&self) -> Option<ast::Attr> {
+        self.syntax().parent().and_then(ast::Attr::cast)
+    }
 }
 
 impl ast::GenericParamList {
index c79742d8efb3ea2c4dde471fa067244569a6a0a9..ea0de4f28d43856a885ca6e8a2d6aa35ec53df58 100644 (file)
@@ -52,8 +52,8 @@ macro_rules! eprintln {
     ptr::{AstPtr, SyntaxNodePtr},
     syntax_error::SyntaxError,
     syntax_node::{
-        SyntaxElement, SyntaxElementChildren, SyntaxNode, SyntaxNodeChildren, SyntaxToken,
-        SyntaxTreeBuilder,
+        PreorderWithTokens, SyntaxElement, SyntaxElementChildren, SyntaxNode, SyntaxNodeChildren,
+        SyntaxToken, SyntaxTreeBuilder,
     },
     token_text::TokenText,
 };
index 8f643b2284e14d3c2f35ab3c6f1993b9a452bad8..6e838dd2292653e43bf520dea8807c32509ee846 100644 (file)
@@ -31,6 +31,7 @@ fn kind_to_raw(kind: SyntaxKind) -> rowan::SyntaxKind {
 pub type SyntaxElement = rowan::SyntaxElement<RustLanguage>;
 pub type SyntaxNodeChildren = rowan::SyntaxNodeChildren<RustLanguage>;
 pub type SyntaxElementChildren = rowan::SyntaxElementChildren<RustLanguage>;
+pub type PreorderWithTokens = rowan::api::PreorderWithTokens<RustLanguage>;
 
 #[derive(Default)]
 pub struct SyntaxTreeBuilder {
index a6624a8ab12de06c60ec01352f67374a0145db56..1a0377830be1f345e6f152e251579874186c7377 100644 (file)
@@ -8,6 +8,7 @@
     fmt::Write,
 };
 
+use itertools::Itertools;
 use proc_macro2::{Punct, Spacing};
 use quote::{format_ident, quote};
 use ungrammar::{rust_grammar, Grammar, Rule};
@@ -208,6 +209,58 @@ fn from(node: #variants) -> #name {
         })
         .unzip();
 
+    let (dyn_node_defs, dyn_node_boilerplate_impls): (Vec<_>, Vec<_>) = grammar
+        .nodes
+        .iter()
+        .flat_map(|node| node.traits.iter().map(move |t| (t, node)))
+        .into_group_map()
+        .into_iter()
+        .sorted_by_key(|(k, _)| k.clone())
+        .map(|(trait_name, nodes)| {
+            let name = format_ident!("Dyn{}", trait_name);
+            let trait_name = format_ident!("{}", trait_name);
+            let kinds: Vec<_> = nodes
+                .iter()
+                .map(|name| format_ident!("{}", to_upper_snake_case(&name.name.to_string())))
+                .collect();
+
+            (
+                quote! {
+                    #[pretty_doc_comment_placeholder_workaround]
+                    #[derive(Debug, Clone, PartialEq, Eq, Hash)]
+                    pub struct #name {
+                        pub(crate) syntax: SyntaxNode,
+                    }
+                    impl ast::#trait_name for #name {}
+                },
+                quote! {
+                    impl #name {
+                        #[inline]
+                        pub fn new<T: ast::#trait_name>(node: T) -> #name {
+                            #name {
+                                syntax: node.syntax().clone()
+                            }
+                        }
+                    }
+                    impl AstNode for #name {
+                        fn can_cast(kind: SyntaxKind) -> bool {
+                            match kind {
+                                #(#kinds)|* => true,
+                                _ => false,
+                            }
+                        }
+                        fn cast(syntax: SyntaxNode) -> Option<Self> {
+                            Self::can_cast(syntax.kind()).then(|| #name { syntax })
+                        }
+                        fn syntax(&self) -> &SyntaxNode {
+                            &self.syntax
+                        }
+                    }
+                },
+            )
+        })
+        .unzip();
+
     let enum_names = grammar.enums.iter().map(|it| &it.name);
     let node_names = grammar.nodes.iter().map(|it| &it.name);
 
@@ -244,8 +297,10 @@ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
 
         #(#node_defs)*
         #(#enum_defs)*
+        #(#dyn_node_defs)*
         #(#node_boilerplate_impls)*
         #(#enum_boilerplate_impls)*
+        #(#dyn_node_boilerplate_impls)*
         #(#display_impls)*
     };
 
index f3e8b321a7ee8d57dce92bfe8e4dabdd569b261f..c8ba638c523116362cb8cbc200daa6f2aae3f1b1 100644 (file)
@@ -21,9 +21,9 @@ pub(crate) fn owned(green: GreenToken) -> Self {
     }
 
     pub fn as_str(&self) -> &str {
-        match self.0 {
-            Repr::Borrowed(it) => it,
-            Repr::Owned(ref green) => green.text(),
+        match &self.0 {
+            &Repr::Borrowed(it) => it,
+            Repr::Owned(green) => green.text(),
         }
     }
 }
index ea71da04226916abd2b80b8b1abfea3113dde4da..8d1c6f5d26ee7f8d63cbcaef40fd44f003ff1927 100644 (file)
@@ -211,13 +211,10 @@ fn int_token(name_ref: Option<ast::NameRef>) -> Option<SyntaxToken> {
 }
 
 fn validate_visibility(vis: ast::Visibility, errors: &mut Vec<SyntaxError>) {
-    if vis.in_token().is_none() {
-        if vis.path().and_then(|p| p.as_single_name_ref()).and_then(|n| n.ident_token()).is_some() {
-            errors.push(SyntaxError::new(
-                "incorrect visibility restriction",
-                vis.syntax.text_range(),
-            ));
-        }
+    let path_without_in_token = vis.in_token().is_none()
+        && vis.path().and_then(|p| p.as_single_name_ref()).and_then(|n| n.ident_token()).is_some();
+    if path_without_in_token {
+        errors.push(SyntaxError::new("incorrect visibility restriction", vis.syntax.text_range()));
     }
     let parent = match vis.syntax().parent() {
         Some(it) => it,
diff --git a/crates/syntax/test_data/parser/err/0049_double_fish.rast b/crates/syntax/test_data/parser/err/0049_double_fish.rast
new file mode 100644 (file)
index 0000000..51e3654
--- /dev/null
@@ -0,0 +1,121 @@
+SOURCE_FILE@0..90
+  FN@0..40
+    FN_KW@0..2 "fn"
+    WHITESPACE@2..3 " "
+    NAME@3..4
+      IDENT@3..4 "f"
+    PARAM_LIST@4..6
+      L_PAREN@4..5 "("
+      R_PAREN@5..6 ")"
+    WHITESPACE@6..7 " "
+    BLOCK_EXPR@7..40
+      L_CURLY@7..8 "{"
+      WHITESPACE@8..13 "\n    "
+      EXPR_STMT@13..31
+        PATH_EXPR@13..31
+          PATH@13..31
+            PATH_SEGMENT@13..31
+              NAME_REF@13..14
+                IDENT@13..14 "S"
+              GENERIC_ARG_LIST@14..31
+                COLON2@14..16 "::"
+                L_ANGLE@16..17 "<"
+                TYPE_ARG@17..31
+                  PATH_TYPE@17..31
+                    PATH@17..31
+                      PATH@17..28
+                        PATH_SEGMENT@17..28
+                          NAME_REF@17..21
+                            IDENT@17..21 "Item"
+                          GENERIC_ARG_LIST@21..28
+                            COLON2@21..23 "::"
+                            L_ANGLE@23..24 "<"
+                            TYPE_ARG@24..27
+                              PATH_TYPE@24..27
+                                PATH@24..27
+                                  PATH_SEGMENT@24..27
+                                    NAME_REF@24..27
+                                      IDENT@24..27 "lol"
+                            R_ANGLE@27..28 ">"
+                      COLON2@28..30 "::"
+                      ERROR@30..31
+                        L_ANGLE@30..31 "<"
+      BIN_EXPR@31..38
+        PATH_EXPR@31..35
+          PATH@31..35
+            PATH_SEGMENT@31..35
+              NAME_REF@31..35
+                IDENT@31..35 "nope"
+        SHR@35..37 ">>"
+        ERROR@37..38
+          SEMICOLON@37..38 ";"
+      WHITESPACE@38..39 "\n"
+      R_CURLY@39..40 "}"
+  WHITESPACE@40..42 "\n\n"
+  FN@42..89
+    FN_KW@42..44 "fn"
+    WHITESPACE@44..45 " "
+    NAME@45..46
+      IDENT@45..46 "g"
+    PARAM_LIST@46..48
+      L_PAREN@46..47 "("
+      R_PAREN@47..48 ")"
+    WHITESPACE@48..49 " "
+    BLOCK_EXPR@49..89
+      L_CURLY@49..50 "{"
+      WHITESPACE@50..55 "\n    "
+      LET_STMT@55..76
+        LET_KW@55..58 "let"
+        WHITESPACE@58..59 " "
+        WILDCARD_PAT@59..60
+          UNDERSCORE@59..60 "_"
+        COLON@60..61 ":"
+        WHITESPACE@61..62 " "
+        PATH_TYPE@62..76
+          PATH@62..76
+            PATH@62..73
+              PATH_SEGMENT@62..73
+                NAME_REF@62..66
+                  IDENT@62..66 "Item"
+                GENERIC_ARG_LIST@66..73
+                  COLON2@66..68 "::"
+                  L_ANGLE@68..69 "<"
+                  TYPE_ARG@69..72
+                    PATH_TYPE@69..72
+                      PATH@69..72
+                        PATH_SEGMENT@69..72
+                          NAME_REF@69..72
+                            IDENT@69..72 "lol"
+                  R_ANGLE@72..73 ">"
+            COLON2@73..75 "::"
+            ERROR@75..76
+              L_ANGLE@75..76 "<"
+      EXPR_STMT@76..83
+        BIN_EXPR@76..83
+          PATH_EXPR@76..80
+            PATH@76..80
+              PATH_SEGMENT@76..80
+                NAME_REF@76..80
+                  IDENT@76..80 "nope"
+          R_ANGLE@80..81 ">"
+          WHITESPACE@81..82 " "
+          ERROR@82..83
+            EQ@82..83 "="
+      WHITESPACE@83..84 " "
+      EXPR_STMT@84..87
+        TUPLE_EXPR@84..86
+          L_PAREN@84..85 "("
+          R_PAREN@85..86 ")"
+        SEMICOLON@86..87 ";"
+      WHITESPACE@87..88 "\n"
+      R_CURLY@88..89 "}"
+  WHITESPACE@89..90 "\n"
+error 30..30: expected identifier
+error 31..31: expected COMMA
+error 31..31: expected R_ANGLE
+error 31..31: expected SEMICOLON
+error 37..37: expected expression
+error 75..75: expected identifier
+error 76..76: expected SEMICOLON
+error 82..82: expected expression
+error 83..83: expected SEMICOLON
diff --git a/crates/syntax/test_data/parser/err/0049_double_fish.rs b/crates/syntax/test_data/parser/err/0049_double_fish.rs
new file mode 100644 (file)
index 0000000..31c12bf
--- /dev/null
@@ -0,0 +1,7 @@
+fn f() {
+    S::<Item::<lol>::<nope>>;
+}
+
+fn g() {
+    let _: Item::<lol>::<nope> = ();
+}
diff --git a/crates/syntax/test_data/parser/inline/err/0013_anonymous_static.rast b/crates/syntax/test_data/parser/inline/err/0013_anonymous_static.rast
new file mode 100644 (file)
index 0000000..8d761b9
--- /dev/null
@@ -0,0 +1,21 @@
+SOURCE_FILE@0..19
+  STATIC@0..18
+    STATIC_KW@0..6 "static"
+    WHITESPACE@6..7 " "
+    ERROR@7..8
+      UNDERSCORE@7..8 "_"
+    COLON@8..9 ":"
+    WHITESPACE@9..10 " "
+    PATH_TYPE@10..13
+      PATH@10..13
+        PATH_SEGMENT@10..13
+          NAME_REF@10..13
+            IDENT@10..13 "i32"
+    WHITESPACE@13..14 " "
+    EQ@14..15 "="
+    WHITESPACE@15..16 " "
+    LITERAL@16..17
+      INT_NUMBER@16..17 "5"
+    SEMICOLON@17..18 ";"
+  WHITESPACE@18..19 "\n"
+error 7..7: expected a name
diff --git a/crates/syntax/test_data/parser/inline/err/0013_anonymous_static.rs b/crates/syntax/test_data/parser/inline/err/0013_anonymous_static.rs
new file mode 100644 (file)
index 0000000..df8cecb
--- /dev/null
@@ -0,0 +1 @@
+static _: i32 = 5;
diff --git a/crates/syntax/test_data/parser/inline/err/0013_static_underscore.rast b/crates/syntax/test_data/parser/inline/err/0013_static_underscore.rast
deleted file mode 100644 (file)
index 8d761b9..0000000
+++ /dev/null
@@ -1,21 +0,0 @@
-SOURCE_FILE@0..19
-  STATIC@0..18
-    STATIC_KW@0..6 "static"
-    WHITESPACE@6..7 " "
-    ERROR@7..8
-      UNDERSCORE@7..8 "_"
-    COLON@8..9 ":"
-    WHITESPACE@9..10 " "
-    PATH_TYPE@10..13
-      PATH@10..13
-        PATH_SEGMENT@10..13
-          NAME_REF@10..13
-            IDENT@10..13 "i32"
-    WHITESPACE@13..14 " "
-    EQ@14..15 "="
-    WHITESPACE@15..16 " "
-    LITERAL@16..17
-      INT_NUMBER@16..17 "5"
-    SEMICOLON@17..18 ";"
-  WHITESPACE@18..19 "\n"
-error 7..7: expected a name
diff --git a/crates/syntax/test_data/parser/inline/err/0013_static_underscore.rs b/crates/syntax/test_data/parser/inline/err/0013_static_underscore.rs
deleted file mode 100644 (file)
index df8cecb..0000000
+++ /dev/null
@@ -1 +0,0 @@
-static _: i32 = 5;
index 970826739fd437be3ab1a7096e504f3f360228a3..9bee074b7b25720d4f107e984809f3b852d8fb77 100644 (file)
-SOURCE_FILE@0..248
-  USE@0..58
+SOURCE_FILE@0..15
+  USE@0..14
     USE_KW@0..3 "use"
     WHITESPACE@3..4 " "
-    USE_TREE@4..57
-      USE_TREE_LIST@4..57
+    USE_TREE@4..13
+      USE_TREE_LIST@4..13
         L_CURLY@4..5 "{"
-        USE_TREE@5..28
-          PATH@5..28
-            PATH@5..22
-              PATH@5..16
-                PATH@5..10
-                  PATH_SEGMENT@5..10
-                    NAME_REF@5..10
-                      CRATE_KW@5..10 "crate"
-                COLON2@10..12 "::"
-                PATH_SEGMENT@12..16
-                  NAME_REF@12..16
-                    IDENT@12..16 "path"
-              COLON2@16..18 "::"
-              PATH_SEGMENT@18..22
-                NAME_REF@18..22
-                  IDENT@18..22 "from"
-            COLON2@22..24 "::"
-            PATH_SEGMENT@24..28
-              NAME_REF@24..28
-                IDENT@24..28 "root"
-        COMMA@28..29 ","
-        WHITESPACE@29..30 " "
-        USE_TREE@30..56
-          PATH@30..56
-            PATH@30..44
-              PATH@30..38
-                PATH@30..32
-                  PATH_SEGMENT@30..32
-                    NAME_REF@30..32
-                      IDENT@30..32 "or"
-                COLON2@32..34 "::"
-                PATH_SEGMENT@34..38
-                  NAME_REF@34..38
-                    IDENT@34..38 "path"
-              COLON2@38..40 "::"
-              PATH_SEGMENT@40..44
-                NAME_REF@40..44
-                  IDENT@40..44 "from"
-            COLON2@44..46 "::"
-            PATH_SEGMENT@46..56
-              NAME_REF@46..56
-                IDENT@46..56 "crate_name"
-        R_CURLY@56..57 "}"
-    SEMICOLON@57..58 ";"
-  WHITESPACE@58..59 " "
-  USE@59..121
-    COMMENT@59..97 "// Rust 2018 (with a  ..."
-    WHITESPACE@97..98 "\n"
-    USE_KW@98..101 "use"
-    WHITESPACE@101..102 " "
-    USE_TREE@102..120
-      USE_TREE_LIST@102..120
-        L_CURLY@102..103 "{"
-        USE_TREE@103..119
-          PATH@103..119
-            PATH@103..113
-              PATH@103..107
-                PATH_SEGMENT@103..107
-                  NAME_REF@103..107
-                    IDENT@103..107 "path"
-              COLON2@107..109 "::"
-              PATH_SEGMENT@109..113
-                NAME_REF@109..113
-                  IDENT@109..113 "from"
-            COLON2@113..115 "::"
-            PATH_SEGMENT@115..119
-              NAME_REF@115..119
-                IDENT@115..119 "root"
-        R_CURLY@119..120 "}"
-    SEMICOLON@120..121 ";"
-  WHITESPACE@121..122 " "
-  USE@122..165
-    COMMENT@122..134 "// Rust 2015"
-    WHITESPACE@134..135 "\n"
-    USE_KW@135..138 "use"
-    WHITESPACE@138..139 " "
-    USE_TREE@139..164
-      COLON2@139..141 "::"
-      USE_TREE_LIST@141..164
-        L_CURLY@141..142 "{"
-        USE_TREE@142..163
-          PATH@142..163
-            PATH@142..157
-              PATH@142..146
-                PATH_SEGMENT@142..146
-                  NAME_REF@142..146
-                    IDENT@142..146 "some"
-              COLON2@146..148 "::"
-              PATH_SEGMENT@148..157
-                NAME_REF@148..157
-                  IDENT@148..157 "arbitrary"
-            COLON2@157..159 "::"
-            PATH_SEGMENT@159..163
-              NAME_REF@159..163
-                IDENT@159..163 "path"
-        R_CURLY@163..164 "}"
-    SEMICOLON@164..165 ";"
-  WHITESPACE@165..166 " "
-  USE@166..204
-    COMMENT@166..178 "// Rust 2015"
-    WHITESPACE@178..179 "\n"
-    USE_KW@179..182 "use"
-    WHITESPACE@182..183 " "
-    USE_TREE@183..203
-      COLON2@183..185 "::"
-      USE_TREE_LIST@185..203
-        L_CURLY@185..186 "{"
-        USE_TREE@186..202
-          USE_TREE_LIST@186..202
-            L_CURLY@186..187 "{"
-            USE_TREE@187..201
-              USE_TREE_LIST@187..201
-                L_CURLY@187..188 "{"
-                USE_TREE@188..200
-                  PATH@188..200
-                    PATH@188..192
-                      PATH_SEGMENT@188..192
-                        NAME_REF@188..192
-                          IDENT@188..192 "root"
-                    COLON2@192..194 "::"
-                    PATH_SEGMENT@194..200
-                      NAME_REF@194..200
-                        IDENT@194..200 "export"
-                R_CURLY@200..201 "}"
-            R_CURLY@201..202 "}"
-        R_CURLY@202..203 "}"
-    SEMICOLON@203..204 ";"
-  WHITESPACE@204..205 " "
-  COMMENT@205..247 "// Nonsensical but pe ..."
-  WHITESPACE@247..248 "\n"
+        USE_TREE@5..6
+          PATH@5..6
+            PATH_SEGMENT@5..6
+              NAME_REF@5..6
+                IDENT@5..6 "a"
+        COMMA@6..7 ","
+        WHITESPACE@7..8 " "
+        USE_TREE@8..9
+          PATH@8..9
+            PATH_SEGMENT@8..9
+              NAME_REF@8..9
+                IDENT@8..9 "b"
+        COMMA@9..10 ","
+        WHITESPACE@10..11 " "
+        USE_TREE@11..12
+          PATH@11..12
+            PATH_SEGMENT@11..12
+              NAME_REF@11..12
+                IDENT@11..12 "c"
+        R_CURLY@12..13 "}"
+    SEMICOLON@13..14 ";"
+  WHITESPACE@14..15 "\n"
index 02af4b446e1970750d07d1b39fe07e9f96e022d6..6fa175f542969de49b677581f41e92a73642c4a3 100644 (file)
@@ -1,4 +1 @@
-use {crate::path::from::root, or::path::from::crate_name}; // Rust 2018 (with a crate named `or`)
-use {path::from::root}; // Rust 2015
-use ::{some::arbitrary::path}; // Rust 2015
-use ::{{{root::export}}}; // Nonsensical but perfectly legal nesting
+use {a, b, c};
diff --git a/crates/syntax/test_data/parser/inline/ok/0020_use_star.rast b/crates/syntax/test_data/parser/inline/ok/0020_use_star.rast
deleted file mode 100644 (file)
index b3623c4..0000000
+++ /dev/null
@@ -1,59 +0,0 @@
-SOURCE_FILE@0..60
-  USE@0..6
-    USE_KW@0..3 "use"
-    WHITESPACE@3..4 " "
-    USE_TREE@4..5
-      STAR@4..5 "*"
-    SEMICOLON@5..6 ";"
-  WHITESPACE@6..7 "\n"
-  USE@7..15
-    USE_KW@7..10 "use"
-    WHITESPACE@10..11 " "
-    USE_TREE@11..14
-      COLON2@11..13 "::"
-      STAR@13..14 "*"
-    SEMICOLON@14..15 ";"
-  WHITESPACE@15..16 "\n"
-  USE@16..36
-    USE_KW@16..19 "use"
-    WHITESPACE@19..20 " "
-    USE_TREE@20..35
-      PATH@20..30
-        PATH@20..24
-          PATH_SEGMENT@20..24
-            NAME_REF@20..24
-              IDENT@20..24 "some"
-        COLON2@24..26 "::"
-        PATH_SEGMENT@26..30
-          NAME_REF@26..30
-            IDENT@26..30 "path"
-      COLON2@30..32 "::"
-      USE_TREE_LIST@32..35
-        L_CURLY@32..33 "{"
-        USE_TREE@33..34
-          STAR@33..34 "*"
-        R_CURLY@34..35 "}"
-    SEMICOLON@35..36 ";"
-  WHITESPACE@36..37 "\n"
-  USE@37..59
-    USE_KW@37..40 "use"
-    WHITESPACE@40..41 " "
-    USE_TREE@41..58
-      PATH@41..51
-        PATH@41..45
-          PATH_SEGMENT@41..45
-            NAME_REF@41..45
-              IDENT@41..45 "some"
-        COLON2@45..47 "::"
-        PATH_SEGMENT@47..51
-          NAME_REF@47..51
-            IDENT@47..51 "path"
-      COLON2@51..53 "::"
-      USE_TREE_LIST@53..58
-        L_CURLY@53..54 "{"
-        USE_TREE@54..57
-          COLON2@54..56 "::"
-          STAR@56..57 "*"
-        R_CURLY@57..58 "}"
-    SEMICOLON@58..59 ";"
-  WHITESPACE@59..60 "\n"
diff --git a/crates/syntax/test_data/parser/inline/ok/0020_use_star.rs b/crates/syntax/test_data/parser/inline/ok/0020_use_star.rs
deleted file mode 100644 (file)
index 6f15769..0000000
+++ /dev/null
@@ -1,4 +0,0 @@
-use *;
-use ::*;
-use some::path::{*};
-use some::path::{::*};
diff --git a/crates/syntax/test_data/parser/inline/ok/0021_assoc_item_list.rast b/crates/syntax/test_data/parser/inline/ok/0021_assoc_item_list.rast
new file mode 100644 (file)
index 0000000..dc7f629
--- /dev/null
@@ -0,0 +1,79 @@
+SOURCE_FILE@0..89
+  IMPL@0..88
+    IMPL_KW@0..4 "impl"
+    WHITESPACE@4..5 " "
+    PATH_TYPE@5..6
+      PATH@5..6
+        PATH_SEGMENT@5..6
+          NAME_REF@5..6
+            IDENT@5..6 "F"
+    WHITESPACE@6..7 " "
+    ASSOC_ITEM_LIST@7..88
+      L_CURLY@7..8 "{"
+      WHITESPACE@8..13 "\n    "
+      TYPE_ALIAS@13..26
+        TYPE_KW@13..17 "type"
+        WHITESPACE@17..18 " "
+        NAME@18..19
+          IDENT@18..19 "A"
+        WHITESPACE@19..20 " "
+        EQ@20..21 "="
+        WHITESPACE@21..22 " "
+        PATH_TYPE@22..25
+          PATH@22..25
+            PATH_SEGMENT@22..25
+              NAME_REF@22..25
+                IDENT@22..25 "i32"
+        SEMICOLON@25..26 ";"
+      WHITESPACE@26..31 "\n    "
+      CONST@31..49
+        CONST_KW@31..36 "const"
+        WHITESPACE@36..37 " "
+        NAME@37..38
+          IDENT@37..38 "B"
+        COLON@38..39 ":"
+        WHITESPACE@39..40 " "
+        PATH_TYPE@40..43
+          PATH@40..43
+            PATH_SEGMENT@40..43
+              NAME_REF@40..43
+                IDENT@40..43 "i32"
+        WHITESPACE@43..44 " "
+        EQ@44..45 "="
+        WHITESPACE@45..46 " "
+        LITERAL@46..48
+          INT_NUMBER@46..48 "92"
+        SEMICOLON@48..49 ";"
+      WHITESPACE@49..54 "\n    "
+      FN@54..65
+        FN_KW@54..56 "fn"
+        WHITESPACE@56..57 " "
+        NAME@57..60
+          IDENT@57..60 "foo"
+        PARAM_LIST@60..62
+          L_PAREN@60..61 "("
+          R_PAREN@61..62 ")"
+        WHITESPACE@62..63 " "
+        BLOCK_EXPR@63..65
+          L_CURLY@63..64 "{"
+          R_CURLY@64..65 "}"
+      WHITESPACE@65..70 "\n    "
+      FN@70..86
+        FN_KW@70..72 "fn"
+        WHITESPACE@72..73 " "
+        NAME@73..76
+          IDENT@73..76 "bar"
+        PARAM_LIST@76..83
+          L_PAREN@76..77 "("
+          SELF_PARAM@77..82
+            AMP@77..78 "&"
+            NAME@78..82
+              SELF_KW@78..82 "self"
+          R_PAREN@82..83 ")"
+        WHITESPACE@83..84 " "
+        BLOCK_EXPR@84..86
+          L_CURLY@84..85 "{"
+          R_CURLY@85..86 "}"
+      WHITESPACE@86..87 "\n"
+      R_CURLY@87..88 "}"
+  WHITESPACE@88..89 "\n"
diff --git a/crates/syntax/test_data/parser/inline/ok/0021_assoc_item_list.rs b/crates/syntax/test_data/parser/inline/ok/0021_assoc_item_list.rs
new file mode 100644 (file)
index 0000000..f108514
--- /dev/null
@@ -0,0 +1,6 @@
+impl F {
+    type A = i32;
+    const B: i32 = 92;
+    fn foo() {}
+    fn bar(&self) {}
+}
diff --git a/crates/syntax/test_data/parser/inline/ok/0021_impl_item_list.rast b/crates/syntax/test_data/parser/inline/ok/0021_impl_item_list.rast
deleted file mode 100644 (file)
index dc7f629..0000000
+++ /dev/null
@@ -1,79 +0,0 @@
-SOURCE_FILE@0..89
-  IMPL@0..88
-    IMPL_KW@0..4 "impl"
-    WHITESPACE@4..5 " "
-    PATH_TYPE@5..6
-      PATH@5..6
-        PATH_SEGMENT@5..6
-          NAME_REF@5..6
-            IDENT@5..6 "F"
-    WHITESPACE@6..7 " "
-    ASSOC_ITEM_LIST@7..88
-      L_CURLY@7..8 "{"
-      WHITESPACE@8..13 "\n    "
-      TYPE_ALIAS@13..26
-        TYPE_KW@13..17 "type"
-        WHITESPACE@17..18 " "
-        NAME@18..19
-          IDENT@18..19 "A"
-        WHITESPACE@19..20 " "
-        EQ@20..21 "="
-        WHITESPACE@21..22 " "
-        PATH_TYPE@22..25
-          PATH@22..25
-            PATH_SEGMENT@22..25
-              NAME_REF@22..25
-                IDENT@22..25 "i32"
-        SEMICOLON@25..26 ";"
-      WHITESPACE@26..31 "\n    "
-      CONST@31..49
-        CONST_KW@31..36 "const"
-        WHITESPACE@36..37 " "
-        NAME@37..38
-          IDENT@37..38 "B"
-        COLON@38..39 ":"
-        WHITESPACE@39..40 " "
-        PATH_TYPE@40..43
-          PATH@40..43
-            PATH_SEGMENT@40..43
-              NAME_REF@40..43
-                IDENT@40..43 "i32"
-        WHITESPACE@43..44 " "
-        EQ@44..45 "="
-        WHITESPACE@45..46 " "
-        LITERAL@46..48
-          INT_NUMBER@46..48 "92"
-        SEMICOLON@48..49 ";"
-      WHITESPACE@49..54 "\n    "
-      FN@54..65
-        FN_KW@54..56 "fn"
-        WHITESPACE@56..57 " "
-        NAME@57..60
-          IDENT@57..60 "foo"
-        PARAM_LIST@60..62
-          L_PAREN@60..61 "("
-          R_PAREN@61..62 ")"
-        WHITESPACE@62..63 " "
-        BLOCK_EXPR@63..65
-          L_CURLY@63..64 "{"
-          R_CURLY@64..65 "}"
-      WHITESPACE@65..70 "\n    "
-      FN@70..86
-        FN_KW@70..72 "fn"
-        WHITESPACE@72..73 " "
-        NAME@73..76
-          IDENT@73..76 "bar"
-        PARAM_LIST@76..83
-          L_PAREN@76..77 "("
-          SELF_PARAM@77..82
-            AMP@77..78 "&"
-            NAME@78..82
-              SELF_KW@78..82 "self"
-          R_PAREN@82..83 ")"
-        WHITESPACE@83..84 " "
-        BLOCK_EXPR@84..86
-          L_CURLY@84..85 "{"
-          R_CURLY@85..86 "}"
-      WHITESPACE@86..87 "\n"
-      R_CURLY@87..88 "}"
-  WHITESPACE@88..89 "\n"
diff --git a/crates/syntax/test_data/parser/inline/ok/0021_impl_item_list.rs b/crates/syntax/test_data/parser/inline/ok/0021_impl_item_list.rs
deleted file mode 100644 (file)
index f108514..0000000
+++ /dev/null
@@ -1,6 +0,0 @@
-impl F {
-    type A = i32;
-    const B: i32 = 92;
-    fn foo() {}
-    fn bar(&self) {}
-}
diff --git a/crates/syntax/test_data/parser/inline/ok/0039_type_arg.rast b/crates/syntax/test_data/parser/inline/ok/0039_type_arg.rast
deleted file mode 100644 (file)
index 11efa23..0000000
+++ /dev/null
@@ -1,66 +0,0 @@
-SOURCE_FILE@0..59
-  TYPE_ALIAS@0..58
-    TYPE_KW@0..4 "type"
-    WHITESPACE@4..5 " "
-    NAME@5..6
-      IDENT@5..6 "A"
-    WHITESPACE@6..7 " "
-    EQ@7..8 "="
-    WHITESPACE@8..9 " "
-    PATH_TYPE@9..57
-      PATH@9..57
-        PATH_SEGMENT@9..57
-          NAME_REF@9..10
-            IDENT@9..10 "B"
-          GENERIC_ARG_LIST@10..57
-            L_ANGLE@10..11 "<"
-            LIFETIME_ARG@11..18
-              LIFETIME@11..18
-                LIFETIME_IDENT@11..18 "'static"
-            COMMA@18..19 ","
-            WHITESPACE@19..20 " "
-            TYPE_ARG@20..23
-              PATH_TYPE@20..23
-                PATH@20..23
-                  PATH_SEGMENT@20..23
-                    NAME_REF@20..23
-                      IDENT@20..23 "i32"
-            COMMA@23..24 ","
-            WHITESPACE@24..25 " "
-            CONST_ARG@25..26
-              LITERAL@25..26
-                INT_NUMBER@25..26 "1"
-            COMMA@26..27 ","
-            WHITESPACE@27..28 " "
-            CONST_ARG@28..33
-              BLOCK_EXPR@28..33
-                L_CURLY@28..29 "{"
-                WHITESPACE@29..30 " "
-                LITERAL@30..31
-                  INT_NUMBER@30..31 "2"
-                WHITESPACE@31..32 " "
-                R_CURLY@32..33 "}"
-            COMMA@33..34 ","
-            WHITESPACE@34..35 " "
-            ASSOC_TYPE_ARG@35..43
-              NAME_REF@35..39
-                IDENT@35..39 "Item"
-              EQ@39..40 "="
-              PATH_TYPE@40..43
-                PATH@40..43
-                  PATH_SEGMENT@40..43
-                    NAME_REF@40..43
-                      IDENT@40..43 "u64"
-            COMMA@43..44 ","
-            WHITESPACE@44..45 " "
-            CONST_ARG@45..49
-              LITERAL@45..49
-                TRUE_KW@45..49 "true"
-            COMMA@49..50 ","
-            WHITESPACE@50..51 " "
-            CONST_ARG@51..56
-              LITERAL@51..56
-                FALSE_KW@51..56 "false"
-            R_ANGLE@56..57 ">"
-    SEMICOLON@57..58 ";"
-  WHITESPACE@58..59 "\n"
diff --git a/crates/syntax/test_data/parser/inline/ok/0039_type_arg.rs b/crates/syntax/test_data/parser/inline/ok/0039_type_arg.rs
deleted file mode 100644 (file)
index 6a8721a..0000000
+++ /dev/null
@@ -1 +0,0 @@
-type A = B<'static, i32, 1, { 2 }, Item=u64, true, false>;
index 3638462f8b2664dc05099eb744c9d7cd70c50c12..f7814abaa4a2cb68da6dd230707e0a6510d06421 100644 (file)
-SOURCE_FILE@0..101
-  TRAIT@0..41
+SOURCE_FILE@0..30
+  TRAIT@0..29
     TRAIT_KW@0..5 "trait"
     WHITESPACE@5..6 " "
     NAME@6..7
       IDENT@6..7 "T"
-    GENERIC_PARAM_LIST@7..10
-      L_ANGLE@7..8 "<"
-      TYPE_PARAM@8..9
-        NAME@8..9
-          IDENT@8..9 "U"
-      R_ANGLE@9..10 ">"
-    COLON@10..11 ":"
-    WHITESPACE@11..12 " "
-    TYPE_BOUND_LIST@12..24
-      TYPE_BOUND@12..16
-        PATH_TYPE@12..16
-          PATH@12..16
-            PATH_SEGMENT@12..16
-              NAME_REF@12..16
-                IDENT@12..16 "Hash"
-      WHITESPACE@16..17 " "
-      PLUS@17..18 "+"
-      WHITESPACE@18..19 " "
-      TYPE_BOUND@19..24
-        PATH_TYPE@19..24
-          PATH@19..24
-            PATH_SEGMENT@19..24
-              NAME_REF@19..24
-                IDENT@19..24 "Clone"
-    WHITESPACE@24..25 " "
-    WHERE_CLAUSE@25..38
-      WHERE_KW@25..30 "where"
-      WHITESPACE@30..31 " "
-      WHERE_PRED@31..38
-        PATH_TYPE@31..32
-          PATH@31..32
-            PATH_SEGMENT@31..32
-              NAME_REF@31..32
-                IDENT@31..32 "U"
-        COLON@32..33 ":"
-        WHITESPACE@33..34 " "
-        TYPE_BOUND_LIST@34..38
-          TYPE_BOUND@34..38
-            PATH_TYPE@34..38
-              PATH@34..38
-                PATH_SEGMENT@34..38
-                  NAME_REF@34..38
-                    IDENT@34..38 "Copy"
-    WHITESPACE@38..39 " "
-    ASSOC_ITEM_LIST@39..41
-      L_CURLY@39..40 "{"
-      R_CURLY@40..41 "}"
-  WHITESPACE@41..42 "\n"
-  TRAIT@42..100
-    TRAIT_KW@42..47 "trait"
-    WHITESPACE@47..48 " "
-    NAME@48..49
-      IDENT@48..49 "X"
-    GENERIC_PARAM_LIST@49..69
-      L_ANGLE@49..50 "<"
-      TYPE_PARAM@50..68
-        NAME@50..51
-          IDENT@50..51 "U"
-        COLON@51..52 ":"
-        WHITESPACE@52..53 " "
-        TYPE_BOUND_LIST@53..68
-          TYPE_BOUND@53..58
-            PATH_TYPE@53..58
-              PATH@53..58
-                PATH_SEGMENT@53..58
-                  NAME_REF@53..58
-                    IDENT@53..58 "Debug"
-          WHITESPACE@58..59 " "
-          PLUS@59..60 "+"
-          WHITESPACE@60..61 " "
-          TYPE_BOUND@61..68
-            PATH_TYPE@61..68
-              PATH@61..68
-                PATH_SEGMENT@61..68
-                  NAME_REF@61..68
-                    IDENT@61..68 "Display"
-      R_ANGLE@68..69 ">"
-    COLON@69..70 ":"
-    WHITESPACE@70..71 " "
-    TYPE_BOUND_LIST@71..83
-      TYPE_BOUND@71..75
-        PATH_TYPE@71..75
-          PATH@71..75
-            PATH_SEGMENT@71..75
-              NAME_REF@71..75
-                IDENT@71..75 "Hash"
-      WHITESPACE@75..76 " "
-      PLUS@76..77 "+"
-      WHITESPACE@77..78 " "
-      TYPE_BOUND@78..83
-        PATH_TYPE@78..83
-          PATH@78..83
-            PATH_SEGMENT@78..83
-              NAME_REF@78..83
-                IDENT@78..83 "Clone"
-    WHITESPACE@83..84 " "
-    WHERE_CLAUSE@84..97
-      WHERE_KW@84..89 "where"
-      WHITESPACE@89..90 " "
-      WHERE_PRED@90..97
-        PATH_TYPE@90..91
-          PATH@90..91
-            PATH_SEGMENT@90..91
-              NAME_REF@90..91
-                IDENT@90..91 "U"
-        COLON@91..92 ":"
-        WHITESPACE@92..93 " "
-        TYPE_BOUND_LIST@93..97
-          TYPE_BOUND@93..97
-            PATH_TYPE@93..97
-              PATH@93..97
-                PATH_SEGMENT@93..97
-                  NAME_REF@93..97
-                    IDENT@93..97 "Copy"
-    WHITESPACE@97..98 " "
-    ASSOC_ITEM_LIST@98..100
-      L_CURLY@98..99 "{"
-      R_CURLY@99..100 "}"
-  WHITESPACE@100..101 "\n"
+    WHITESPACE@7..8 " "
+    ASSOC_ITEM_LIST@8..29
+      L_CURLY@8..9 "{"
+      WHITESPACE@9..10 " "
+      FN@10..27
+        FN_KW@10..12 "fn"
+        WHITESPACE@12..13 " "
+        NAME@13..16
+          IDENT@13..16 "new"
+        PARAM_LIST@16..18
+          L_PAREN@16..17 "("
+          R_PAREN@17..18 ")"
+        WHITESPACE@18..19 " "
+        RET_TYPE@19..26
+          THIN_ARROW@19..21 "->"
+          WHITESPACE@21..22 " "
+          PATH_TYPE@22..26
+            PATH@22..26
+              PATH_SEGMENT@22..26
+                NAME_REF@22..26
+                  IDENT@22..26 "Self"
+        SEMICOLON@26..27 ";"
+      WHITESPACE@27..28 " "
+      R_CURLY@28..29 "}"
+  WHITESPACE@29..30 "\n"
index 32761dd03d05e70a44ee2cc0d03e609a99cee13a..dcd9a71144fd53f7432f15bf5266598f8d3d76ac 100644 (file)
@@ -1,2 +1 @@
-trait T<U>: Hash + Clone where U: Copy {}
-trait X<U: Debug + Display>: Hash + Clone where U: Copy {}
+trait T { fn new() -> Self; }
diff --git a/crates/syntax/test_data/parser/inline/ok/0043_use_alias.rast b/crates/syntax/test_data/parser/inline/ok/0043_use_alias.rast
deleted file mode 100644 (file)
index 60b5172..0000000
+++ /dev/null
@@ -1,138 +0,0 @@
-SOURCE_FILE@0..198
-  USE@0..28
-    USE_KW@0..3 "use"
-    WHITESPACE@3..4 " "
-    USE_TREE@4..27
-      PATH@4..14
-        PATH@4..8
-          PATH_SEGMENT@4..8
-            NAME_REF@4..8
-              IDENT@4..8 "some"
-        COLON2@8..10 "::"
-        PATH_SEGMENT@10..14
-          NAME_REF@10..14
-            IDENT@10..14 "path"
-      WHITESPACE@14..15 " "
-      RENAME@15..27
-        AS_KW@15..17 "as"
-        WHITESPACE@17..18 " "
-        NAME@18..27
-          IDENT@18..27 "some_name"
-    SEMICOLON@27..28 ";"
-  WHITESPACE@28..29 "\n"
-  USE@29..181
-    USE_KW@29..32 "use"
-    WHITESPACE@32..33 " "
-    USE_TREE@33..180
-      PATH@33..37
-        PATH_SEGMENT@33..37
-          NAME_REF@33..37
-            IDENT@33..37 "some"
-      COLON2@37..39 "::"
-      USE_TREE_LIST@39..180
-        L_CURLY@39..40 "{"
-        WHITESPACE@40..42 "\n "
-        USE_TREE@42..72
-          PATH@42..53
-            PATH@42..47
-              PATH_SEGMENT@42..47
-                NAME_REF@42..47
-                  IDENT@42..47 "other"
-            COLON2@47..49 "::"
-            PATH_SEGMENT@49..53
-              NAME_REF@49..53
-                IDENT@49..53 "path"
-          WHITESPACE@53..54 " "
-          RENAME@54..72
-            AS_KW@54..56 "as"
-            WHITESPACE@56..57 " "
-            NAME@57..72
-              IDENT@57..72 "some_other_name"
-        COMMA@72..73 ","
-        WHITESPACE@73..75 "\n "
-        USE_TREE@75..108
-          PATH@75..90
-            PATH@75..84
-              PATH_SEGMENT@75..84
-                NAME_REF@75..84
-                  IDENT@75..84 "different"
-            COLON2@84..86 "::"
-            PATH_SEGMENT@86..90
-              NAME_REF@86..90
-                IDENT@86..90 "path"
-          WHITESPACE@90..91 " "
-          RENAME@91..108
-            AS_KW@91..93 "as"
-            WHITESPACE@93..94 " "
-            NAME@94..108
-              IDENT@94..108 "different_name"
-        COMMA@108..109 ","
-        WHITESPACE@109..111 "\n "
-        USE_TREE@111..129
-          PATH@111..129
-            PATH@111..123
-              PATH@111..114
-                PATH_SEGMENT@111..114
-                  NAME_REF@111..114
-                    IDENT@111..114 "yet"
-              COLON2@114..116 "::"
-              PATH_SEGMENT@116..123
-                NAME_REF@116..123
-                  IDENT@116..123 "another"
-            COLON2@123..125 "::"
-            PATH_SEGMENT@125..129
-              NAME_REF@125..129
-                IDENT@125..129 "path"
-        COMMA@129..130 ","
-        WHITESPACE@130..132 "\n "
-        USE_TREE@132..178
-          PATH@132..175
-            PATH@132..164
-              PATH@132..158
-                PATH@132..148
-                  PATH@132..144
-                    PATH@132..139
-                      PATH_SEGMENT@132..139
-                        NAME_REF@132..139
-                          IDENT@132..139 "running"
-                    COLON2@139..141 "::"
-                    PATH_SEGMENT@141..144
-                      NAME_REF@141..144
-                        IDENT@141..144 "out"
-                  COLON2@144..146 "::"
-                  PATH_SEGMENT@146..148
-                    NAME_REF@146..148
-                      IDENT@146..148 "of"
-                COLON2@148..150 "::"
-                PATH_SEGMENT@150..158
-                  NAME_REF@150..158
-                    IDENT@150..158 "synonyms"
-              COLON2@158..160 "::"
-              PATH_SEGMENT@160..164
-                NAME_REF@160..164
-                  IDENT@160..164 "for_"
-            COLON2@164..166 "::"
-            PATH_SEGMENT@166..175
-              NAME_REF@166..175
-                IDENT@166..175 "different"
-          COLON2@175..177 "::"
-          STAR@177..178 "*"
-        WHITESPACE@178..179 "\n"
-        R_CURLY@179..180 "}"
-    SEMICOLON@180..181 ";"
-  WHITESPACE@181..182 "\n"
-  USE@182..197
-    USE_KW@182..185 "use"
-    WHITESPACE@185..186 " "
-    USE_TREE@186..196
-      PATH@186..191
-        PATH_SEGMENT@186..191
-          NAME_REF@186..191
-            IDENT@186..191 "Trait"
-      WHITESPACE@191..192 " "
-      RENAME@192..196
-        AS_KW@192..194 "as"
-        WHITESPACE@194..195 " "
-        UNDERSCORE@195..196 "_"
-    SEMICOLON@196..197 ";"
-  WHITESPACE@197..198 "\n"
diff --git a/crates/syntax/test_data/parser/inline/ok/0043_use_alias.rs b/crates/syntax/test_data/parser/inline/ok/0043_use_alias.rs
deleted file mode 100644 (file)
index 9be50f8..0000000
+++ /dev/null
@@ -1,8 +0,0 @@
-use some::path as some_name;
-use some::{
- other::path as some_other_name,
- different::path as different_name,
- yet::another::path,
- running::out::of::synonyms::for_::different::*
-};
-use Trait as _;
index 402950bcc94a9dacfb50461ae7e0ffcebf951b17..453bef315849dcda25619f20b20879c207fe20f6 100644 (file)
@@ -1,45 +1,33 @@
-SOURCE_FILE@0..64
-  STRUCT@0..63
+SOURCE_FILE@0..28
+  STRUCT@0..27
     STRUCT_KW@0..6 "struct"
     WHITESPACE@6..7 " "
     NAME@7..8
       IDENT@7..8 "S"
     WHITESPACE@8..9 " "
-    RECORD_FIELD_LIST@9..63
+    RECORD_FIELD_LIST@9..27
       L_CURLY@9..10 "{"
-      WHITESPACE@10..15 "\n    "
-      RECORD_FIELD@15..60
-        ATTR@15..43
-          POUND@15..16 "#"
-          L_BRACK@16..17 "["
-          META@17..42
-            PATH@17..22
-              PATH_SEGMENT@17..22
-                NAME_REF@17..22
-                  IDENT@17..22 "serde"
-            TOKEN_TREE@22..42
-              L_PAREN@22..23 "("
-              IDENT@23..27 "with"
-              WHITESPACE@27..28 " "
-              EQ@28..29 "="
-              WHITESPACE@29..30 " "
-              STRING@30..41 "\"url_serde\""
-              R_PAREN@41..42 ")"
-          R_BRACK@42..43 "]"
-        WHITESPACE@43..48 "\n    "
-        VISIBILITY@48..51
-          PUB_KW@48..51 "pub"
-        WHITESPACE@51..52 " "
-        NAME@52..55
-          IDENT@52..55 "uri"
-        COLON@55..56 ":"
-        WHITESPACE@56..57 " "
-        PATH_TYPE@57..60
-          PATH@57..60
-            PATH_SEGMENT@57..60
-              NAME_REF@57..60
-                IDENT@57..60 "Uri"
-      COMMA@60..61 ","
-      WHITESPACE@61..62 "\n"
-      R_CURLY@62..63 "}"
-  WHITESPACE@63..64 "\n"
+      WHITESPACE@10..11 " "
+      RECORD_FIELD@11..25
+        ATTR@11..18
+          POUND@11..12 "#"
+          L_BRACK@12..13 "["
+          META@13..17
+            PATH@13..17
+              PATH_SEGMENT@13..17
+                NAME_REF@13..17
+                  IDENT@13..17 "attr"
+          R_BRACK@17..18 "]"
+        WHITESPACE@18..19 " "
+        NAME@19..20
+          IDENT@19..20 "f"
+        COLON@20..21 ":"
+        WHITESPACE@21..22 " "
+        PATH_TYPE@22..25
+          PATH@22..25
+            PATH_SEGMENT@22..25
+              NAME_REF@22..25
+                IDENT@22..25 "f32"
+      WHITESPACE@25..26 " "
+      R_CURLY@26..27 "}"
+  WHITESPACE@27..28 "\n"
index 4744d8ac06597ffd9908f2bf70c983037d3607e0..d7f0b4382dac61f6019d3a614788a7d8703f40fa 100644 (file)
@@ -1,4 +1 @@
-struct S {
-    #[serde(with = "url_serde")]
-    pub uri: Uri,
-}
+struct S { #[attr] f: f32 }
diff --git a/crates/syntax/test_data/parser/inline/ok/0063_impl_def_neg.rast b/crates/syntax/test_data/parser/inline/ok/0063_impl_def_neg.rast
deleted file mode 100644 (file)
index 4368930..0000000
+++ /dev/null
@@ -1,23 +0,0 @@
-SOURCE_FILE@0..20
-  IMPL@0..19
-    IMPL_KW@0..4 "impl"
-    WHITESPACE@4..5 " "
-    BANG@5..6 "!"
-    PATH_TYPE@6..10
-      PATH@6..10
-        PATH_SEGMENT@6..10
-          NAME_REF@6..10
-            IDENT@6..10 "Send"
-    WHITESPACE@10..11 " "
-    FOR_KW@11..14 "for"
-    WHITESPACE@14..15 " "
-    PATH_TYPE@15..16
-      PATH@15..16
-        PATH_SEGMENT@15..16
-          NAME_REF@15..16
-            IDENT@15..16 "X"
-    WHITESPACE@16..17 " "
-    ASSOC_ITEM_LIST@17..19
-      L_CURLY@17..18 "{"
-      R_CURLY@18..19 "}"
-  WHITESPACE@19..20 "\n"
diff --git a/crates/syntax/test_data/parser/inline/ok/0063_impl_def_neg.rs b/crates/syntax/test_data/parser/inline/ok/0063_impl_def_neg.rs
deleted file mode 100644 (file)
index b7527c8..0000000
+++ /dev/null
@@ -1 +0,0 @@
-impl !Send for X {}
diff --git a/crates/syntax/test_data/parser/inline/ok/0063_impl_item_neg.rast b/crates/syntax/test_data/parser/inline/ok/0063_impl_item_neg.rast
new file mode 100644 (file)
index 0000000..4ab3522
--- /dev/null
@@ -0,0 +1,23 @@
+SOURCE_FILE@0..20
+  IMPL@0..19
+    IMPL_KW@0..4 "impl"
+    WHITESPACE@4..5 " "
+    BANG@5..6 "!"
+    PATH_TYPE@6..10
+      PATH@6..10
+        PATH_SEGMENT@6..10
+          NAME_REF@6..10
+            IDENT@6..10 "Send"
+    WHITESPACE@10..11 " "
+    FOR_KW@11..14 "for"
+    WHITESPACE@14..15 " "
+    PATH_TYPE@15..16
+      PATH@15..16
+        PATH_SEGMENT@15..16
+          NAME_REF@15..16
+            IDENT@15..16 "S"
+    WHITESPACE@16..17 " "
+    ASSOC_ITEM_LIST@17..19
+      L_CURLY@17..18 "{"
+      R_CURLY@18..19 "}"
+  WHITESPACE@19..20 "\n"
diff --git a/crates/syntax/test_data/parser/inline/ok/0063_impl_item_neg.rs b/crates/syntax/test_data/parser/inline/ok/0063_impl_item_neg.rs
new file mode 100644 (file)
index 0000000..a7bd4b0
--- /dev/null
@@ -0,0 +1 @@
+impl !Send for S {}
diff --git a/crates/syntax/test_data/parser/inline/ok/0068_union_items.rast b/crates/syntax/test_data/parser/inline/ok/0068_union_items.rast
deleted file mode 100644 (file)
index 6589e47..0000000
+++ /dev/null
@@ -1,46 +0,0 @@
-SOURCE_FILE@0..51
-  UNION@0..12
-    UNION_KW@0..5 "union"
-    WHITESPACE@5..6 " "
-    NAME@6..9
-      IDENT@6..9 "Foo"
-    WHITESPACE@9..10 " "
-    RECORD_FIELD_LIST@10..12
-      L_CURLY@10..11 "{"
-      R_CURLY@11..12 "}"
-  WHITESPACE@12..13 "\n"
-  UNION@13..50
-    UNION_KW@13..18 "union"
-    WHITESPACE@18..19 " "
-    NAME@19..22
-      IDENT@19..22 "Foo"
-    WHITESPACE@22..23 " "
-    RECORD_FIELD_LIST@23..50
-      L_CURLY@23..24 "{"
-      WHITESPACE@24..29 "\n    "
-      RECORD_FIELD@29..35
-        NAME@29..30
-          IDENT@29..30 "a"
-        COLON@30..31 ":"
-        WHITESPACE@31..32 " "
-        PATH_TYPE@32..35
-          PATH@32..35
-            PATH_SEGMENT@32..35
-              NAME_REF@32..35
-                IDENT@32..35 "i32"
-      COMMA@35..36 ","
-      WHITESPACE@36..41 "\n    "
-      RECORD_FIELD@41..47
-        NAME@41..42
-          IDENT@41..42 "b"
-        COLON@42..43 ":"
-        WHITESPACE@43..44 " "
-        PATH_TYPE@44..47
-          PATH@44..47
-            PATH_SEGMENT@44..47
-              NAME_REF@44..47
-                IDENT@44..47 "f32"
-      COMMA@47..48 ","
-      WHITESPACE@48..49 "\n"
-      R_CURLY@49..50 "}"
-  WHITESPACE@50..51 "\n"
diff --git a/crates/syntax/test_data/parser/inline/ok/0068_union_items.rs b/crates/syntax/test_data/parser/inline/ok/0068_union_items.rs
deleted file mode 100644 (file)
index b7dd610..0000000
+++ /dev/null
@@ -1,5 +0,0 @@
-union Foo {}
-union Foo {
-    a: i32,
-    b: f32,
-}
diff --git a/crates/syntax/test_data/parser/inline/ok/0069_use_tree_list_after_path.rast b/crates/syntax/test_data/parser/inline/ok/0069_use_tree_list_after_path.rast
deleted file mode 100644 (file)
index 192a9cc..0000000
+++ /dev/null
@@ -1,39 +0,0 @@
-SOURCE_FILE@0..37
-  USE@0..18
-    USE_KW@0..3 "use"
-    WHITESPACE@3..4 " "
-    USE_TREE@4..17
-      PATH@4..9
-        PATH_SEGMENT@4..9
-          NAME_REF@4..9
-            CRATE_KW@4..9 "crate"
-      COLON2@9..11 "::"
-      USE_TREE_LIST@11..17
-        L_CURLY@11..12 "{"
-        USE_TREE@12..16
-          PATH@12..16
-            PATH_SEGMENT@12..16
-              NAME_REF@12..16
-                IDENT@12..16 "Item"
-        R_CURLY@16..17 "}"
-    SEMICOLON@17..18 ";"
-  WHITESPACE@18..19 "\n"
-  USE@19..36
-    USE_KW@19..22 "use"
-    WHITESPACE@22..23 " "
-    USE_TREE@23..35
-      PATH@23..27
-        PATH_SEGMENT@23..27
-          NAME_REF@23..27
-            SELF_KW@23..27 "self"
-      COLON2@27..29 "::"
-      USE_TREE_LIST@29..35
-        L_CURLY@29..30 "{"
-        USE_TREE@30..34
-          PATH@30..34
-            PATH_SEGMENT@30..34
-              NAME_REF@30..34
-                IDENT@30..34 "Item"
-        R_CURLY@34..35 "}"
-    SEMICOLON@35..36 ";"
-  WHITESPACE@36..37 "\n"
diff --git a/crates/syntax/test_data/parser/inline/ok/0069_use_tree_list_after_path.rs b/crates/syntax/test_data/parser/inline/ok/0069_use_tree_list_after_path.rs
deleted file mode 100644 (file)
index c0a3d63..0000000
+++ /dev/null
@@ -1,2 +0,0 @@
-use crate::{Item};
-use self::{Item};
diff --git a/crates/syntax/test_data/parser/inline/ok/0078_type_alias.rast b/crates/syntax/test_data/parser/inline/ok/0078_type_alias.rast
new file mode 100644 (file)
index 0000000..2befc83
--- /dev/null
@@ -0,0 +1,16 @@
+SOURCE_FILE@0..16
+  TYPE_ALIAS@0..15
+    TYPE_KW@0..4 "type"
+    WHITESPACE@4..5 " "
+    NAME@5..8
+      IDENT@5..8 "Foo"
+    WHITESPACE@8..9 " "
+    EQ@9..10 "="
+    WHITESPACE@10..11 " "
+    PATH_TYPE@11..14
+      PATH@11..14
+        PATH_SEGMENT@11..14
+          NAME_REF@11..14
+            IDENT@11..14 "Bar"
+    SEMICOLON@14..15 ";"
+  WHITESPACE@15..16 "\n"
diff --git a/crates/syntax/test_data/parser/inline/ok/0078_type_alias.rs b/crates/syntax/test_data/parser/inline/ok/0078_type_alias.rs
new file mode 100644 (file)
index 0000000..04c0344
--- /dev/null
@@ -0,0 +1 @@
+type Foo = Bar;
diff --git a/crates/syntax/test_data/parser/inline/ok/0078_type_item.rast b/crates/syntax/test_data/parser/inline/ok/0078_type_item.rast
deleted file mode 100644 (file)
index 2befc83..0000000
+++ /dev/null
@@ -1,16 +0,0 @@
-SOURCE_FILE@0..16
-  TYPE_ALIAS@0..15
-    TYPE_KW@0..4 "type"
-    WHITESPACE@4..5 " "
-    NAME@5..8
-      IDENT@5..8 "Foo"
-    WHITESPACE@8..9 " "
-    EQ@9..10 "="
-    WHITESPACE@10..11 " "
-    PATH_TYPE@11..14
-      PATH@11..14
-        PATH_SEGMENT@11..14
-          NAME_REF@11..14
-            IDENT@11..14 "Bar"
-    SEMICOLON@14..15 ";"
-  WHITESPACE@15..16 "\n"
diff --git a/crates/syntax/test_data/parser/inline/ok/0078_type_item.rs b/crates/syntax/test_data/parser/inline/ok/0078_type_item.rs
deleted file mode 100644 (file)
index 04c0344..0000000
+++ /dev/null
@@ -1 +0,0 @@
-type Foo = Bar;
diff --git a/crates/syntax/test_data/parser/inline/ok/0079_impl_def.rast b/crates/syntax/test_data/parser/inline/ok/0079_impl_def.rast
deleted file mode 100644 (file)
index 209711f..0000000
+++ /dev/null
@@ -1,14 +0,0 @@
-SOURCE_FILE@0..12
-  IMPL@0..11
-    IMPL_KW@0..4 "impl"
-    WHITESPACE@4..5 " "
-    PATH_TYPE@5..8
-      PATH@5..8
-        PATH_SEGMENT@5..8
-          NAME_REF@5..8
-            IDENT@5..8 "Foo"
-    WHITESPACE@8..9 " "
-    ASSOC_ITEM_LIST@9..11
-      L_CURLY@9..10 "{"
-      R_CURLY@10..11 "}"
-  WHITESPACE@11..12 "\n"
diff --git a/crates/syntax/test_data/parser/inline/ok/0079_impl_def.rs b/crates/syntax/test_data/parser/inline/ok/0079_impl_def.rs
deleted file mode 100644 (file)
index d6337f6..0000000
+++ /dev/null
@@ -1 +0,0 @@
-impl Foo {}
diff --git a/crates/syntax/test_data/parser/inline/ok/0079_impl_item.rast b/crates/syntax/test_data/parser/inline/ok/0079_impl_item.rast
new file mode 100644 (file)
index 0000000..6516a78
--- /dev/null
@@ -0,0 +1,14 @@
+SOURCE_FILE@0..10
+  IMPL@0..9
+    IMPL_KW@0..4 "impl"
+    WHITESPACE@4..5 " "
+    PATH_TYPE@5..6
+      PATH@5..6
+        PATH_SEGMENT@5..6
+          NAME_REF@5..6
+            IDENT@5..6 "S"
+    WHITESPACE@6..7 " "
+    ASSOC_ITEM_LIST@7..9
+      L_CURLY@7..8 "{"
+      R_CURLY@8..9 "}"
+  WHITESPACE@9..10 "\n"
diff --git a/crates/syntax/test_data/parser/inline/ok/0079_impl_item.rs b/crates/syntax/test_data/parser/inline/ok/0079_impl_item.rs
new file mode 100644 (file)
index 0000000..647799d
--- /dev/null
@@ -0,0 +1 @@
+impl S {}
diff --git a/crates/syntax/test_data/parser/inline/ok/0083_struct_items.rs b/crates/syntax/test_data/parser/inline/ok/0083_struct_items.rs
deleted file mode 100644 (file)
index 693e3f3..0000000
+++ /dev/null
@@ -1,8 +0,0 @@
-struct Foo;
-struct Foo {}
-struct Foo();
-struct Foo(String, usize);
-struct Foo {
-    a: i32,
-    b: f32,
-}
diff --git a/crates/syntax/test_data/parser/inline/ok/0110_use_path.rast b/crates/syntax/test_data/parser/inline/ok/0110_use_path.rast
deleted file mode 100644 (file)
index c9fad5f..0000000
+++ /dev/null
@@ -1,38 +0,0 @@
-SOURCE_FILE@0..154
-  USE@0..17
-    USE_KW@0..3 "use"
-    WHITESPACE@3..4 " "
-    USE_TREE@4..16
-      PATH@4..16
-        PATH_SEGMENT@4..16
-          COLON2@4..6 "::"
-          NAME_REF@6..16
-            IDENT@6..16 "crate_name"
-    SEMICOLON@16..17 ";"
-  WHITESPACE@17..18 " "
-  USE@18..61
-    COMMENT@18..45 "// Rust 2018 - All fl ..."
-    WHITESPACE@45..46 "\n"
-    USE_KW@46..49 "use"
-    WHITESPACE@49..50 " "
-    USE_TREE@50..60
-      PATH@50..60
-        PATH_SEGMENT@50..60
-          NAME_REF@50..60
-            IDENT@50..60 "crate_name"
-    SEMICOLON@60..61 ";"
-  WHITESPACE@61..62 " "
-  USE@62..124
-    COMMENT@62..91 "// Rust 2018 - Anchor ..."
-    WHITESPACE@91..92 "\n"
-    USE_KW@92..95 "use"
-    WHITESPACE@95..96 " "
-    USE_TREE@96..123
-      PATH@96..123
-        PATH_SEGMENT@96..123
-          NAME_REF@96..123
-            IDENT@96..123 "item_in_scope_or_crat ..."
-    SEMICOLON@123..124 ";"
-  WHITESPACE@124..125 " "
-  COMMENT@125..153 "// Rust 2018 - Unifor ..."
-  WHITESPACE@153..154 "\n"
diff --git a/crates/syntax/test_data/parser/inline/ok/0110_use_path.rs b/crates/syntax/test_data/parser/inline/ok/0110_use_path.rs
deleted file mode 100644 (file)
index 328e947..0000000
+++ /dev/null
@@ -1,3 +0,0 @@
-use ::crate_name; // Rust 2018 - All flavours
-use crate_name; // Rust 2018 - Anchored paths
-use item_in_scope_or_crate_name; // Rust 2018 - Uniform Paths
index 0e1594dc4852f8532853af2e0ad5eec48ebf3434..31961113634ebf0c789bd6ef5b398fff80b2d9c0 100644 (file)
@@ -1,64 +1,42 @@
-SOURCE_FILE@0..53
-  STRUCT@0..33
+SOURCE_FILE@0..31
+  STRUCT@0..30
     STRUCT_KW@0..6 "struct"
     WHITESPACE@6..7 " "
-    NAME@7..11
-      IDENT@7..11 "Test"
-    GENERIC_PARAM_LIST@11..14
-      L_ANGLE@11..12 "<"
-      TYPE_PARAM@12..13
-        NAME@12..13
-          IDENT@12..13 "T"
-      R_ANGLE@13..14 ">"
-    TUPLE_FIELD_LIST@14..17
-      L_PAREN@14..15 "("
-      TUPLE_FIELD@15..16
-        PATH_TYPE@15..16
-          PATH@15..16
-            PATH_SEGMENT@15..16
-              NAME_REF@15..16
-                IDENT@15..16 "T"
-      R_PAREN@16..17 ")"
-    WHITESPACE@17..18 " "
-    WHERE_CLAUSE@18..32
-      WHERE_KW@18..23 "where"
-      WHITESPACE@23..24 " "
-      WHERE_PRED@24..32
-        PATH_TYPE@24..25
-          PATH@24..25
-            PATH_SEGMENT@24..25
-              NAME_REF@24..25
-                IDENT@24..25 "T"
-        COLON@25..26 ":"
-        WHITESPACE@26..27 " "
-        TYPE_BOUND_LIST@27..32
-          TYPE_BOUND@27..32
-            PATH_TYPE@27..32
-              PATH@27..32
-                PATH_SEGMENT@27..32
-                  NAME_REF@27..32
-                    IDENT@27..32 "Clone"
-    SEMICOLON@32..33 ";"
-  WHITESPACE@33..34 "\n"
-  STRUCT@34..52
-    STRUCT_KW@34..40 "struct"
-    WHITESPACE@40..41 " "
-    NAME@41..45
-      IDENT@41..45 "Test"
-    GENERIC_PARAM_LIST@45..48
-      L_ANGLE@45..46 "<"
-      TYPE_PARAM@46..47
-        NAME@46..47
-          IDENT@46..47 "T"
-      R_ANGLE@47..48 ">"
-    TUPLE_FIELD_LIST@48..51
-      L_PAREN@48..49 "("
-      TUPLE_FIELD@49..50
-        PATH_TYPE@49..50
-          PATH@49..50
-            PATH_SEGMENT@49..50
-              NAME_REF@49..50
-                IDENT@49..50 "T"
-      R_PAREN@50..51 ")"
-    SEMICOLON@51..52 ";"
-  WHITESPACE@52..53 "\n"
+    NAME@7..8
+      IDENT@7..8 "S"
+    GENERIC_PARAM_LIST@8..11
+      L_ANGLE@8..9 "<"
+      TYPE_PARAM@9..10
+        NAME@9..10
+          IDENT@9..10 "T"
+      R_ANGLE@10..11 ">"
+    TUPLE_FIELD_LIST@11..14
+      L_PAREN@11..12 "("
+      TUPLE_FIELD@12..13
+        PATH_TYPE@12..13
+          PATH@12..13
+            PATH_SEGMENT@12..13
+              NAME_REF@12..13
+                IDENT@12..13 "T"
+      R_PAREN@13..14 ")"
+    WHITESPACE@14..15 " "
+    WHERE_CLAUSE@15..29
+      WHERE_KW@15..20 "where"
+      WHITESPACE@20..21 " "
+      WHERE_PRED@21..29
+        PATH_TYPE@21..22
+          PATH@21..22
+            PATH_SEGMENT@21..22
+              NAME_REF@21..22
+                IDENT@21..22 "T"
+        COLON@22..23 ":"
+        WHITESPACE@23..24 " "
+        TYPE_BOUND_LIST@24..29
+          TYPE_BOUND@24..29
+            PATH_TYPE@24..29
+              PATH@24..29
+                PATH_SEGMENT@24..29
+                  NAME_REF@24..29
+                    IDENT@24..29 "Clone"
+    SEMICOLON@29..30 ";"
+  WHITESPACE@30..31 "\n"
index ddd59016dc62f8e4b814e82a7a9fd0485e0aa057..a602e0018275a3748a84f7796a8e58283db250b1 100644 (file)
@@ -1,2 +1 @@
-struct Test<T>(T) where T: Clone;
-struct Test<T>(T);
+struct S<T>(T) where T: Clone;
index db2b645b067bda784eda1bb2fbacc29302d4be7f..9a0bcdc18e3d404533db01111ed95eec1af8d889 100644 (file)
@@ -1,42 +1,28 @@
-SOURCE_FILE@0..60
-  STRUCT@0..59
+SOURCE_FILE@0..24
+  STRUCT@0..23
     STRUCT_KW@0..6 "struct"
     WHITESPACE@6..7 " "
     NAME@7..8
       IDENT@7..8 "S"
     WHITESPACE@8..9 " "
-    TUPLE_FIELD_LIST@9..58
+    TUPLE_FIELD_LIST@9..22
       L_PAREN@9..10 "("
-      WHITESPACE@10..15 "\n    "
-      TUPLE_FIELD@15..55
-        ATTR@15..43
-          POUND@15..16 "#"
-          L_BRACK@16..17 "["
-          META@17..42
-            PATH@17..22
-              PATH_SEGMENT@17..22
-                NAME_REF@17..22
-                  IDENT@17..22 "serde"
-            TOKEN_TREE@22..42
-              L_PAREN@22..23 "("
-              IDENT@23..27 "with"
-              WHITESPACE@27..28 " "
-              EQ@28..29 "="
-              WHITESPACE@29..30 " "
-              STRING@30..41 "\"url_serde\""
-              R_PAREN@41..42 ")"
-          R_BRACK@42..43 "]"
-        WHITESPACE@43..48 "\n    "
-        VISIBILITY@48..51
-          PUB_KW@48..51 "pub"
-        WHITESPACE@51..52 " "
-        PATH_TYPE@52..55
-          PATH@52..55
-            PATH_SEGMENT@52..55
-              NAME_REF@52..55
-                IDENT@52..55 "Uri"
-      COMMA@55..56 ","
-      WHITESPACE@56..57 "\n"
-      R_PAREN@57..58 ")"
-    SEMICOLON@58..59 ";"
-  WHITESPACE@59..60 "\n"
+      TUPLE_FIELD@10..21
+        ATTR@10..17
+          POUND@10..11 "#"
+          L_BRACK@11..12 "["
+          META@12..16
+            PATH@12..16
+              PATH_SEGMENT@12..16
+                NAME_REF@12..16
+                  IDENT@12..16 "attr"
+          R_BRACK@16..17 "]"
+        WHITESPACE@17..18 " "
+        PATH_TYPE@18..21
+          PATH@18..21
+            PATH_SEGMENT@18..21
+              NAME_REF@18..21
+                IDENT@18..21 "f32"
+      R_PAREN@21..22 ")"
+    SEMICOLON@22..23 ";"
+  WHITESPACE@23..24 "\n"
index 635b9ac21af745a1bb1ef411c18ea2af36e624f1..648ffe5654810ae5e0b1f59240a677759a319f3c 100644 (file)
@@ -1,4 +1 @@
-struct S (
-    #[serde(with = "url_serde")]
-    pub Uri,
-);
+struct S (#[attr] f32);
diff --git a/crates/syntax/test_data/parser/inline/ok/0118_impl_inner_attributes.rast b/crates/syntax/test_data/parser/inline/ok/0118_impl_inner_attributes.rast
deleted file mode 100644 (file)
index 24ac1d6..0000000
+++ /dev/null
@@ -1,41 +0,0 @@
-SOURCE_FILE@0..94
-  ENUM@0..8
-    ENUM_KW@0..4 "enum"
-    WHITESPACE@4..5 " "
-    NAME@5..6
-      IDENT@5..6 "F"
-    VARIANT_LIST@6..8
-      L_CURLY@6..7 "{"
-      R_CURLY@7..8 "}"
-  WHITESPACE@8..9 "\n"
-  IMPL@9..93
-    IMPL_KW@9..13 "impl"
-    WHITESPACE@13..14 " "
-    PATH_TYPE@14..15
-      PATH@14..15
-        PATH_SEGMENT@14..15
-          NAME_REF@14..15
-            IDENT@14..15 "F"
-    WHITESPACE@15..16 " "
-    ASSOC_ITEM_LIST@16..93
-      L_CURLY@16..17 "{"
-      WHITESPACE@17..23 "\n     "
-      COMMENT@23..48 "//! This is a doc com ..."
-      WHITESPACE@48..54 "\n     "
-      ATTR@54..91
-        POUND@54..55 "#"
-        BANG@55..56 "!"
-        L_BRACK@56..57 "["
-        META@57..90
-          PATH@57..60
-            PATH_SEGMENT@57..60
-              NAME_REF@57..60
-                IDENT@57..60 "doc"
-          TOKEN_TREE@60..90
-            L_PAREN@60..61 "("
-            STRING@61..89 "\"This is also a doc c ..."
-            R_PAREN@89..90 ")"
-        R_BRACK@90..91 "]"
-      WHITESPACE@91..92 "\n"
-      R_CURLY@92..93 "}"
-  WHITESPACE@93..94 "\n"
diff --git a/crates/syntax/test_data/parser/inline/ok/0118_impl_inner_attributes.rs b/crates/syntax/test_data/parser/inline/ok/0118_impl_inner_attributes.rs
deleted file mode 100644 (file)
index 4d68cce..0000000
+++ /dev/null
@@ -1,5 +0,0 @@
-enum F{}
-impl F {
-     //! This is a doc comment
-     #![doc("This is also a doc comment")]
-}
diff --git a/crates/syntax/test_data/parser/inline/ok/0122_generic_lifetime_type_attribute.rast b/crates/syntax/test_data/parser/inline/ok/0122_generic_lifetime_type_attribute.rast
deleted file mode 100644 (file)
index 5682bd2..0000000
+++ /dev/null
@@ -1,69 +0,0 @@
-SOURCE_FILE@0..64
-  FN@0..63
-    FN_KW@0..2 "fn"
-    WHITESPACE@2..3 " "
-    NAME@3..6
-      IDENT@3..6 "foo"
-    GENERIC_PARAM_LIST@6..49
-      L_ANGLE@6..7 "<"
-      LIFETIME_PARAM@7..29
-        ATTR@7..26
-          POUND@7..8 "#"
-          L_BRACK@8..9 "["
-          META@9..25
-            PATH@9..15
-              PATH_SEGMENT@9..15
-                NAME_REF@9..15
-                  IDENT@9..15 "derive"
-            TOKEN_TREE@15..25
-              L_PAREN@15..16 "("
-              IDENT@16..24 "Lifetime"
-              R_PAREN@24..25 ")"
-          R_BRACK@25..26 "]"
-        WHITESPACE@26..27 " "
-        LIFETIME@27..29
-          LIFETIME_IDENT@27..29 "'a"
-      COMMA@29..30 ","
-      WHITESPACE@30..31 " "
-      TYPE_PARAM@31..48
-        ATTR@31..46
-          POUND@31..32 "#"
-          L_BRACK@32..33 "["
-          META@33..45
-            PATH@33..39
-              PATH_SEGMENT@33..39
-                NAME_REF@33..39
-                  IDENT@33..39 "derive"
-            TOKEN_TREE@39..45
-              L_PAREN@39..40 "("
-              IDENT@40..44 "Type"
-              R_PAREN@44..45 ")"
-          R_BRACK@45..46 "]"
-        WHITESPACE@46..47 " "
-        NAME@47..48
-          IDENT@47..48 "T"
-      R_ANGLE@48..49 ">"
-    PARAM_LIST@49..59
-      L_PAREN@49..50 "("
-      PARAM@50..58
-        WILDCARD_PAT@50..51
-          UNDERSCORE@50..51 "_"
-        COLON@51..52 ":"
-        WHITESPACE@52..53 " "
-        REF_TYPE@53..58
-          AMP@53..54 "&"
-          LIFETIME@54..56
-            LIFETIME_IDENT@54..56 "'a"
-          WHITESPACE@56..57 " "
-          PATH_TYPE@57..58
-            PATH@57..58
-              PATH_SEGMENT@57..58
-                NAME_REF@57..58
-                  IDENT@57..58 "T"
-      R_PAREN@58..59 ")"
-    WHITESPACE@59..60 " "
-    BLOCK_EXPR@60..63
-      L_CURLY@60..61 "{"
-      WHITESPACE@61..62 "\n"
-      R_CURLY@62..63 "}"
-  WHITESPACE@63..64 "\n"
diff --git a/crates/syntax/test_data/parser/inline/ok/0122_generic_lifetime_type_attribute.rs b/crates/syntax/test_data/parser/inline/ok/0122_generic_lifetime_type_attribute.rs
deleted file mode 100644 (file)
index e8fdf74..0000000
+++ /dev/null
@@ -1,2 +0,0 @@
-fn foo<#[derive(Lifetime)] 'a, #[derive(Type)] T>(_: &'a T) {
-}
index 6655aeab139ae97de085c60724a37dd69e2aa064..4d7b78d5caa3a1b7dce73c9a29af70f3c0c05e72 100644 (file)
@@ -1,45 +1,19 @@
-SOURCE_FILE@0..50
-  MACRO_DEF@0..28
+SOURCE_FILE@0..21
+  MACRO_DEF@0..20
     MACRO_KW@0..5 "macro"
     WHITESPACE@5..6 " "
     NAME@6..7
       IDENT@6..7 "m"
-    WHITESPACE@7..8 " "
-    TOKEN_TREE@8..28
-      L_CURLY@8..9 "{"
-      WHITESPACE@9..10 " "
-      TOKEN_TREE@10..20
-        L_PAREN@10..11 "("
-        DOLLAR@11..12 "$"
-        IDENT@12..13 "i"
-        COLON@13..14 ":"
-        IDENT@14..19 "ident"
-        R_PAREN@19..20 ")"
-      WHITESPACE@20..21 " "
-      EQ@21..22 "="
-      R_ANGLE@22..23 ">"
-      WHITESPACE@23..24 " "
-      TOKEN_TREE@24..26
-        L_CURLY@24..25 "{"
-        R_CURLY@25..26 "}"
-      WHITESPACE@26..27 " "
-      R_CURLY@27..28 "}"
-  WHITESPACE@28..29 "\n"
-  MACRO_DEF@29..49
-    MACRO_KW@29..34 "macro"
-    WHITESPACE@34..35 " "
-    NAME@35..36
-      IDENT@35..36 "m"
-    TOKEN_TREE@36..49
-      TOKEN_TREE@36..46
-        L_PAREN@36..37 "("
-        DOLLAR@37..38 "$"
-        IDENT@38..39 "i"
-        COLON@39..40 ":"
-        IDENT@40..45 "ident"
-        R_PAREN@45..46 ")"
-      WHITESPACE@46..47 " "
-      TOKEN_TREE@47..49
-        L_CURLY@47..48 "{"
-        R_CURLY@48..49 "}"
-  WHITESPACE@49..50 "\n"
+    TOKEN_TREE@7..20
+      TOKEN_TREE@7..17
+        L_PAREN@7..8 "("
+        DOLLAR@8..9 "$"
+        IDENT@9..10 "i"
+        COLON@10..11 ":"
+        IDENT@11..16 "ident"
+        R_PAREN@16..17 ")"
+      WHITESPACE@17..18 " "
+      TOKEN_TREE@18..20
+        L_CURLY@18..19 "{"
+        R_CURLY@19..20 "}"
+  WHITESPACE@20..21 "\n"
index 319a4e2aad2e91497293708be9b147e5a4a1539e..a014ae5464e5210421f7e58d9952b188bd7b8e71 100644 (file)
@@ -1,2 +1 @@
-macro m { ($i:ident) => {} }
 macro m($i:ident) {}
diff --git a/crates/syntax/test_data/parser/inline/ok/0148_pub_macro_def.rast b/crates/syntax/test_data/parser/inline/ok/0148_pub_macro_def.rast
deleted file mode 100644 (file)
index 1c527f6..0000000
+++ /dev/null
@@ -1,21 +0,0 @@
-SOURCE_FILE@0..24
-  MACRO_DEF@0..23
-    VISIBILITY@0..3
-      PUB_KW@0..3 "pub"
-    WHITESPACE@3..4 " "
-    MACRO_KW@4..9 "macro"
-    WHITESPACE@9..10 " "
-    NAME@10..11
-      IDENT@10..11 "m"
-    TOKEN_TREE@11..23
-      TOKEN_TREE@11..20
-        L_PAREN@11..12 "("
-        DOLLAR@12..13 "$"
-        COLON@13..14 ":"
-        IDENT@14..19 "ident"
-        R_PAREN@19..20 ")"
-      WHITESPACE@20..21 " "
-      TOKEN_TREE@21..23
-        L_CURLY@21..22 "{"
-        R_CURLY@22..23 "}"
-  WHITESPACE@23..24 "\n"
diff --git a/crates/syntax/test_data/parser/inline/ok/0148_pub_macro_def.rs b/crates/syntax/test_data/parser/inline/ok/0148_pub_macro_def.rs
deleted file mode 100644 (file)
index 3b2be59..0000000
+++ /dev/null
@@ -1 +0,0 @@
-pub macro m($:ident) {}
index dac50410e422c657317d5de50b6360fd218e1623..6bcbd0f1c64966fa5608e656b4048c6db5e13444 100644 (file)
@@ -1,4 +1,4 @@
-SOURCE_FILE@0..83
+SOURCE_FILE@0..19
   TRAIT@0..18
     TRAIT_KW@0..5 "trait"
     WHITESPACE@5..6 " "
@@ -31,98 +31,3 @@ SOURCE_FILE@0..83
                 R_ANGLE@16..17 ">"
     SEMICOLON@17..18 ";"
   WHITESPACE@18..19 "\n"
-  TRAIT@19..51
-    TRAIT_KW@19..24 "trait"
-    WHITESPACE@24..25 " "
-    NAME@25..26
-      IDENT@25..26 "Z"
-    GENERIC_PARAM_LIST@26..29
-      L_ANGLE@26..27 "<"
-      TYPE_PARAM@27..28
-        NAME@27..28
-          IDENT@27..28 "U"
-      R_ANGLE@28..29 ">"
-    WHITESPACE@29..30 " "
-    EQ@30..31 "="
-    WHITESPACE@31..32 " "
-    TYPE_BOUND_LIST@32..36
-      TYPE_BOUND@32..36
-        PATH_TYPE@32..36
-          PATH@32..36
-            PATH_SEGMENT@32..36
-              NAME_REF@32..33
-                IDENT@32..33 "T"
-              GENERIC_ARG_LIST@33..36
-                L_ANGLE@33..34 "<"
-                TYPE_ARG@34..35
-                  PATH_TYPE@34..35
-                    PATH@34..35
-                      PATH_SEGMENT@34..35
-                        NAME_REF@34..35
-                          IDENT@34..35 "U"
-                R_ANGLE@35..36 ">"
-    WHITESPACE@36..37 " "
-    WHERE_CLAUSE@37..50
-      WHERE_KW@37..42 "where"
-      WHITESPACE@42..43 " "
-      WHERE_PRED@43..50
-        PATH_TYPE@43..44
-          PATH@43..44
-            PATH_SEGMENT@43..44
-              NAME_REF@43..44
-                IDENT@43..44 "U"
-        COLON@44..45 ":"
-        WHITESPACE@45..46 " "
-        TYPE_BOUND_LIST@46..50
-          TYPE_BOUND@46..50
-            PATH_TYPE@46..50
-              PATH@46..50
-                PATH_SEGMENT@46..50
-                  NAME_REF@46..50
-                    IDENT@46..50 "Copy"
-    SEMICOLON@50..51 ";"
-  WHITESPACE@51..52 "\n"
-  TRAIT@52..82
-    TRAIT_KW@52..57 "trait"
-    WHITESPACE@57..58 " "
-    NAME@58..59
-      IDENT@58..59 "Z"
-    GENERIC_PARAM_LIST@59..62
-      L_ANGLE@59..60 "<"
-      TYPE_PARAM@60..61
-        NAME@60..61
-          IDENT@60..61 "U"
-      R_ANGLE@61..62 ">"
-    WHITESPACE@62..63 " "
-    EQ@63..64 "="
-    WHITESPACE@64..65 " "
-    TYPE_BOUND_LIST@65..65
-    WHERE_CLAUSE@65..81
-      WHERE_KW@65..70 "where"
-      WHITESPACE@70..71 " "
-      WHERE_PRED@71..81
-        PATH_TYPE@71..75
-          PATH@71..75
-            PATH_SEGMENT@71..75
-              NAME_REF@71..75
-                IDENT@71..75 "Self"
-        COLON@75..76 ":"
-        WHITESPACE@76..77 " "
-        TYPE_BOUND_LIST@77..81
-          TYPE_BOUND@77..81
-            PATH_TYPE@77..81
-              PATH@77..81
-                PATH_SEGMENT@77..81
-                  NAME_REF@77..78
-                    IDENT@77..78 "T"
-                  GENERIC_ARG_LIST@78..81
-                    L_ANGLE@78..79 "<"
-                    TYPE_ARG@79..80
-                      PATH_TYPE@79..80
-                        PATH@79..80
-                          PATH_SEGMENT@79..80
-                            NAME_REF@79..80
-                              IDENT@79..80 "U"
-                    R_ANGLE@80..81 ">"
-    SEMICOLON@81..82 ";"
-  WHITESPACE@82..83 "\n"
index 4bd428ee46985f1ce4791d79b7925c49b684c4c9..71d76789faed3062e267ba1017502c72be87d012 100644 (file)
@@ -1,3 +1 @@
 trait Z<U> = T<U>;
-trait Z<U> = T<U> where U: Copy;
-trait Z<U> = where Self: T<U>;
diff --git a/crates/syntax/test_data/parser/inline/ok/0152_impl.rast b/crates/syntax/test_data/parser/inline/ok/0152_impl.rast
deleted file mode 100644 (file)
index 7968cf9..0000000
+++ /dev/null
@@ -1,22 +0,0 @@
-SOURCE_FILE@0..16
-  IMPL@0..15
-    IMPL_KW@0..4 "impl"
-    WHITESPACE@4..5 " "
-    PATH_TYPE@5..6
-      PATH@5..6
-        PATH_SEGMENT@5..6
-          NAME_REF@5..6
-            IDENT@5..6 "T"
-    WHITESPACE@6..7 " "
-    FOR_KW@7..10 "for"
-    WHITESPACE@10..11 " "
-    PATH_TYPE@11..12
-      PATH@11..12
-        PATH_SEGMENT@11..12
-          NAME_REF@11..12
-            IDENT@11..12 "S"
-    WHITESPACE@12..13 " "
-    ASSOC_ITEM_LIST@13..15
-      L_CURLY@13..14 "{"
-      R_CURLY@14..15 "}"
-  WHITESPACE@15..16 "\n"
diff --git a/crates/syntax/test_data/parser/inline/ok/0152_impl.rs b/crates/syntax/test_data/parser/inline/ok/0152_impl.rs
deleted file mode 100644 (file)
index a1a550d..0000000
+++ /dev/null
@@ -1 +0,0 @@
-impl T for S {}
diff --git a/crates/syntax/test_data/parser/inline/ok/0153_trait.rast b/crates/syntax/test_data/parser/inline/ok/0153_trait.rast
deleted file mode 100644 (file)
index 9881e50..0000000
+++ /dev/null
@@ -1,11 +0,0 @@
-SOURCE_FILE@0..11
-  TRAIT@0..10
-    TRAIT_KW@0..5 "trait"
-    WHITESPACE@5..6 " "
-    NAME@6..7
-      IDENT@6..7 "T"
-    WHITESPACE@7..8 " "
-    ASSOC_ITEM_LIST@8..10
-      L_CURLY@8..9 "{"
-      R_CURLY@9..10 "}"
-  WHITESPACE@10..11 "\n"
diff --git a/crates/syntax/test_data/parser/inline/ok/0153_trait.rs b/crates/syntax/test_data/parser/inline/ok/0153_trait.rs
deleted file mode 100644 (file)
index 8d183db..0000000
+++ /dev/null
@@ -1 +0,0 @@
-trait T {}
diff --git a/crates/syntax/test_data/parser/inline/ok/0161_impl_def_const.rast b/crates/syntax/test_data/parser/inline/ok/0161_impl_def_const.rast
deleted file mode 100644 (file)
index dcd3953..0000000
+++ /dev/null
@@ -1,24 +0,0 @@
-SOURCE_FILE@0..25
-  IMPL@0..24
-    IMPL_KW@0..4 "impl"
-    WHITESPACE@4..5 " "
-    CONST_KW@5..10 "const"
-    WHITESPACE@10..11 " "
-    PATH_TYPE@11..15
-      PATH@11..15
-        PATH_SEGMENT@11..15
-          NAME_REF@11..15
-            IDENT@11..15 "Send"
-    WHITESPACE@15..16 " "
-    FOR_KW@16..19 "for"
-    WHITESPACE@19..20 " "
-    PATH_TYPE@20..21
-      PATH@20..21
-        PATH_SEGMENT@20..21
-          NAME_REF@20..21
-            IDENT@20..21 "X"
-    WHITESPACE@21..22 " "
-    ASSOC_ITEM_LIST@22..24
-      L_CURLY@22..23 "{"
-      R_CURLY@23..24 "}"
-  WHITESPACE@24..25 "\n"
diff --git a/crates/syntax/test_data/parser/inline/ok/0161_impl_def_const.rs b/crates/syntax/test_data/parser/inline/ok/0161_impl_def_const.rs
deleted file mode 100644 (file)
index 8d68864..0000000
+++ /dev/null
@@ -1 +0,0 @@
-impl const Send for X {}
diff --git a/crates/syntax/test_data/parser/inline/ok/0161_impl_item_const.rast b/crates/syntax/test_data/parser/inline/ok/0161_impl_item_const.rast
new file mode 100644 (file)
index 0000000..925dfa2
--- /dev/null
@@ -0,0 +1,24 @@
+SOURCE_FILE@0..25
+  IMPL@0..24
+    IMPL_KW@0..4 "impl"
+    WHITESPACE@4..5 " "
+    CONST_KW@5..10 "const"
+    WHITESPACE@10..11 " "
+    PATH_TYPE@11..15
+      PATH@11..15
+        PATH_SEGMENT@11..15
+          NAME_REF@11..15
+            IDENT@11..15 "Send"
+    WHITESPACE@15..16 " "
+    FOR_KW@16..19 "for"
+    WHITESPACE@19..20 " "
+    PATH_TYPE@20..21
+      PATH@20..21
+        PATH_SEGMENT@20..21
+          NAME_REF@20..21
+            IDENT@20..21 "S"
+    WHITESPACE@21..22 " "
+    ASSOC_ITEM_LIST@22..24
+      L_CURLY@22..23 "{"
+      R_CURLY@23..24 "}"
+  WHITESPACE@24..25 "\n"
diff --git a/crates/syntax/test_data/parser/inline/ok/0161_impl_item_const.rs b/crates/syntax/test_data/parser/inline/ok/0161_impl_item_const.rs
new file mode 100644 (file)
index 0000000..3252d6f
--- /dev/null
@@ -0,0 +1 @@
+impl const Send for S {}
diff --git a/crates/syntax/test_data/parser/inline/ok/0164_const_generic_negated_literal.rast b/crates/syntax/test_data/parser/inline/ok/0164_const_generic_negated_literal.rast
deleted file mode 100644 (file)
index b20e523..0000000
+++ /dev/null
@@ -1,30 +0,0 @@
-SOURCE_FILE@0..19
-  FN@0..18
-    FN_KW@0..2 "fn"
-    WHITESPACE@2..3 " "
-    NAME@3..4
-      IDENT@3..4 "f"
-    PARAM_LIST@4..6
-      L_PAREN@4..5 "("
-      R_PAREN@5..6 ")"
-    WHITESPACE@6..7 " "
-    BLOCK_EXPR@7..18
-      L_CURLY@7..8 "{"
-      WHITESPACE@8..9 " "
-      PATH_EXPR@9..16
-        PATH@9..16
-          PATH_SEGMENT@9..16
-            NAME_REF@9..10
-              IDENT@9..10 "S"
-            GENERIC_ARG_LIST@10..16
-              COLON2@10..12 "::"
-              L_ANGLE@12..13 "<"
-              CONST_ARG@13..15
-                PREFIX_EXPR@13..15
-                  MINUS@13..14 "-"
-                  LITERAL@14..15
-                    INT_NUMBER@14..15 "1"
-              R_ANGLE@15..16 ">"
-      WHITESPACE@16..17 " "
-      R_CURLY@17..18 "}"
-  WHITESPACE@18..19 "\n"
diff --git a/crates/syntax/test_data/parser/inline/ok/0164_const_generic_negated_literal.rs b/crates/syntax/test_data/parser/inline/ok/0164_const_generic_negated_literal.rs
deleted file mode 100644 (file)
index 8a81d05..0000000
+++ /dev/null
@@ -1 +0,0 @@
-fn f() { S::<-1> }
index e8f40a4cd7063beb68d5cd7f9434d3974ab4edf9..8677f8ae2c810c8a5a81b2f05f8da00ccc166228 100644 (file)
@@ -1,4 +1,4 @@
-SOURCE_FILE@0..96
+SOURCE_FILE@0..29
   STRUCT@0..28
     STRUCT_KW@0..6 "struct"
     WHITESPACE@6..7 " "
@@ -29,68 +29,3 @@ SOURCE_FILE@0..96
       R_ANGLE@26..27 ">"
     SEMICOLON@27..28 ";"
   WHITESPACE@28..29 "\n"
-  STRUCT@29..57
-    STRUCT_KW@29..35 "struct"
-    WHITESPACE@35..36 " "
-    NAME@36..37
-      IDENT@36..37 "B"
-    GENERIC_PARAM_LIST@37..56
-      L_ANGLE@37..38 "<"
-      CONST_PARAM@38..55
-        CONST_KW@38..43 "const"
-        WHITESPACE@43..44 " "
-        NAME@44..45
-          IDENT@44..45 "N"
-        COLON@45..46 ":"
-        WHITESPACE@46..47 " "
-        PATH_TYPE@47..50
-          PATH@47..50
-            PATH_SEGMENT@47..50
-              NAME_REF@47..50
-                IDENT@47..50 "i32"
-        WHITESPACE@50..51 " "
-        EQ@51..52 "="
-        WHITESPACE@52..53 " "
-        CONST_ARG@53..55
-          BLOCK_EXPR@53..55
-            L_CURLY@53..54 "{"
-            R_CURLY@54..55 "}"
-      R_ANGLE@55..56 ">"
-    SEMICOLON@56..57 ";"
-  WHITESPACE@57..58 "\n"
-  STRUCT@58..95
-    STRUCT_KW@58..64 "struct"
-    WHITESPACE@64..65 " "
-    NAME@65..66
-      IDENT@65..66 "C"
-    GENERIC_PARAM_LIST@66..94
-      L_ANGLE@66..67 "<"
-      CONST_PARAM@67..93
-        CONST_KW@67..72 "const"
-        WHITESPACE@72..73 " "
-        NAME@73..74
-          IDENT@73..74 "N"
-        COLON@74..75 ":"
-        WHITESPACE@75..76 " "
-        PATH_TYPE@76..79
-          PATH@76..79
-            PATH_SEGMENT@76..79
-              NAME_REF@76..79
-                IDENT@76..79 "i32"
-        WHITESPACE@79..80 " "
-        EQ@80..81 "="
-        WHITESPACE@81..82 " "
-        CONST_ARG@82..93
-          PATH_EXPR@82..93
-            PATH@82..93
-              PATH@82..86
-                PATH_SEGMENT@82..86
-                  NAME_REF@82..86
-                    IDENT@82..86 "some"
-              COLON2@86..88 "::"
-              PATH_SEGMENT@88..93
-                NAME_REF@88..93
-                  IDENT@88..93 "CONST"
-      R_ANGLE@93..94 ">"
-    SEMICOLON@94..95 ";"
-  WHITESPACE@95..96 "\n"
index 68388c8fbdf385b2f75f79131a44e0037cd04161..879ecffa75d3a89e322e5cbae4e74e274392a710 100644 (file)
@@ -1,3 +1 @@
 struct A<const N: i32 = -1>;
-struct B<const N: i32 = {}>;
-struct C<const N: i32 = some::CONST>;
diff --git a/crates/syntax/test_data/parser/inline/ok/0168_extern_crate_rename.rast b/crates/syntax/test_data/parser/inline/ok/0168_extern_crate_rename.rast
new file mode 100644 (file)
index 0000000..87516e9
--- /dev/null
@@ -0,0 +1,16 @@
+SOURCE_FILE@0..25
+  EXTERN_CRATE@0..24
+    EXTERN_KW@0..6 "extern"
+    WHITESPACE@6..7 " "
+    CRATE_KW@7..12 "crate"
+    WHITESPACE@12..13 " "
+    NAME_REF@13..16
+      IDENT@13..16 "foo"
+    WHITESPACE@16..17 " "
+    RENAME@17..23
+      AS_KW@17..19 "as"
+      WHITESPACE@19..20 " "
+      NAME@20..23
+        IDENT@20..23 "bar"
+    SEMICOLON@23..24 ";"
+  WHITESPACE@24..25 "\n"
diff --git a/crates/syntax/test_data/parser/inline/ok/0168_extern_crate_rename.rs b/crates/syntax/test_data/parser/inline/ok/0168_extern_crate_rename.rs
new file mode 100644 (file)
index 0000000..fc76e17
--- /dev/null
@@ -0,0 +1 @@
+extern crate foo as bar;
diff --git a/crates/syntax/test_data/parser/inline/ok/0168_extern_crate_self.rast b/crates/syntax/test_data/parser/inline/ok/0168_extern_crate_self.rast
new file mode 100644 (file)
index 0000000..26b4c0f
--- /dev/null
@@ -0,0 +1,10 @@
+SOURCE_FILE@0..19
+  EXTERN_CRATE@0..18
+    EXTERN_KW@0..6 "extern"
+    WHITESPACE@6..7 " "
+    CRATE_KW@7..12 "crate"
+    WHITESPACE@12..13 " "
+    NAME_REF@13..17
+      SELF_KW@13..17 "self"
+    SEMICOLON@17..18 ";"
+  WHITESPACE@18..19 "\n"
diff --git a/crates/syntax/test_data/parser/inline/ok/0168_extern_crate_self.rs b/crates/syntax/test_data/parser/inline/ok/0168_extern_crate_self.rs
new file mode 100644 (file)
index 0000000..c969ed1
--- /dev/null
@@ -0,0 +1 @@
+extern crate self;
diff --git a/crates/syntax/test_data/parser/inline/ok/0169_mod_item.rast b/crates/syntax/test_data/parser/inline/ok/0169_mod_item.rast
new file mode 100644 (file)
index 0000000..423eacf
--- /dev/null
@@ -0,0 +1,8 @@
+SOURCE_FILE@0..7
+  MODULE@0..6
+    MOD_KW@0..3 "mod"
+    WHITESPACE@3..4 " "
+    NAME@4..5
+      IDENT@4..5 "a"
+    SEMICOLON@5..6 ";"
+  WHITESPACE@6..7 "\n"
diff --git a/crates/syntax/test_data/parser/inline/ok/0169_mod_item.rs b/crates/syntax/test_data/parser/inline/ok/0169_mod_item.rs
new file mode 100644 (file)
index 0000000..f21af61
--- /dev/null
@@ -0,0 +1 @@
+mod a;
diff --git a/crates/syntax/test_data/parser/inline/ok/0170_mod_item_curly.rast b/crates/syntax/test_data/parser/inline/ok/0170_mod_item_curly.rast
new file mode 100644 (file)
index 0000000..33ad9c4
--- /dev/null
@@ -0,0 +1,12 @@
+SOURCE_FILE@0..10
+  MODULE@0..9
+    MOD_KW@0..3 "mod"
+    WHITESPACE@3..4 " "
+    NAME@4..5
+      IDENT@4..5 "b"
+    WHITESPACE@5..6 " "
+    ITEM_LIST@6..9
+      L_CURLY@6..7 "{"
+      WHITESPACE@7..8 " "
+      R_CURLY@8..9 "}"
+  WHITESPACE@9..10 "\n"
diff --git a/crates/syntax/test_data/parser/inline/ok/0170_mod_item_curly.rs b/crates/syntax/test_data/parser/inline/ok/0170_mod_item_curly.rs
new file mode 100644 (file)
index 0000000..16b1b43
--- /dev/null
@@ -0,0 +1 @@
+mod b { }
diff --git a/crates/syntax/test_data/parser/inline/ok/0170_tuple_struct.rast b/crates/syntax/test_data/parser/inline/ok/0170_tuple_struct.rast
new file mode 100644 (file)
index 0000000..935fd6e
--- /dev/null
@@ -0,0 +1,25 @@
+SOURCE_FILE@0..25
+  STRUCT@0..24
+    STRUCT_KW@0..6 "struct"
+    WHITESPACE@6..7 " "
+    NAME@7..8
+      IDENT@7..8 "S"
+    TUPLE_FIELD_LIST@8..23
+      L_PAREN@8..9 "("
+      TUPLE_FIELD@9..15
+        PATH_TYPE@9..15
+          PATH@9..15
+            PATH_SEGMENT@9..15
+              NAME_REF@9..15
+                IDENT@9..15 "String"
+      COMMA@15..16 ","
+      WHITESPACE@16..17 " "
+      TUPLE_FIELD@17..22
+        PATH_TYPE@17..22
+          PATH@17..22
+            PATH_SEGMENT@17..22
+              NAME_REF@17..22
+                IDENT@17..22 "usize"
+      R_PAREN@22..23 ")"
+    SEMICOLON@23..24 ";"
+  WHITESPACE@24..25 "\n"
diff --git a/crates/syntax/test_data/parser/inline/ok/0170_tuple_struct.rs b/crates/syntax/test_data/parser/inline/ok/0170_tuple_struct.rs
new file mode 100644 (file)
index 0000000..b4e0571
--- /dev/null
@@ -0,0 +1 @@
+struct S(String, usize);
diff --git a/crates/syntax/test_data/parser/inline/ok/0171_struct_item.rast b/crates/syntax/test_data/parser/inline/ok/0171_struct_item.rast
new file mode 100644 (file)
index 0000000..3134482
--- /dev/null
@@ -0,0 +1,11 @@
+SOURCE_FILE@0..12
+  STRUCT@0..11
+    STRUCT_KW@0..6 "struct"
+    WHITESPACE@6..7 " "
+    NAME@7..8
+      IDENT@7..8 "S"
+    WHITESPACE@8..9 " "
+    RECORD_FIELD_LIST@9..11
+      L_CURLY@9..10 "{"
+      R_CURLY@10..11 "}"
+  WHITESPACE@11..12 "\n"
diff --git a/crates/syntax/test_data/parser/inline/ok/0171_struct_item.rs b/crates/syntax/test_data/parser/inline/ok/0171_struct_item.rs
new file mode 100644 (file)
index 0000000..5f1a34f
--- /dev/null
@@ -0,0 +1 @@
+struct S {}
diff --git a/crates/syntax/test_data/parser/inline/ok/0172_const_item.rast b/crates/syntax/test_data/parser/inline/ok/0172_const_item.rast
new file mode 100644 (file)
index 0000000..8a61d5e
--- /dev/null
@@ -0,0 +1,20 @@
+SOURCE_FILE@0..19
+  CONST@0..18
+    CONST_KW@0..5 "const"
+    WHITESPACE@5..6 " "
+    NAME@6..7
+      IDENT@6..7 "C"
+    COLON@7..8 ":"
+    WHITESPACE@8..9 " "
+    PATH_TYPE@9..12
+      PATH@9..12
+        PATH_SEGMENT@9..12
+          NAME_REF@9..12
+            IDENT@9..12 "u32"
+    WHITESPACE@12..13 " "
+    EQ@13..14 "="
+    WHITESPACE@14..15 " "
+    LITERAL@15..17
+      INT_NUMBER@15..17 "92"
+    SEMICOLON@17..18 ";"
+  WHITESPACE@18..19 "\n"
diff --git a/crates/syntax/test_data/parser/inline/ok/0172_const_item.rs b/crates/syntax/test_data/parser/inline/ok/0172_const_item.rs
new file mode 100644 (file)
index 0000000..6d5f5be
--- /dev/null
@@ -0,0 +1 @@
+const C: u32 = 92;
diff --git a/crates/syntax/test_data/parser/inline/ok/0172_record_field_list.rast b/crates/syntax/test_data/parser/inline/ok/0172_record_field_list.rast
new file mode 100644 (file)
index 0000000..ce1135c
--- /dev/null
@@ -0,0 +1,35 @@
+SOURCE_FILE@0..28
+  STRUCT@0..27
+    STRUCT_KW@0..6 "struct"
+    WHITESPACE@6..7 " "
+    NAME@7..8
+      IDENT@7..8 "S"
+    WHITESPACE@8..9 " "
+    RECORD_FIELD_LIST@9..27
+      L_CURLY@9..10 "{"
+      WHITESPACE@10..11 " "
+      RECORD_FIELD@11..17
+        NAME@11..12
+          IDENT@11..12 "a"
+        COLON@12..13 ":"
+        WHITESPACE@13..14 " "
+        PATH_TYPE@14..17
+          PATH@14..17
+            PATH_SEGMENT@14..17
+              NAME_REF@14..17
+                IDENT@14..17 "i32"
+      COMMA@17..18 ","
+      WHITESPACE@18..19 " "
+      RECORD_FIELD@19..25
+        NAME@19..20
+          IDENT@19..20 "b"
+        COLON@20..21 ":"
+        WHITESPACE@21..22 " "
+        PATH_TYPE@22..25
+          PATH@22..25
+            PATH_SEGMENT@22..25
+              NAME_REF@22..25
+                IDENT@22..25 "f32"
+      WHITESPACE@25..26 " "
+      R_CURLY@26..27 "}"
+  WHITESPACE@27..28 "\n"
diff --git a/crates/syntax/test_data/parser/inline/ok/0172_record_field_list.rs b/crates/syntax/test_data/parser/inline/ok/0172_record_field_list.rs
new file mode 100644 (file)
index 0000000..a3bd778
--- /dev/null
@@ -0,0 +1 @@
+struct S { a: i32, b: f32 }
diff --git a/crates/syntax/test_data/parser/inline/ok/0173_anonymous_const.rast b/crates/syntax/test_data/parser/inline/ok/0173_anonymous_const.rast
new file mode 100644 (file)
index 0000000..68ce503
--- /dev/null
@@ -0,0 +1,19 @@
+SOURCE_FILE@0..18
+  CONST@0..17
+    CONST_KW@0..5 "const"
+    WHITESPACE@5..6 " "
+    UNDERSCORE@6..7 "_"
+    COLON@7..8 ":"
+    WHITESPACE@8..9 " "
+    PATH_TYPE@9..12
+      PATH@9..12
+        PATH_SEGMENT@9..12
+          NAME_REF@9..12
+            IDENT@9..12 "u32"
+    WHITESPACE@12..13 " "
+    EQ@13..14 "="
+    WHITESPACE@14..15 " "
+    LITERAL@15..16
+      INT_NUMBER@15..16 "0"
+    SEMICOLON@16..17 ";"
+  WHITESPACE@17..18 "\n"
diff --git a/crates/syntax/test_data/parser/inline/ok/0173_anonymous_const.rs b/crates/syntax/test_data/parser/inline/ok/0173_anonymous_const.rs
new file mode 100644 (file)
index 0000000..c1d5cdf
--- /dev/null
@@ -0,0 +1 @@
+const _: u32 = 0;
diff --git a/crates/syntax/test_data/parser/inline/ok/0173_macro_def_curly.rast b/crates/syntax/test_data/parser/inline/ok/0173_macro_def_curly.rast
new file mode 100644 (file)
index 0000000..3ec00bf
--- /dev/null
@@ -0,0 +1,27 @@
+SOURCE_FILE@0..29
+  MACRO_DEF@0..28
+    MACRO_KW@0..5 "macro"
+    WHITESPACE@5..6 " "
+    NAME@6..7
+      IDENT@6..7 "m"
+    WHITESPACE@7..8 " "
+    TOKEN_TREE@8..28
+      L_CURLY@8..9 "{"
+      WHITESPACE@9..10 " "
+      TOKEN_TREE@10..20
+        L_PAREN@10..11 "("
+        DOLLAR@11..12 "$"
+        IDENT@12..13 "i"
+        COLON@13..14 ":"
+        IDENT@14..19 "ident"
+        R_PAREN@19..20 ")"
+      WHITESPACE@20..21 " "
+      EQ@21..22 "="
+      R_ANGLE@22..23 ">"
+      WHITESPACE@23..24 " "
+      TOKEN_TREE@24..26
+        L_CURLY@24..25 "{"
+        R_CURLY@25..26 "}"
+      WHITESPACE@26..27 " "
+      R_CURLY@27..28 "}"
+  WHITESPACE@28..29 "\n"
diff --git a/crates/syntax/test_data/parser/inline/ok/0173_macro_def_curly.rs b/crates/syntax/test_data/parser/inline/ok/0173_macro_def_curly.rs
new file mode 100644 (file)
index 0000000..5ed0c77
--- /dev/null
@@ -0,0 +1 @@
+macro m { ($i:ident) => {} }
diff --git a/crates/syntax/test_data/parser/inline/ok/0173_union_item.rast b/crates/syntax/test_data/parser/inline/ok/0173_union_item.rast
new file mode 100644 (file)
index 0000000..5d5c0d6
--- /dev/null
@@ -0,0 +1,35 @@
+SOURCE_FILE@0..28
+  STRUCT@0..27
+    STRUCT_KW@0..6 "struct"
+    WHITESPACE@6..7 " "
+    NAME@7..8
+      IDENT@7..8 "U"
+    WHITESPACE@8..9 " "
+    RECORD_FIELD_LIST@9..27
+      L_CURLY@9..10 "{"
+      WHITESPACE@10..11 " "
+      RECORD_FIELD@11..17
+        NAME@11..12
+          IDENT@11..12 "i"
+        COLON@12..13 ":"
+        WHITESPACE@13..14 " "
+        PATH_TYPE@14..17
+          PATH@14..17
+            PATH_SEGMENT@14..17
+              NAME_REF@14..17
+                IDENT@14..17 "i32"
+      COMMA@17..18 ","
+      WHITESPACE@18..19 " "
+      RECORD_FIELD@19..25
+        NAME@19..20
+          IDENT@19..20 "f"
+        COLON@20..21 ":"
+        WHITESPACE@21..22 " "
+        PATH_TYPE@22..25
+          PATH@22..25
+            PATH_SEGMENT@22..25
+              NAME_REF@22..25
+                IDENT@22..25 "f32"
+      WHITESPACE@25..26 " "
+      R_CURLY@26..27 "}"
+  WHITESPACE@27..28 "\n"
diff --git a/crates/syntax/test_data/parser/inline/ok/0173_union_item.rs b/crates/syntax/test_data/parser/inline/ok/0173_union_item.rs
new file mode 100644 (file)
index 0000000..5edf50d
--- /dev/null
@@ -0,0 +1 @@
+struct U { i: i32, f: f32 }
diff --git a/crates/syntax/test_data/parser/inline/ok/0174_trait_item_generic_params.rast b/crates/syntax/test_data/parser/inline/ok/0174_trait_item_generic_params.rast
new file mode 100644 (file)
index 0000000..f7af2e3
--- /dev/null
@@ -0,0 +1,35 @@
+SOURCE_FILE@0..31
+  TRAIT@0..30
+    TRAIT_KW@0..5 "trait"
+    WHITESPACE@5..6 " "
+    NAME@6..7
+      IDENT@6..7 "X"
+    GENERIC_PARAM_LIST@7..27
+      L_ANGLE@7..8 "<"
+      TYPE_PARAM@8..26
+        NAME@8..9
+          IDENT@8..9 "U"
+        COLON@9..10 ":"
+        WHITESPACE@10..11 " "
+        TYPE_BOUND_LIST@11..26
+          TYPE_BOUND@11..16
+            PATH_TYPE@11..16
+              PATH@11..16
+                PATH_SEGMENT@11..16
+                  NAME_REF@11..16
+                    IDENT@11..16 "Debug"
+          WHITESPACE@16..17 " "
+          PLUS@17..18 "+"
+          WHITESPACE@18..19 " "
+          TYPE_BOUND@19..26
+            PATH_TYPE@19..26
+              PATH@19..26
+                PATH_SEGMENT@19..26
+                  NAME_REF@19..26
+                    IDENT@19..26 "Display"
+      R_ANGLE@26..27 ">"
+    WHITESPACE@27..28 " "
+    ASSOC_ITEM_LIST@28..30
+      L_CURLY@28..29 "{"
+      R_CURLY@29..30 "}"
+  WHITESPACE@30..31 "\n"
diff --git a/crates/syntax/test_data/parser/inline/ok/0174_trait_item_generic_params.rs b/crates/syntax/test_data/parser/inline/ok/0174_trait_item_generic_params.rs
new file mode 100644 (file)
index 0000000..4a51926
--- /dev/null
@@ -0,0 +1 @@
+trait X<U: Debug + Display> {}
diff --git a/crates/syntax/test_data/parser/inline/ok/0174_unit_struct.rast b/crates/syntax/test_data/parser/inline/ok/0174_unit_struct.rast
new file mode 100644 (file)
index 0000000..b202358
--- /dev/null
@@ -0,0 +1,8 @@
+SOURCE_FILE@0..10
+  STRUCT@0..9
+    STRUCT_KW@0..6 "struct"
+    WHITESPACE@6..7 " "
+    NAME@7..8
+      IDENT@7..8 "S"
+    SEMICOLON@8..9 ";"
+  WHITESPACE@9..10 "\n"
diff --git a/crates/syntax/test_data/parser/inline/ok/0174_unit_struct.rs b/crates/syntax/test_data/parser/inline/ok/0174_unit_struct.rs
new file mode 100644 (file)
index 0000000..28377c2
--- /dev/null
@@ -0,0 +1 @@
+struct S;
diff --git a/crates/syntax/test_data/parser/inline/ok/0174_use_tree_star.rast b/crates/syntax/test_data/parser/inline/ok/0174_use_tree_star.rast
new file mode 100644 (file)
index 0000000..b9c6a3b
--- /dev/null
@@ -0,0 +1,24 @@
+SOURCE_FILE@0..21
+  USE@0..6
+    USE_KW@0..3 "use"
+    WHITESPACE@3..4 " "
+    USE_TREE@4..5
+      STAR@4..5 "*"
+    SEMICOLON@5..6 ";"
+  WHITESPACE@6..7 "\n"
+  USE@7..20
+    USE_KW@7..10 "use"
+    WHITESPACE@10..11 " "
+    USE_TREE@11..19
+      PATH@11..14
+        PATH_SEGMENT@11..14
+          NAME_REF@11..14
+            IDENT@11..14 "std"
+      COLON2@14..16 "::"
+      USE_TREE_LIST@16..19
+        L_CURLY@16..17 "{"
+        USE_TREE@17..18
+          STAR@17..18 "*"
+        R_CURLY@18..19 "}"
+    SEMICOLON@19..20 ";"
+  WHITESPACE@20..21 "\n"
diff --git a/crates/syntax/test_data/parser/inline/ok/0174_use_tree_star.rs b/crates/syntax/test_data/parser/inline/ok/0174_use_tree_star.rs
new file mode 100644 (file)
index 0000000..b8c6134
--- /dev/null
@@ -0,0 +1,2 @@
+use *;
+use std::{*};
diff --git a/crates/syntax/test_data/parser/inline/ok/0175_trait_item_bounds.rast b/crates/syntax/test_data/parser/inline/ok/0175_trait_item_bounds.rast
new file mode 100644 (file)
index 0000000..f025e32
--- /dev/null
@@ -0,0 +1,29 @@
+SOURCE_FILE@0..25
+  TRAIT@0..24
+    TRAIT_KW@0..5 "trait"
+    WHITESPACE@5..6 " "
+    NAME@6..7
+      IDENT@6..7 "T"
+    COLON@7..8 ":"
+    WHITESPACE@8..9 " "
+    TYPE_BOUND_LIST@9..21
+      TYPE_BOUND@9..13
+        PATH_TYPE@9..13
+          PATH@9..13
+            PATH_SEGMENT@9..13
+              NAME_REF@9..13
+                IDENT@9..13 "Hash"
+      WHITESPACE@13..14 " "
+      PLUS@14..15 "+"
+      WHITESPACE@15..16 " "
+      TYPE_BOUND@16..21
+        PATH_TYPE@16..21
+          PATH@16..21
+            PATH_SEGMENT@16..21
+              NAME_REF@16..21
+                IDENT@16..21 "Clone"
+    WHITESPACE@21..22 " "
+    ASSOC_ITEM_LIST@22..24
+      L_CURLY@22..23 "{"
+      R_CURLY@23..24 "}"
+  WHITESPACE@24..25 "\n"
diff --git a/crates/syntax/test_data/parser/inline/ok/0175_trait_item_bounds.rs b/crates/syntax/test_data/parser/inline/ok/0175_trait_item_bounds.rs
new file mode 100644 (file)
index 0000000..e6ad2b5
--- /dev/null
@@ -0,0 +1 @@
+trait T: Hash + Clone {}
diff --git a/crates/syntax/test_data/parser/inline/ok/0176_trait_item_where_clause.rast b/crates/syntax/test_data/parser/inline/ok/0176_trait_item_where_clause.rast
new file mode 100644 (file)
index 0000000..85105b4
--- /dev/null
@@ -0,0 +1,30 @@
+SOURCE_FILE@0..28
+  TRAIT@0..27
+    TRAIT_KW@0..5 "trait"
+    WHITESPACE@5..6 " "
+    NAME@6..7
+      IDENT@6..7 "T"
+    WHITESPACE@7..8 " "
+    WHERE_CLAUSE@8..24
+      WHERE_KW@8..13 "where"
+      WHITESPACE@13..14 " "
+      WHERE_PRED@14..24
+        PATH_TYPE@14..18
+          PATH@14..18
+            PATH_SEGMENT@14..18
+              NAME_REF@14..18
+                IDENT@14..18 "Self"
+        COLON@18..19 ":"
+        WHITESPACE@19..20 " "
+        TYPE_BOUND_LIST@20..24
+          TYPE_BOUND@20..24
+            PATH_TYPE@20..24
+              PATH@20..24
+                PATH_SEGMENT@20..24
+                  NAME_REF@20..24
+                    IDENT@20..24 "Copy"
+    WHITESPACE@24..25 " "
+    ASSOC_ITEM_LIST@25..27
+      L_CURLY@25..26 "{"
+      R_CURLY@26..27 "}"
+  WHITESPACE@27..28 "\n"
diff --git a/crates/syntax/test_data/parser/inline/ok/0176_trait_item_where_clause.rs b/crates/syntax/test_data/parser/inline/ok/0176_trait_item_where_clause.rs
new file mode 100644 (file)
index 0000000..52a6a80
--- /dev/null
@@ -0,0 +1 @@
+trait T where Self: Copy {}
diff --git a/crates/syntax/test_data/parser/inline/ok/0176_use_tree_alias.rast b/crates/syntax/test_data/parser/inline/ok/0176_use_tree_alias.rast
new file mode 100644 (file)
index 0000000..210ff1f
--- /dev/null
@@ -0,0 +1,32 @@
+SOURCE_FILE@0..35
+  USE@0..18
+    USE_KW@0..3 "use"
+    WHITESPACE@3..4 " "
+    USE_TREE@4..17
+      PATH@4..7
+        PATH_SEGMENT@4..7
+          NAME_REF@4..7
+            IDENT@4..7 "std"
+      WHITESPACE@7..8 " "
+      RENAME@8..17
+        AS_KW@8..10 "as"
+        WHITESPACE@10..11 " "
+        NAME@11..17
+          IDENT@11..17 "stdlib"
+    SEMICOLON@17..18 ";"
+  WHITESPACE@18..19 "\n"
+  USE@19..34
+    USE_KW@19..22 "use"
+    WHITESPACE@22..23 " "
+    USE_TREE@23..33
+      PATH@23..28
+        PATH_SEGMENT@23..28
+          NAME_REF@23..28
+            IDENT@23..28 "Trait"
+      WHITESPACE@28..29 " "
+      RENAME@29..33
+        AS_KW@29..31 "as"
+        WHITESPACE@31..32 " "
+        UNDERSCORE@32..33 "_"
+    SEMICOLON@33..34 ";"
+  WHITESPACE@34..35 "\n"
diff --git a/crates/syntax/test_data/parser/inline/ok/0176_use_tree_alias.rs b/crates/syntax/test_data/parser/inline/ok/0176_use_tree_alias.rs
new file mode 100644 (file)
index 0000000..19a6906
--- /dev/null
@@ -0,0 +1,2 @@
+use std as stdlib;
+use Trait as _;
diff --git a/crates/syntax/test_data/parser/inline/ok/0177_assoc_item_list_inner_attrs.rast b/crates/syntax/test_data/parser/inline/ok/0177_assoc_item_list_inner_attrs.rast
new file mode 100644 (file)
index 0000000..65b4743
--- /dev/null
@@ -0,0 +1,26 @@
+SOURCE_FILE@0..20
+  IMPL@0..19
+    IMPL_KW@0..4 "impl"
+    WHITESPACE@4..5 " "
+    PATH_TYPE@5..6
+      PATH@5..6
+        PATH_SEGMENT@5..6
+          NAME_REF@5..6
+            IDENT@5..6 "S"
+    WHITESPACE@6..7 " "
+    ASSOC_ITEM_LIST@7..19
+      L_CURLY@7..8 "{"
+      WHITESPACE@8..9 " "
+      ATTR@9..17
+        POUND@9..10 "#"
+        BANG@10..11 "!"
+        L_BRACK@11..12 "["
+        META@12..16
+          PATH@12..16
+            PATH_SEGMENT@12..16
+              NAME_REF@12..16
+                IDENT@12..16 "attr"
+        R_BRACK@16..17 "]"
+      WHITESPACE@17..18 " "
+      R_CURLY@18..19 "}"
+  WHITESPACE@19..20 "\n"
diff --git a/crates/syntax/test_data/parser/inline/ok/0177_assoc_item_list_inner_attrs.rs b/crates/syntax/test_data/parser/inline/ok/0177_assoc_item_list_inner_attrs.rs
new file mode 100644 (file)
index 0000000..915e2c9
--- /dev/null
@@ -0,0 +1 @@
+impl S { #![attr] }
diff --git a/crates/syntax/test_data/parser/inline/ok/0177_trait_alias_where_clause.rast b/crates/syntax/test_data/parser/inline/ok/0177_trait_alias_where_clause.rast
new file mode 100644 (file)
index 0000000..929182f
--- /dev/null
@@ -0,0 +1,96 @@
+SOURCE_FILE@0..64
+  TRAIT@0..32
+    TRAIT_KW@0..5 "trait"
+    WHITESPACE@5..6 " "
+    NAME@6..7
+      IDENT@6..7 "Z"
+    GENERIC_PARAM_LIST@7..10
+      L_ANGLE@7..8 "<"
+      TYPE_PARAM@8..9
+        NAME@8..9
+          IDENT@8..9 "U"
+      R_ANGLE@9..10 ">"
+    WHITESPACE@10..11 " "
+    EQ@11..12 "="
+    WHITESPACE@12..13 " "
+    TYPE_BOUND_LIST@13..17
+      TYPE_BOUND@13..17
+        PATH_TYPE@13..17
+          PATH@13..17
+            PATH_SEGMENT@13..17
+              NAME_REF@13..14
+                IDENT@13..14 "T"
+              GENERIC_ARG_LIST@14..17
+                L_ANGLE@14..15 "<"
+                TYPE_ARG@15..16
+                  PATH_TYPE@15..16
+                    PATH@15..16
+                      PATH_SEGMENT@15..16
+                        NAME_REF@15..16
+                          IDENT@15..16 "U"
+                R_ANGLE@16..17 ">"
+    WHITESPACE@17..18 " "
+    WHERE_CLAUSE@18..31
+      WHERE_KW@18..23 "where"
+      WHITESPACE@23..24 " "
+      WHERE_PRED@24..31
+        PATH_TYPE@24..25
+          PATH@24..25
+            PATH_SEGMENT@24..25
+              NAME_REF@24..25
+                IDENT@24..25 "U"
+        COLON@25..26 ":"
+        WHITESPACE@26..27 " "
+        TYPE_BOUND_LIST@27..31
+          TYPE_BOUND@27..31
+            PATH_TYPE@27..31
+              PATH@27..31
+                PATH_SEGMENT@27..31
+                  NAME_REF@27..31
+                    IDENT@27..31 "Copy"
+    SEMICOLON@31..32 ";"
+  WHITESPACE@32..33 "\n"
+  TRAIT@33..63
+    TRAIT_KW@33..38 "trait"
+    WHITESPACE@38..39 " "
+    NAME@39..40
+      IDENT@39..40 "Z"
+    GENERIC_PARAM_LIST@40..43
+      L_ANGLE@40..41 "<"
+      TYPE_PARAM@41..42
+        NAME@41..42
+          IDENT@41..42 "U"
+      R_ANGLE@42..43 ">"
+    WHITESPACE@43..44 " "
+    EQ@44..45 "="
+    WHITESPACE@45..46 " "
+    TYPE_BOUND_LIST@46..46
+    WHERE_CLAUSE@46..62
+      WHERE_KW@46..51 "where"
+      WHITESPACE@51..52 " "
+      WHERE_PRED@52..62
+        PATH_TYPE@52..56
+          PATH@52..56
+            PATH_SEGMENT@52..56
+              NAME_REF@52..56
+                IDENT@52..56 "Self"
+        COLON@56..57 ":"
+        WHITESPACE@57..58 " "
+        TYPE_BOUND_LIST@58..62
+          TYPE_BOUND@58..62
+            PATH_TYPE@58..62
+              PATH@58..62
+                PATH_SEGMENT@58..62
+                  NAME_REF@58..59
+                    IDENT@58..59 "T"
+                  GENERIC_ARG_LIST@59..62
+                    L_ANGLE@59..60 "<"
+                    TYPE_ARG@60..61
+                      PATH_TYPE@60..61
+                        PATH@60..61
+                          PATH_SEGMENT@60..61
+                            NAME_REF@60..61
+                              IDENT@60..61 "U"
+                    R_ANGLE@61..62 ">"
+    SEMICOLON@62..63 ";"
+  WHITESPACE@63..64 "\n"
diff --git a/crates/syntax/test_data/parser/inline/ok/0177_trait_alias_where_clause.rs b/crates/syntax/test_data/parser/inline/ok/0177_trait_alias_where_clause.rs
new file mode 100644 (file)
index 0000000..a90d54b
--- /dev/null
@@ -0,0 +1,2 @@
+trait Z<U> = T<U> where U: Copy;
+trait Z<U> = where Self: T<U>;
diff --git a/crates/syntax/test_data/parser/inline/ok/0177_use_tree.rast b/crates/syntax/test_data/parser/inline/ok/0177_use_tree.rast
new file mode 100644 (file)
index 0000000..978d296
--- /dev/null
@@ -0,0 +1,30 @@
+SOURCE_FILE@0..32
+  USE@0..31
+    USE_KW@0..3 "use"
+    WHITESPACE@3..4 " "
+    USE_TREE@4..30
+      PATH@4..15
+        PATH@4..9
+          PATH_SEGMENT@4..9
+            NAME_REF@4..9
+              IDENT@4..9 "outer"
+        COLON2@9..11 "::"
+        PATH_SEGMENT@11..15
+          NAME_REF@11..15
+            IDENT@11..15 "tree"
+      COLON2@15..17 "::"
+      USE_TREE_LIST@17..30
+        L_CURLY@17..18 "{"
+        USE_TREE@18..29
+          PATH@18..29
+            PATH@18..23
+              PATH_SEGMENT@18..23
+                NAME_REF@18..23
+                  IDENT@18..23 "inner"
+            COLON2@23..25 "::"
+            PATH_SEGMENT@25..29
+              NAME_REF@25..29
+                IDENT@25..29 "tree"
+        R_CURLY@29..30 "}"
+    SEMICOLON@30..31 ";"
+  WHITESPACE@31..32 "\n"
diff --git a/crates/syntax/test_data/parser/inline/ok/0177_use_tree.rs b/crates/syntax/test_data/parser/inline/ok/0177_use_tree.rs
new file mode 100644 (file)
index 0000000..3cc3943
--- /dev/null
@@ -0,0 +1 @@
+use outer::tree::{inner::tree};
diff --git a/crates/syntax/test_data/parser/inline/ok/0177_use_tree_path.rast b/crates/syntax/test_data/parser/inline/ok/0177_use_tree_path.rast
new file mode 100644 (file)
index 0000000..24086b5
--- /dev/null
@@ -0,0 +1,72 @@
+SOURCE_FILE@0..75
+  USE@0..10
+    USE_KW@0..3 "use"
+    WHITESPACE@3..4 " "
+    USE_TREE@4..9
+      PATH@4..9
+        PATH_SEGMENT@4..9
+          COLON2@4..6 "::"
+          NAME_REF@6..9
+            IDENT@6..9 "std"
+    SEMICOLON@9..10 ";"
+  WHITESPACE@10..11 "\n"
+  USE@11..32
+    USE_KW@11..14 "use"
+    WHITESPACE@14..15 " "
+    USE_TREE@15..31
+      PATH@15..31
+        PATH@15..18
+          PATH_SEGMENT@15..18
+            NAME_REF@15..18
+              IDENT@15..18 "std"
+        COLON2@18..20 "::"
+        PATH_SEGMENT@20..31
+          NAME_REF@20..31
+            IDENT@20..31 "collections"
+    SEMICOLON@31..32 ";"
+  WHITESPACE@32..34 "\n\n"
+  USE@34..46
+    USE_KW@34..37 "use"
+    WHITESPACE@37..38 " "
+    USE_TREE@38..45
+      PATH@38..45
+        PATH@38..42
+          PATH_SEGMENT@38..42
+            NAME_REF@38..42
+              SELF_KW@38..42 "self"
+        COLON2@42..44 "::"
+        PATH_SEGMENT@44..45
+          NAME_REF@44..45
+            IDENT@44..45 "m"
+    SEMICOLON@45..46 ";"
+  WHITESPACE@46..47 "\n"
+  USE@47..60
+    USE_KW@47..50 "use"
+    WHITESPACE@50..51 " "
+    USE_TREE@51..59
+      PATH@51..59
+        PATH@51..56
+          PATH_SEGMENT@51..56
+            NAME_REF@51..56
+              SUPER_KW@51..56 "super"
+        COLON2@56..58 "::"
+        PATH_SEGMENT@58..59
+          NAME_REF@58..59
+            IDENT@58..59 "m"
+    SEMICOLON@59..60 ";"
+  WHITESPACE@60..61 "\n"
+  USE@61..74
+    USE_KW@61..64 "use"
+    WHITESPACE@64..65 " "
+    USE_TREE@65..73
+      PATH@65..73
+        PATH@65..70
+          PATH_SEGMENT@65..70
+            NAME_REF@65..70
+              CRATE_KW@65..70 "crate"
+        COLON2@70..72 "::"
+        PATH_SEGMENT@72..73
+          NAME_REF@72..73
+            IDENT@72..73 "m"
+    SEMICOLON@73..74 ";"
+  WHITESPACE@74..75 "\n"
diff --git a/crates/syntax/test_data/parser/inline/ok/0177_use_tree_path.rs b/crates/syntax/test_data/parser/inline/ok/0177_use_tree_path.rs
new file mode 100644 (file)
index 0000000..5b22f88
--- /dev/null
@@ -0,0 +1,6 @@
+use ::std;
+use std::collections;
+
+use self::m;
+use super::m;
+use crate::m;
diff --git a/crates/syntax/test_data/parser/inline/ok/0178_use_tree_path_use_tree.rast b/crates/syntax/test_data/parser/inline/ok/0178_use_tree_path_use_tree.rast
new file mode 100644 (file)
index 0000000..620a792
--- /dev/null
@@ -0,0 +1,20 @@
+SOURCE_FILE@0..24
+  USE@0..23
+    USE_KW@0..3 "use"
+    WHITESPACE@3..4 " "
+    USE_TREE@4..22
+      PATH@4..7
+        PATH_SEGMENT@4..7
+          NAME_REF@4..7
+            IDENT@4..7 "std"
+      COLON2@7..9 "::"
+      USE_TREE_LIST@9..22
+        L_CURLY@9..10 "{"
+        USE_TREE@10..21
+          PATH@10..21
+            PATH_SEGMENT@10..21
+              NAME_REF@10..21
+                IDENT@10..21 "collections"
+        R_CURLY@21..22 "}"
+    SEMICOLON@22..23 ";"
+  WHITESPACE@23..24 "\n"
diff --git a/crates/syntax/test_data/parser/inline/ok/0178_use_tree_path_use_tree.rs b/crates/syntax/test_data/parser/inline/ok/0178_use_tree_path_use_tree.rs
new file mode 100644 (file)
index 0000000..c3086f5
--- /dev/null
@@ -0,0 +1 @@
+use std::{collections};
diff --git a/crates/syntax/test_data/parser/inline/ok/0179_use_tree_abs_star.rast b/crates/syntax/test_data/parser/inline/ok/0179_use_tree_abs_star.rast
new file mode 100644 (file)
index 0000000..4b132f2
--- /dev/null
@@ -0,0 +1,26 @@
+SOURCE_FILE@0..25
+  USE@0..8
+    USE_KW@0..3 "use"
+    WHITESPACE@3..4 " "
+    USE_TREE@4..7
+      COLON2@4..6 "::"
+      STAR@6..7 "*"
+    SEMICOLON@7..8 ";"
+  WHITESPACE@8..9 "\n"
+  USE@9..24
+    USE_KW@9..12 "use"
+    WHITESPACE@12..13 " "
+    USE_TREE@13..23
+      PATH@13..16
+        PATH_SEGMENT@13..16
+          NAME_REF@13..16
+            IDENT@13..16 "std"
+      COLON2@16..18 "::"
+      USE_TREE_LIST@18..23
+        L_CURLY@18..19 "{"
+        USE_TREE@19..22
+          COLON2@19..21 "::"
+          STAR@21..22 "*"
+        R_CURLY@22..23 "}"
+    SEMICOLON@23..24 ";"
+  WHITESPACE@24..25 "\n"
diff --git a/crates/syntax/test_data/parser/inline/ok/0179_use_tree_abs_star.rs b/crates/syntax/test_data/parser/inline/ok/0179_use_tree_abs_star.rs
new file mode 100644 (file)
index 0000000..caae0ba
--- /dev/null
@@ -0,0 +1,2 @@
+use ::*;
+use std::{::*};
diff --git a/crates/syntax/test_data/parser/inline/ok/0180_use_tree_path_star.rast b/crates/syntax/test_data/parser/inline/ok/0180_use_tree_path_star.rast
new file mode 100644 (file)
index 0000000..93384d6
--- /dev/null
@@ -0,0 +1,13 @@
+SOURCE_FILE@0..12
+  USE@0..11
+    USE_KW@0..3 "use"
+    WHITESPACE@3..4 " "
+    USE_TREE@4..10
+      PATH@4..7
+        PATH_SEGMENT@4..7
+          NAME_REF@4..7
+            IDENT@4..7 "std"
+      COLON2@7..9 "::"
+      STAR@9..10 "*"
+    SEMICOLON@10..11 ";"
+  WHITESPACE@11..12 "\n"
diff --git a/crates/syntax/test_data/parser/inline/ok/0180_use_tree_path_star.rs b/crates/syntax/test_data/parser/inline/ok/0180_use_tree_path_star.rs
new file mode 100644 (file)
index 0000000..dd601cf
--- /dev/null
@@ -0,0 +1 @@
+use std::*;
diff --git a/crates/syntax/test_data/parser/inline/ok/0181_generic_param_attribute.rast b/crates/syntax/test_data/parser/inline/ok/0181_generic_param_attribute.rast
new file mode 100644 (file)
index 0000000..475c236
--- /dev/null
@@ -0,0 +1,45 @@
+SOURCE_FILE@0..40
+  FN@0..39
+    FN_KW@0..2 "fn"
+    WHITESPACE@2..3 " "
+    NAME@3..6
+      IDENT@3..6 "foo"
+    GENERIC_PARAM_LIST@6..34
+      L_ANGLE@6..7 "<"
+      LIFETIME_PARAM@7..20
+        ATTR@7..17
+          POUND@7..8 "#"
+          L_BRACK@8..9 "["
+          META@9..16
+            PATH@9..16
+              PATH_SEGMENT@9..16
+                NAME_REF@9..16
+                  IDENT@9..16 "lt_attr"
+          R_BRACK@16..17 "]"
+        WHITESPACE@17..18 " "
+        LIFETIME@18..20
+          LIFETIME_IDENT@18..20 "'a"
+      COMMA@20..21 ","
+      WHITESPACE@21..22 " "
+      TYPE_PARAM@22..33
+        ATTR@22..31
+          POUND@22..23 "#"
+          L_BRACK@23..24 "["
+          META@24..30
+            PATH@24..30
+              PATH_SEGMENT@24..30
+                NAME_REF@24..30
+                  IDENT@24..30 "t_attr"
+          R_BRACK@30..31 "]"
+        WHITESPACE@31..32 " "
+        NAME@32..33
+          IDENT@32..33 "T"
+      R_ANGLE@33..34 ">"
+    PARAM_LIST@34..36
+      L_PAREN@34..35 "("
+      R_PAREN@35..36 ")"
+    WHITESPACE@36..37 " "
+    BLOCK_EXPR@37..39
+      L_CURLY@37..38 "{"
+      R_CURLY@38..39 "}"
+  WHITESPACE@39..40 "\n"
diff --git a/crates/syntax/test_data/parser/inline/ok/0181_generic_param_attribute.rs b/crates/syntax/test_data/parser/inline/ok/0181_generic_param_attribute.rs
new file mode 100644 (file)
index 0000000..0509f81
--- /dev/null
@@ -0,0 +1 @@
+fn foo<#[lt_attr] 'a, #[t_attr] T>() {}
diff --git a/crates/syntax/test_data/parser/inline/ok/0181_use_item.rast b/crates/syntax/test_data/parser/inline/ok/0181_use_item.rast
new file mode 100644 (file)
index 0000000..3952fcf
--- /dev/null
@@ -0,0 +1,16 @@
+SOURCE_FILE@0..22
+  USE@0..21
+    USE_KW@0..3 "use"
+    WHITESPACE@3..4 " "
+    USE_TREE@4..20
+      PATH@4..20
+        PATH@4..7
+          PATH_SEGMENT@4..7
+            NAME_REF@4..7
+              IDENT@4..7 "std"
+        COLON2@7..9 "::"
+        PATH_SEGMENT@9..20
+          NAME_REF@9..20
+            IDENT@9..20 "collections"
+    SEMICOLON@20..21 ";"
+  WHITESPACE@21..22 "\n"
diff --git a/crates/syntax/test_data/parser/inline/ok/0181_use_item.rs b/crates/syntax/test_data/parser/inline/ok/0181_use_item.rs
new file mode 100644 (file)
index 0000000..48ac87b
--- /dev/null
@@ -0,0 +1 @@
+use std::collections;
diff --git a/crates/syntax/test_data/parser/inline/ok/0182_lifetime_param.rast b/crates/syntax/test_data/parser/inline/ok/0182_lifetime_param.rast
new file mode 100644 (file)
index 0000000..0eb7361
--- /dev/null
@@ -0,0 +1,24 @@
+SOURCE_FILE@0..18
+  FN@0..17
+    FN_KW@0..2 "fn"
+    WHITESPACE@2..3 " "
+    NAME@3..4
+      IDENT@3..4 "f"
+    GENERIC_PARAM_LIST@4..12
+      L_ANGLE@4..5 "<"
+      LIFETIME_PARAM@5..11
+        LIFETIME@5..7
+          LIFETIME_IDENT@5..7 "'a"
+        COLON@7..8 ":"
+        WHITESPACE@8..9 " "
+        LIFETIME@9..11
+          LIFETIME_IDENT@9..11 "'b"
+      R_ANGLE@11..12 ">"
+    PARAM_LIST@12..14
+      L_PAREN@12..13 "("
+      R_PAREN@13..14 ")"
+    WHITESPACE@14..15 " "
+    BLOCK_EXPR@15..17
+      L_CURLY@15..16 "{"
+      R_CURLY@16..17 "}"
+  WHITESPACE@17..18 "\n"
diff --git a/crates/syntax/test_data/parser/inline/ok/0182_lifetime_param.rs b/crates/syntax/test_data/parser/inline/ok/0182_lifetime_param.rs
new file mode 100644 (file)
index 0000000..2bb38ec
--- /dev/null
@@ -0,0 +1 @@
+fn f<'a: 'b>() {}
diff --git a/crates/syntax/test_data/parser/inline/ok/0183_const_arg_block.rast b/crates/syntax/test_data/parser/inline/ok/0183_const_arg_block.rast
new file mode 100644 (file)
index 0000000..9020d1b
--- /dev/null
@@ -0,0 +1,31 @@
+SOURCE_FILE@0..22
+  TYPE_ALIAS@0..21
+    TYPE_KW@0..4 "type"
+    WHITESPACE@4..5 " "
+    NAME@5..6
+      IDENT@5..6 "T"
+    WHITESPACE@6..7 " "
+    EQ@7..8 "="
+    WHITESPACE@8..9 " "
+    PATH_TYPE@9..20
+      PATH@9..20
+        PATH_SEGMENT@9..20
+          NAME_REF@9..10
+            IDENT@9..10 "S"
+          GENERIC_ARG_LIST@10..20
+            L_ANGLE@10..11 "<"
+            CONST_ARG@11..19
+              BLOCK_EXPR@11..19
+                L_CURLY@11..12 "{"
+                BIN_EXPR@12..18
+                  LITERAL@12..14
+                    INT_NUMBER@12..14 "90"
+                  WHITESPACE@14..15 " "
+                  PLUS@15..16 "+"
+                  WHITESPACE@16..17 " "
+                  LITERAL@17..18
+                    INT_NUMBER@17..18 "2"
+                R_CURLY@18..19 "}"
+            R_ANGLE@19..20 ">"
+    SEMICOLON@20..21 ";"
+  WHITESPACE@21..22 "\n"
diff --git a/crates/syntax/test_data/parser/inline/ok/0183_const_arg_block.rs b/crates/syntax/test_data/parser/inline/ok/0183_const_arg_block.rs
new file mode 100644 (file)
index 0000000..1c279db
--- /dev/null
@@ -0,0 +1 @@
+type T = S<{90 + 2}>;
diff --git a/crates/syntax/test_data/parser/inline/ok/0183_type_param.rast b/crates/syntax/test_data/parser/inline/ok/0183_type_param.rast
new file mode 100644 (file)
index 0000000..9d8ab49
--- /dev/null
@@ -0,0 +1,29 @@
+SOURCE_FILE@0..20
+  FN@0..19
+    FN_KW@0..2 "fn"
+    WHITESPACE@2..3 " "
+    NAME@3..4
+      IDENT@3..4 "f"
+    GENERIC_PARAM_LIST@4..14
+      L_ANGLE@4..5 "<"
+      TYPE_PARAM@5..13
+        NAME@5..6
+          IDENT@5..6 "T"
+        COLON@6..7 ":"
+        WHITESPACE@7..8 " "
+        TYPE_BOUND_LIST@8..13
+          TYPE_BOUND@8..13
+            PATH_TYPE@8..13
+              PATH@8..13
+                PATH_SEGMENT@8..13
+                  NAME_REF@8..13
+                    IDENT@8..13 "Clone"
+      R_ANGLE@13..14 ">"
+    PARAM_LIST@14..16
+      L_PAREN@14..15 "("
+      R_PAREN@15..16 ")"
+    WHITESPACE@16..17 " "
+    BLOCK_EXPR@17..19
+      L_CURLY@17..18 "{"
+      R_CURLY@18..19 "}"
+  WHITESPACE@19..20 "\n"
diff --git a/crates/syntax/test_data/parser/inline/ok/0183_type_param.rs b/crates/syntax/test_data/parser/inline/ok/0183_type_param.rs
new file mode 100644 (file)
index 0000000..b250bc6
--- /dev/null
@@ -0,0 +1 @@
+fn f<T: Clone>() {}
diff --git a/crates/syntax/test_data/parser/inline/ok/0184_const_arg.rast b/crates/syntax/test_data/parser/inline/ok/0184_const_arg.rast
new file mode 100644 (file)
index 0000000..15bfcb2
--- /dev/null
@@ -0,0 +1,22 @@
+SOURCE_FILE@0..16
+  TYPE_ALIAS@0..15
+    TYPE_KW@0..4 "type"
+    WHITESPACE@4..5 " "
+    NAME@5..6
+      IDENT@5..6 "T"
+    WHITESPACE@6..7 " "
+    EQ@7..8 "="
+    WHITESPACE@8..9 " "
+    PATH_TYPE@9..14
+      PATH@9..14
+        PATH_SEGMENT@9..14
+          NAME_REF@9..10
+            IDENT@9..10 "S"
+          GENERIC_ARG_LIST@10..14
+            L_ANGLE@10..11 "<"
+            CONST_ARG@11..13
+              LITERAL@11..13
+                INT_NUMBER@11..13 "92"
+            R_ANGLE@13..14 ">"
+    SEMICOLON@14..15 ";"
+  WHITESPACE@15..16 "\n"
diff --git a/crates/syntax/test_data/parser/inline/ok/0184_const_arg.rs b/crates/syntax/test_data/parser/inline/ok/0184_const_arg.rs
new file mode 100644 (file)
index 0000000..8b5e5db
--- /dev/null
@@ -0,0 +1 @@
+type T = S<92>;
diff --git a/crates/syntax/test_data/parser/inline/ok/0184_generic_param_list.rast b/crates/syntax/test_data/parser/inline/ok/0184_generic_param_list.rast
new file mode 100644 (file)
index 0000000..9d8ab49
--- /dev/null
@@ -0,0 +1,29 @@
+SOURCE_FILE@0..20
+  FN@0..19
+    FN_KW@0..2 "fn"
+    WHITESPACE@2..3 " "
+    NAME@3..4
+      IDENT@3..4 "f"
+    GENERIC_PARAM_LIST@4..14
+      L_ANGLE@4..5 "<"
+      TYPE_PARAM@5..13
+        NAME@5..6
+          IDENT@5..6 "T"
+        COLON@6..7 ":"
+        WHITESPACE@7..8 " "
+        TYPE_BOUND_LIST@8..13
+          TYPE_BOUND@8..13
+            PATH_TYPE@8..13
+              PATH@8..13
+                PATH_SEGMENT@8..13
+                  NAME_REF@8..13
+                    IDENT@8..13 "Clone"
+      R_ANGLE@13..14 ">"
+    PARAM_LIST@14..16
+      L_PAREN@14..15 "("
+      R_PAREN@15..16 ")"
+    WHITESPACE@16..17 " "
+    BLOCK_EXPR@17..19
+      L_CURLY@17..18 "{"
+      R_CURLY@18..19 "}"
+  WHITESPACE@19..20 "\n"
diff --git a/crates/syntax/test_data/parser/inline/ok/0184_generic_param_list.rs b/crates/syntax/test_data/parser/inline/ok/0184_generic_param_list.rs
new file mode 100644 (file)
index 0000000..b250bc6
--- /dev/null
@@ -0,0 +1 @@
+fn f<T: Clone>() {}
diff --git a/crates/syntax/test_data/parser/inline/ok/0185_assoc_type_bound.rast b/crates/syntax/test_data/parser/inline/ok/0185_assoc_type_bound.rast
new file mode 100644 (file)
index 0000000..24519dc
--- /dev/null
@@ -0,0 +1,37 @@
+SOURCE_FILE@0..45
+  TYPE_ALIAS@0..44
+    TYPE_KW@0..4 "type"
+    WHITESPACE@4..5 " "
+    NAME@5..6
+      IDENT@5..6 "T"
+    WHITESPACE@6..7 " "
+    EQ@7..8 "="
+    WHITESPACE@8..9 " "
+    PATH_TYPE@9..43
+      PATH@9..43
+        PATH_SEGMENT@9..43
+          NAME_REF@9..26
+            IDENT@9..26 "StreamingIterator"
+          GENERIC_ARG_LIST@26..43
+            L_ANGLE@26..27 "<"
+            ASSOC_TYPE_ARG@27..42
+              NAME_REF@27..31
+                IDENT@27..31 "Item"
+              GENERIC_ARG_LIST@31..35
+                L_ANGLE@31..32 "<"
+                LIFETIME_ARG@32..34
+                  LIFETIME@32..34
+                    LIFETIME_IDENT@32..34 "'a"
+                R_ANGLE@34..35 ">"
+              COLON@35..36 ":"
+              WHITESPACE@36..37 " "
+              TYPE_BOUND_LIST@37..42
+                TYPE_BOUND@37..42
+                  PATH_TYPE@37..42
+                    PATH@37..42
+                      PATH_SEGMENT@37..42
+                        NAME_REF@37..42
+                          IDENT@37..42 "Clone"
+            R_ANGLE@42..43 ">"
+    SEMICOLON@43..44 ";"
+  WHITESPACE@44..45 "\n"
diff --git a/crates/syntax/test_data/parser/inline/ok/0185_assoc_type_bound.rs b/crates/syntax/test_data/parser/inline/ok/0185_assoc_type_bound.rs
new file mode 100644 (file)
index 0000000..daae97e
--- /dev/null
@@ -0,0 +1 @@
+type T = StreamingIterator<Item<'a>: Clone>;
diff --git a/crates/syntax/test_data/parser/inline/ok/0186_lifetime_arg.rast b/crates/syntax/test_data/parser/inline/ok/0186_lifetime_arg.rast
new file mode 100644 (file)
index 0000000..812d222
--- /dev/null
@@ -0,0 +1,22 @@
+SOURCE_FILE@0..21
+  TYPE_ALIAS@0..20
+    TYPE_KW@0..4 "type"
+    WHITESPACE@4..5 " "
+    NAME@5..6
+      IDENT@5..6 "T"
+    WHITESPACE@6..7 " "
+    EQ@7..8 "="
+    WHITESPACE@8..9 " "
+    PATH_TYPE@9..19
+      PATH@9..19
+        PATH_SEGMENT@9..19
+          NAME_REF@9..10
+            IDENT@9..10 "S"
+          GENERIC_ARG_LIST@10..19
+            L_ANGLE@10..11 "<"
+            LIFETIME_ARG@11..18
+              LIFETIME@11..18
+                LIFETIME_IDENT@11..18 "'static"
+            R_ANGLE@18..19 ">"
+    SEMICOLON@19..20 ";"
+  WHITESPACE@20..21 "\n"
diff --git a/crates/syntax/test_data/parser/inline/ok/0186_lifetime_arg.rs b/crates/syntax/test_data/parser/inline/ok/0186_lifetime_arg.rs
new file mode 100644 (file)
index 0000000..41715aa
--- /dev/null
@@ -0,0 +1 @@
+type T = S<'static>;
diff --git a/crates/syntax/test_data/parser/inline/ok/0187_assoc_type_eq.rast b/crates/syntax/test_data/parser/inline/ok/0187_assoc_type_eq.rast
new file mode 100644 (file)
index 0000000..308cea7
--- /dev/null
@@ -0,0 +1,41 @@
+SOURCE_FILE@0..46
+  TYPE_ALIAS@0..45
+    TYPE_KW@0..4 "type"
+    WHITESPACE@4..5 " "
+    NAME@5..6
+      IDENT@5..6 "T"
+    WHITESPACE@6..7 " "
+    EQ@7..8 "="
+    WHITESPACE@8..9 " "
+    PATH_TYPE@9..44
+      PATH@9..44
+        PATH_SEGMENT@9..44
+          NAME_REF@9..26
+            IDENT@9..26 "StreamingIterator"
+          GENERIC_ARG_LIST@26..44
+            L_ANGLE@26..27 "<"
+            ASSOC_TYPE_ARG@27..43
+              NAME_REF@27..31
+                IDENT@27..31 "Item"
+              GENERIC_ARG_LIST@31..35
+                L_ANGLE@31..32 "<"
+                LIFETIME_ARG@32..34
+                  LIFETIME@32..34
+                    LIFETIME_IDENT@32..34 "'a"
+                R_ANGLE@34..35 ">"
+              WHITESPACE@35..36 " "
+              EQ@36..37 "="
+              WHITESPACE@37..38 " "
+              REF_TYPE@38..43
+                AMP@38..39 "&"
+                LIFETIME@39..41
+                  LIFETIME_IDENT@39..41 "'a"
+                WHITESPACE@41..42 " "
+                PATH_TYPE@42..43
+                  PATH@42..43
+                    PATH_SEGMENT@42..43
+                      NAME_REF@42..43
+                        IDENT@42..43 "T"
+            R_ANGLE@43..44 ">"
+    SEMICOLON@44..45 ";"
+  WHITESPACE@45..46 "\n"
diff --git a/crates/syntax/test_data/parser/inline/ok/0187_assoc_type_eq.rs b/crates/syntax/test_data/parser/inline/ok/0187_assoc_type_eq.rs
new file mode 100644 (file)
index 0000000..3591417
--- /dev/null
@@ -0,0 +1 @@
+type T = StreamingIterator<Item<'a> = &'a T>;
diff --git a/crates/syntax/test_data/parser/inline/ok/0188_const_arg_path.rast b/crates/syntax/test_data/parser/inline/ok/0188_const_arg_path.rast
new file mode 100644 (file)
index 0000000..8da0121
--- /dev/null
@@ -0,0 +1,37 @@
+SOURCE_FILE@0..35
+  STRUCT@0..34
+    STRUCT_KW@0..6 "struct"
+    WHITESPACE@6..7 " "
+    NAME@7..8
+      IDENT@7..8 "S"
+    GENERIC_PARAM_LIST@8..33
+      L_ANGLE@8..9 "<"
+      CONST_PARAM@9..32
+        CONST_KW@9..14 "const"
+        WHITESPACE@14..15 " "
+        NAME@15..16
+          IDENT@15..16 "N"
+        COLON@16..17 ":"
+        WHITESPACE@17..18 " "
+        PATH_TYPE@18..21
+          PATH@18..21
+            PATH_SEGMENT@18..21
+              NAME_REF@18..21
+                IDENT@18..21 "u32"
+        WHITESPACE@21..22 " "
+        EQ@22..23 "="
+        WHITESPACE@23..24 " "
+        CONST_ARG@24..32
+          PATH_EXPR@24..32
+            PATH@24..32
+              PATH@24..27
+                PATH_SEGMENT@24..27
+                  NAME_REF@24..27
+                    IDENT@24..27 "u32"
+              COLON2@27..29 "::"
+              PATH_SEGMENT@29..32
+                NAME_REF@29..32
+                  IDENT@29..32 "MAX"
+      R_ANGLE@32..33 ">"
+    SEMICOLON@33..34 ";"
+  WHITESPACE@34..35 "\n"
diff --git a/crates/syntax/test_data/parser/inline/ok/0188_const_arg_path.rs b/crates/syntax/test_data/parser/inline/ok/0188_const_arg_path.rs
new file mode 100644 (file)
index 0000000..ee075f3
--- /dev/null
@@ -0,0 +1 @@
+struct S<const N: u32 = u32::MAX>;
diff --git a/crates/syntax/test_data/parser/inline/ok/0189_const_arg_literal.rast b/crates/syntax/test_data/parser/inline/ok/0189_const_arg_literal.rast
new file mode 100644 (file)
index 0000000..a94cd25
--- /dev/null
@@ -0,0 +1,27 @@
+SOURCE_FILE@0..33
+  TYPE_ALIAS@0..32
+    TYPE_KW@0..4 "type"
+    WHITESPACE@4..5 " "
+    NAME@5..6
+      IDENT@5..6 "T"
+    WHITESPACE@6..7 " "
+    EQ@7..8 "="
+    WHITESPACE@8..9 " "
+    PATH_TYPE@9..31
+      PATH@9..31
+        PATH_SEGMENT@9..31
+          NAME_REF@9..10
+            IDENT@9..10 "S"
+          GENERIC_ARG_LIST@10..31
+            L_ANGLE@10..11 "<"
+            CONST_ARG@11..18
+              LITERAL@11..18
+                STRING@11..18 "\"hello\""
+            COMMA@18..19 ","
+            WHITESPACE@19..20 " "
+            CONST_ARG@20..30
+              LITERAL@20..30
+                INT_NUMBER@20..30 "0xdeadbeef"
+            R_ANGLE@30..31 ">"
+    SEMICOLON@31..32 ";"
+  WHITESPACE@32..33 "\n"
diff --git a/crates/syntax/test_data/parser/inline/ok/0189_const_arg_literal.rs b/crates/syntax/test_data/parser/inline/ok/0189_const_arg_literal.rs
new file mode 100644 (file)
index 0000000..7eacada
--- /dev/null
@@ -0,0 +1 @@
+type T = S<"hello", 0xdeadbeef>;
diff --git a/crates/syntax/test_data/parser/inline/ok/0190_generic_arg.rast b/crates/syntax/test_data/parser/inline/ok/0190_generic_arg.rast
new file mode 100644 (file)
index 0000000..c41d290
--- /dev/null
@@ -0,0 +1,25 @@
+SOURCE_FILE@0..17
+  TYPE_ALIAS@0..16
+    TYPE_KW@0..4 "type"
+    WHITESPACE@4..5 " "
+    NAME@5..6
+      IDENT@5..6 "T"
+    WHITESPACE@6..7 " "
+    EQ@7..8 "="
+    WHITESPACE@8..9 " "
+    PATH_TYPE@9..15
+      PATH@9..15
+        PATH_SEGMENT@9..15
+          NAME_REF@9..10
+            IDENT@9..10 "S"
+          GENERIC_ARG_LIST@10..15
+            L_ANGLE@10..11 "<"
+            TYPE_ARG@11..14
+              PATH_TYPE@11..14
+                PATH@11..14
+                  PATH_SEGMENT@11..14
+                    NAME_REF@11..14
+                      IDENT@11..14 "i32"
+            R_ANGLE@14..15 ">"
+    SEMICOLON@15..16 ";"
+  WHITESPACE@16..17 "\n"
diff --git a/crates/syntax/test_data/parser/inline/ok/0190_generic_arg.rs b/crates/syntax/test_data/parser/inline/ok/0190_generic_arg.rs
new file mode 100644 (file)
index 0000000..f2ccc55
--- /dev/null
@@ -0,0 +1 @@
+type T = S<i32>;
diff --git a/crates/syntax/test_data/parser/inline/ok/0191_const_arg_negative_number.rast b/crates/syntax/test_data/parser/inline/ok/0191_const_arg_negative_number.rast
new file mode 100644 (file)
index 0000000..87ff6cb
--- /dev/null
@@ -0,0 +1,24 @@
+SOURCE_FILE@0..17
+  TYPE_ALIAS@0..16
+    TYPE_KW@0..4 "type"
+    WHITESPACE@4..5 " "
+    NAME@5..6
+      IDENT@5..6 "T"
+    WHITESPACE@6..7 " "
+    EQ@7..8 "="
+    WHITESPACE@8..9 " "
+    PATH_TYPE@9..15
+      PATH@9..15
+        PATH_SEGMENT@9..15
+          NAME_REF@9..10
+            IDENT@9..10 "S"
+          GENERIC_ARG_LIST@10..15
+            L_ANGLE@10..11 "<"
+            CONST_ARG@11..14
+              PREFIX_EXPR@11..14
+                MINUS@11..12 "-"
+                LITERAL@12..14
+                  INT_NUMBER@12..14 "92"
+            R_ANGLE@14..15 ">"
+    SEMICOLON@15..16 ";"
+  WHITESPACE@16..17 "\n"
diff --git a/crates/syntax/test_data/parser/inline/ok/0191_const_arg_negative_number.rs b/crates/syntax/test_data/parser/inline/ok/0191_const_arg_negative_number.rs
new file mode 100644 (file)
index 0000000..d0a87bd
--- /dev/null
@@ -0,0 +1 @@
+type T = S<-92>;
diff --git a/crates/syntax/test_data/parser/inline/ok/0192_const_arg_bool_literal.rast b/crates/syntax/test_data/parser/inline/ok/0192_const_arg_bool_literal.rast
new file mode 100644 (file)
index 0000000..7c44c6b
--- /dev/null
@@ -0,0 +1,22 @@
+SOURCE_FILE@0..18
+  TYPE_ALIAS@0..17
+    TYPE_KW@0..4 "type"
+    WHITESPACE@4..5 " "
+    NAME@5..6
+      IDENT@5..6 "T"
+    WHITESPACE@6..7 " "
+    EQ@7..8 "="
+    WHITESPACE@8..9 " "
+    PATH_TYPE@9..16
+      PATH@9..16
+        PATH_SEGMENT@9..16
+          NAME_REF@9..10
+            IDENT@9..10 "S"
+          GENERIC_ARG_LIST@10..16
+            L_ANGLE@10..11 "<"
+            CONST_ARG@11..15
+              LITERAL@11..15
+                TRUE_KW@11..15 "true"
+            R_ANGLE@15..16 ">"
+    SEMICOLON@16..17 ";"
+  WHITESPACE@17..18 "\n"
diff --git a/crates/syntax/test_data/parser/inline/ok/0192_const_arg_bool_literal.rs b/crates/syntax/test_data/parser/inline/ok/0192_const_arg_bool_literal.rs
new file mode 100644 (file)
index 0000000..4b92e2d
--- /dev/null
@@ -0,0 +1 @@
+type T = S<true>;
index 8b1e0a52da3144d69af57231dee1dd40476b9b2a..712e1534625905cda8e1d0fbae1474be460be89c 100644 (file)
@@ -1,93 +1,76 @@
-SOURCE_FILE@0..118
-  MODULE@0..6
+SOURCE_FILE@0..100
+  MODULE@0..46
     MOD_KW@0..3 "mod"
     WHITESPACE@3..4 " "
     NAME@4..5
-      IDENT@4..5 "a"
-    SEMICOLON@5..6 ";"
-  WHITESPACE@6..8 "\n\n"
-  MODULE@8..17
-    MOD_KW@8..11 "mod"
-    WHITESPACE@11..12 " "
-    NAME@12..13
-      IDENT@12..13 "b"
-    WHITESPACE@13..14 " "
-    ITEM_LIST@14..17
-      L_CURLY@14..15 "{"
-      WHITESPACE@15..16 "\n"
-      R_CURLY@16..17 "}"
-  WHITESPACE@17..19 "\n\n"
-  MODULE@19..65
-    MOD_KW@19..22 "mod"
-    WHITESPACE@22..23 " "
-    NAME@23..24
-      IDENT@23..24 "c"
-    WHITESPACE@24..25 " "
-    ITEM_LIST@25..65
-      L_CURLY@25..26 "{"
-      WHITESPACE@26..31 "\n    "
-      FN@31..47
-        FN_KW@31..33 "fn"
-        WHITESPACE@33..34 " "
-        NAME@34..37
-          IDENT@34..37 "foo"
-        PARAM_LIST@37..39
-          L_PAREN@37..38 "("
-          R_PAREN@38..39 ")"
+      IDENT@4..5 "c"
+    WHITESPACE@5..6 " "
+    ITEM_LIST@6..46
+      L_CURLY@6..7 "{"
+      WHITESPACE@7..12 "\n    "
+      FN@12..28
+        FN_KW@12..14 "fn"
+        WHITESPACE@14..15 " "
+        NAME@15..18
+          IDENT@15..18 "foo"
+        PARAM_LIST@18..20
+          L_PAREN@18..19 "("
+          R_PAREN@19..20 ")"
+        WHITESPACE@20..21 " "
+        BLOCK_EXPR@21..28
+          L_CURLY@21..22 "{"
+          WHITESPACE@22..27 "\n    "
+          R_CURLY@27..28 "}"
+      WHITESPACE@28..33 "\n    "
+      STRUCT@33..44
+        STRUCT_KW@33..39 "struct"
         WHITESPACE@39..40 " "
-        BLOCK_EXPR@40..47
-          L_CURLY@40..41 "{"
-          WHITESPACE@41..46 "\n    "
-          R_CURLY@46..47 "}"
-      WHITESPACE@47..52 "\n    "
-      STRUCT@52..63
-        STRUCT_KW@52..58 "struct"
-        WHITESPACE@58..59 " "
-        NAME@59..60
-          IDENT@59..60 "S"
-        WHITESPACE@60..61 " "
-        RECORD_FIELD_LIST@61..63
-          L_CURLY@61..62 "{"
-          R_CURLY@62..63 "}"
-      WHITESPACE@63..64 "\n"
-      R_CURLY@64..65 "}"
-  WHITESPACE@65..67 "\n\n"
-  MODULE@67..118
-    MOD_KW@67..70 "mod"
-    WHITESPACE@70..71 " "
-    NAME@71..72
-      IDENT@71..72 "d"
-    WHITESPACE@72..73 " "
-    ITEM_LIST@73..118
-      L_CURLY@73..74 "{"
-      WHITESPACE@74..79 "\n    "
-      ATTR@79..87
-        POUND@79..80 "#"
-        BANG@80..81 "!"
-        L_BRACK@81..82 "["
-        META@82..86
-          PATH@82..86
-            PATH_SEGMENT@82..86
-              NAME_REF@82..86
-                IDENT@82..86 "attr"
-        R_BRACK@86..87 "]"
-      WHITESPACE@87..92 "\n    "
-      MODULE@92..98
-        MOD_KW@92..95 "mod"
-        WHITESPACE@95..96 " "
-        NAME@96..97
-          IDENT@96..97 "e"
-        SEMICOLON@97..98 ";"
-      WHITESPACE@98..103 "\n    "
-      MODULE@103..116
-        MOD_KW@103..106 "mod"
-        WHITESPACE@106..107 " "
-        NAME@107..108
-          IDENT@107..108 "f"
-        WHITESPACE@108..109 " "
-        ITEM_LIST@109..116
-          L_CURLY@109..110 "{"
-          WHITESPACE@110..115 "\n    "
-          R_CURLY@115..116 "}"
-      WHITESPACE@116..117 "\n"
-      R_CURLY@117..118 "}"
+        NAME@40..41
+          IDENT@40..41 "S"
+        WHITESPACE@41..42 " "
+        RECORD_FIELD_LIST@42..44
+          L_CURLY@42..43 "{"
+          R_CURLY@43..44 "}"
+      WHITESPACE@44..45 "\n"
+      R_CURLY@45..46 "}"
+  WHITESPACE@46..48 "\n\n"
+  MODULE@48..99
+    MOD_KW@48..51 "mod"
+    WHITESPACE@51..52 " "
+    NAME@52..53
+      IDENT@52..53 "d"
+    WHITESPACE@53..54 " "
+    ITEM_LIST@54..99
+      L_CURLY@54..55 "{"
+      WHITESPACE@55..60 "\n    "
+      ATTR@60..68
+        POUND@60..61 "#"
+        BANG@61..62 "!"
+        L_BRACK@62..63 "["
+        META@63..67
+          PATH@63..67
+            PATH_SEGMENT@63..67
+              NAME_REF@63..67
+                IDENT@63..67 "attr"
+        R_BRACK@67..68 "]"
+      WHITESPACE@68..73 "\n    "
+      MODULE@73..79
+        MOD_KW@73..76 "mod"
+        WHITESPACE@76..77 " "
+        NAME@77..78
+          IDENT@77..78 "e"
+        SEMICOLON@78..79 ";"
+      WHITESPACE@79..84 "\n    "
+      MODULE@84..97
+        MOD_KW@84..87 "mod"
+        WHITESPACE@87..88 " "
+        NAME@88..89
+          IDENT@88..89 "f"
+        WHITESPACE@89..90 " "
+        ITEM_LIST@90..97
+          L_CURLY@90..91 "{"
+          WHITESPACE@91..96 "\n    "
+          R_CURLY@96..97 "}"
+      WHITESPACE@97..98 "\n"
+      R_CURLY@98..99 "}"
+  WHITESPACE@99..100 "\n"
index d22993bc150f6f79670ca36cd931f1e76bcdbf32..4ff0d9795c781701edf44d062abaa68e37761015 100644 (file)
@@ -1,8 +1,3 @@
-mod a;
-
-mod b {
-}
-
 mod c {
     fn foo() {
     }
@@ -14,4 +9,4 @@ mod d {
     mod e;
     mod f {
     }
-}
\ No newline at end of file
+}
index c5dbfb702a14cc3bf1c86d703851361b547777a9..79dc9001a21650c35dd0fd4d28637b775b6dfff7 100644 (file)
@@ -1,4 +1,4 @@
-SOURCE_FILE@0..98
+SOURCE_FILE@0..122
   FN@0..9
     FN_KW@0..2 "fn"
     WHITESPACE@2..3 " "
@@ -28,81 +28,101 @@ SOURCE_FILE@0..98
       L_CURLY@21..22 "{"
       R_CURLY@22..23 "}"
   WHITESPACE@23..24 "\n"
-  FN@24..44
-    VISIBILITY@24..34
+  MACRO_DEF@24..47
+    VISIBILITY@24..27
       PUB_KW@24..27 "pub"
-      L_PAREN@27..28 "("
-      PATH@28..33
-        PATH_SEGMENT@28..33
-          NAME_REF@28..33
-            CRATE_KW@28..33 "crate"
-      R_PAREN@33..34 ")"
-    WHITESPACE@34..35 " "
-    FN_KW@35..37 "fn"
-    WHITESPACE@37..38 " "
-    NAME@38..39
-      IDENT@38..39 "c"
-    PARAM_LIST@39..41
-      L_PAREN@39..40 "("
-      R_PAREN@40..41 ")"
-    WHITESPACE@41..42 " "
-    BLOCK_EXPR@42..44
-      L_CURLY@42..43 "{"
-      R_CURLY@43..44 "}"
-  WHITESPACE@44..45 "\n"
-  FN@45..65
-    VISIBILITY@45..55
-      PUB_KW@45..48 "pub"
-      L_PAREN@48..49 "("
-      PATH@49..54
-        PATH_SEGMENT@49..54
-          NAME_REF@49..54
-            SUPER_KW@49..54 "super"
-      R_PAREN@54..55 ")"
-    WHITESPACE@55..56 " "
-    FN_KW@56..58 "fn"
+    WHITESPACE@27..28 " "
+    MACRO_KW@28..33 "macro"
+    WHITESPACE@33..34 " "
+    NAME@34..35
+      IDENT@34..35 "m"
+    TOKEN_TREE@35..47
+      TOKEN_TREE@35..44
+        L_PAREN@35..36 "("
+        DOLLAR@36..37 "$"
+        COLON@37..38 ":"
+        IDENT@38..43 "ident"
+        R_PAREN@43..44 ")"
+      WHITESPACE@44..45 " "
+      TOKEN_TREE@45..47
+        L_CURLY@45..46 "{"
+        R_CURLY@46..47 "}"
+  WHITESPACE@47..48 "\n"
+  FN@48..68
+    VISIBILITY@48..58
+      PUB_KW@48..51 "pub"
+      L_PAREN@51..52 "("
+      PATH@52..57
+        PATH_SEGMENT@52..57
+          NAME_REF@52..57
+            CRATE_KW@52..57 "crate"
+      R_PAREN@57..58 ")"
     WHITESPACE@58..59 " "
-    NAME@59..60
-      IDENT@59..60 "d"
-    PARAM_LIST@60..62
-      L_PAREN@60..61 "("
-      R_PAREN@61..62 ")"
-    WHITESPACE@62..63 " "
-    BLOCK_EXPR@63..65
-      L_CURLY@63..64 "{"
-      R_CURLY@64..65 "}"
-  WHITESPACE@65..66 "\n"
-  FN@66..97
-    VISIBILITY@66..87
-      PUB_KW@66..69 "pub"
-      L_PAREN@69..70 "("
-      IN_KW@70..72 "in"
-      WHITESPACE@72..73 " "
-      PATH@73..86
-        PATH@73..81
-          PATH@73..76
-            PATH_SEGMENT@73..76
-              NAME_REF@73..76
-                IDENT@73..76 "foo"
-          COLON2@76..78 "::"
-          PATH_SEGMENT@78..81
-            NAME_REF@78..81
-              IDENT@78..81 "bar"
-        COLON2@81..83 "::"
-        PATH_SEGMENT@83..86
-          NAME_REF@83..86
-            IDENT@83..86 "baz"
-      R_PAREN@86..87 ")"
-    WHITESPACE@87..88 " "
-    FN_KW@88..90 "fn"
-    WHITESPACE@90..91 " "
-    NAME@91..92
-      IDENT@91..92 "e"
-    PARAM_LIST@92..94
-      L_PAREN@92..93 "("
-      R_PAREN@93..94 ")"
-    WHITESPACE@94..95 " "
-    BLOCK_EXPR@95..97
-      L_CURLY@95..96 "{"
-      R_CURLY@96..97 "}"
-  WHITESPACE@97..98 "\n"
+    FN_KW@59..61 "fn"
+    WHITESPACE@61..62 " "
+    NAME@62..63
+      IDENT@62..63 "c"
+    PARAM_LIST@63..65
+      L_PAREN@63..64 "("
+      R_PAREN@64..65 ")"
+    WHITESPACE@65..66 " "
+    BLOCK_EXPR@66..68
+      L_CURLY@66..67 "{"
+      R_CURLY@67..68 "}"
+  WHITESPACE@68..69 "\n"
+  FN@69..89
+    VISIBILITY@69..79
+      PUB_KW@69..72 "pub"
+      L_PAREN@72..73 "("
+      PATH@73..78
+        PATH_SEGMENT@73..78
+          NAME_REF@73..78
+            SUPER_KW@73..78 "super"
+      R_PAREN@78..79 ")"
+    WHITESPACE@79..80 " "
+    FN_KW@80..82 "fn"
+    WHITESPACE@82..83 " "
+    NAME@83..84
+      IDENT@83..84 "d"
+    PARAM_LIST@84..86
+      L_PAREN@84..85 "("
+      R_PAREN@85..86 ")"
+    WHITESPACE@86..87 " "
+    BLOCK_EXPR@87..89
+      L_CURLY@87..88 "{"
+      R_CURLY@88..89 "}"
+  WHITESPACE@89..90 "\n"
+  FN@90..121
+    VISIBILITY@90..111
+      PUB_KW@90..93 "pub"
+      L_PAREN@93..94 "("
+      IN_KW@94..96 "in"
+      WHITESPACE@96..97 " "
+      PATH@97..110
+        PATH@97..105
+          PATH@97..100
+            PATH_SEGMENT@97..100
+              NAME_REF@97..100
+                IDENT@97..100 "foo"
+          COLON2@100..102 "::"
+          PATH_SEGMENT@102..105
+            NAME_REF@102..105
+              IDENT@102..105 "bar"
+        COLON2@105..107 "::"
+        PATH_SEGMENT@107..110
+          NAME_REF@107..110
+            IDENT@107..110 "baz"
+      R_PAREN@110..111 ")"
+    WHITESPACE@111..112 " "
+    FN_KW@112..114 "fn"
+    WHITESPACE@114..115 " "
+    NAME@115..116
+      IDENT@115..116 "e"
+    PARAM_LIST@116..118
+      L_PAREN@116..117 "("
+      R_PAREN@117..118 ")"
+    WHITESPACE@118..119 " "
+    BLOCK_EXPR@119..121
+      L_CURLY@119..120 "{"
+      R_CURLY@120..121 "}"
+  WHITESPACE@121..122 "\n"
index 75b1db12134fb9f6bb4825dcd59e9ef5b9e7843c..129d486fae2aaf845e9dd5110e158ae01d07ab5f 100644 (file)
@@ -1,5 +1,6 @@
 fn a() {}
 pub fn b() {}
+pub macro m($:ident) {}
 pub(crate) fn c() {}
 pub(super) fn d() {}
 pub(in foo::bar::baz) fn e() {}
index b89ed6f98e05b303da29e15fce7076e26c69b06e..6b234b0b2418e9767eb76efedf39c42d25058f1f 100644 (file)
@@ -1,38 +1 @@
-SOURCE_FILE@0..39
-  CONST@0..17
-    CONST_KW@0..5 "const"
-    WHITESPACE@5..6 " "
-    UNDERSCORE@6..7 "_"
-    COLON@7..8 ":"
-    WHITESPACE@8..9 " "
-    PATH_TYPE@9..12
-      PATH@9..12
-        PATH_SEGMENT@9..12
-          NAME_REF@9..12
-            IDENT@9..12 "u32"
-    WHITESPACE@12..13 " "
-    EQ@13..14 "="
-    WHITESPACE@14..15 " "
-    LITERAL@15..16
-      INT_NUMBER@15..16 "0"
-    SEMICOLON@16..17 ";"
-  WHITESPACE@17..18 "\n"
-  CONST@18..38
-    CONST_KW@18..23 "const"
-    WHITESPACE@23..24 " "
-    NAME@24..27
-      IDENT@24..27 "FOO"
-    COLON@27..28 ":"
-    WHITESPACE@28..29 " "
-    PATH_TYPE@29..32
-      PATH@29..32
-        PATH_SEGMENT@29..32
-          NAME_REF@29..32
-            IDENT@29..32 "u32"
-    WHITESPACE@32..33 " "
-    EQ@33..34 "="
-    WHITESPACE@34..35 " "
-    LITERAL@35..37
-      INT_NUMBER@35..37 "92"
-    SEMICOLON@37..38 ";"
-  WHITESPACE@38..39 "\n"
+SOURCE_FILE@0..0
index 1f2ffa0da384726ae0e87bd7eb560092e59a41a1..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 100644 (file)
@@ -1,2 +0,0 @@
-const _: u32 = 0;
-const FOO: u32 = 92;
index 166df3ea017f8f30aa81ff8df93afbb292875331..71cc67a9ca3212f8b614cbab004759e1c03de632 100644 (file)
@@ -92,19 +92,32 @@ impl Fixture {
     ///  //- other meta
     ///  ```
     ///
-    /// Fixture can also start with a minicore declaration:
+    /// Fixture can also start with a proc_macros and minicore declaration(in that order):
     ///
     /// ```
+    /// //- proc_macros: identity
     /// //- minicore: sized
     /// ```
     ///
-    /// That will include a subset of `libcore` into the fixture, see
+    /// That will include predefined proc macros and a subset of `libcore` into the fixture, see
     /// `minicore.rs` for what's available.
-    pub fn parse(ra_fixture: &str) -> (Option<MiniCore>, Vec<Fixture>) {
+    pub fn parse(ra_fixture: &str) -> (Option<MiniCore>, Vec<String>, Vec<Fixture>) {
         let fixture = trim_indent(ra_fixture);
         let mut fixture = fixture.as_str();
         let mut mini_core = None;
         let mut res: Vec<Fixture> = Vec::new();
+        let mut test_proc_macros = vec![];
+
+        if fixture.starts_with("//- proc_macros:") {
+            let first_line = fixture.split_inclusive('\n').next().unwrap();
+            test_proc_macros = first_line
+                .strip_prefix("//- proc_macros:")
+                .unwrap()
+                .split(',')
+                .map(|it| it.trim().to_string())
+                .collect();
+            fixture = &fixture[first_line.len()..];
+        }
 
         if fixture.starts_with("//- minicore:") {
             let first_line = fixture.split_inclusive('\n').next().unwrap();
@@ -144,7 +157,7 @@ pub fn parse(ra_fixture: &str) -> (Option<MiniCore>, Vec<Fixture>) {
             }
         }
 
-        (mini_core, res)
+        (mini_core, test_proc_macros, res)
     }
 
     //- /lib.rs crate:foo deps:bar,baz cfg:foo=a,bar=b env:OUTDIR=path/to,OTHER=foo
@@ -333,7 +346,6 @@ pub fn source_code(mut self) -> String {
                 panic!("unused minicore flag: {:?}", flag);
             }
         }
-        format!("{}", buf);
         buf
     }
 }
@@ -355,13 +367,15 @@ pub fn baz() {}
 
 #[test]
 fn parse_fixture_gets_full_meta() {
-    let (mini_core, parsed) = Fixture::parse(
+    let (mini_core, proc_macros, parsed) = Fixture::parse(
         r#"
+//- proc_macros: identity
 //- minicore: coerce_unsized
 //- /lib.rs crate:foo deps:bar,baz cfg:foo=a,bar=b,atom env:OUTDIR=path/to,OTHER=foo
 mod m;
 "#,
     );
+    assert_eq!(proc_macros, vec!["identity".to_string()]);
     assert_eq!(mini_core.unwrap().activated_flags, vec!["coerce_unsized".to_string()]);
     assert_eq!(1, parsed.len());
 
index ecd589c58275616ec13c01c5a93133ec604fa39c..2f0af5e721489d3104bd55bf82dbb420f2a14d7a 100644 (file)
@@ -10,7 +10,6 @@ doctest = false
 
 [dependencies]
 tracing = "0.1"
-rustc-hash = "1.0"
 jod-thread = "0.1.0"
 walkdir = "2.3.1"
 crossbeam-channel = "0.5.0"
index 637438bd66c3834f4bb9926b0c67b69374d55e56..4fb34aed07a21c6910dfe5ff50d6130c57a1dd73 100644 (file)
@@ -18,7 +18,7 @@
 pub struct NotifyHandle {
     // Relative order of fields below is significant.
     sender: Sender<Message>,
-    thread: jod_thread::JoinHandle,
+    _thread: jod_thread::JoinHandle,
 }
 
 #[derive(Debug)]
@@ -35,7 +35,7 @@ fn spawn(sender: loader::Sender) -> NotifyHandle {
             .name("VfsLoader".to_owned())
             .spawn(move || actor.run(receiver))
             .expect("failed to spawn thread");
-        NotifyHandle { sender, thread }
+        NotifyHandle { sender, _thread: thread }
     }
     fn set_config(&mut self, config: loader::Config) {
         self.sender.send(Message::Config(config)).unwrap()
index f73e542037065d61709ea7c98b6a2ec6ac7d1aaa..5e7b7222262b10db6a1496bf56e01956fa10895e 100644 (file)
@@ -16,7 +16,7 @@ The path structure for newly inserted paths to use.
 [[rust-analyzer.assist.importGroup]]rust-analyzer.assist.importGroup (default: `true`)::
 +
 --
-Group inserted imports by the [following order](https://rust-analyzer.github.io/manual.html#auto-import). Groups are separated by newlines.
+Group inserted imports by the https://rust-analyzer.github.io/manual.html#auto-import[following order]. Groups are separated by newlines.
 --
 [[rust-analyzer.assist.allowMergingIntoGlobImports]]rust-analyzer.assist.allowMergingIntoGlobImports (default: `true`)::
 +
index ee42702660865e17b5c2b192d2b475144fa4a7dd..0afca215ecb4d446441114e5a8a420e15b658884 100644 (file)
@@ -6,6 +6,10 @@
 :source-highlighter: rouge
 :experimental:
 
+////
+IMPORTANT: the master copy of this document lives in the https://github.com/rust-analyzer/rust-analyzer repository
+////
+
 At its core, rust-analyzer is a *library* for semantic analysis of Rust code as it changes over time.
 This manual focuses on a specific usage of the library -- running it as part of a server that implements the
 https://microsoft.github.io/language-server-protocol/[Language Server Protocol] (LSP).
@@ -583,10 +587,10 @@ interface Crate {
     target?: string;
     /// Environment variables, used for
     /// the `env!` macro
-    env: { [key: string]: string; },
+    env: { [key: string]: string; },
 
     /// Whether the crate is a proc-macro crate.
-    is_proc_macro: bool;
+    is_proc_macro: boolean;
     /// For proc-macro crates, path to compiled
     /// proc-macro (.so file).
     proc_macro_dylib_path?: string;
@@ -623,7 +627,7 @@ Note that calls to `cargo check` are disabled when using `rust-project.json` by
 { "rust-analyzer.checkOnSave.overrideCommand": ["cargo", "check", "--message-format=json"] }
 ----
 
-The `checkOnSave.overrideCommand` requires the command specified to output json error messages for rust-analyzer to consume. The `--message-format=json` flag does this for `cargo check` so whichever command you use must also output errors in this format. See the <<Configuration>> section for more information. 
+The `checkOnSave.overrideCommand` requires the command specified to output json error messages for rust-analyzer to consume. The `--message-format=json` flag does this for `cargo check` so whichever command you use must also output errors in this format. See the <<Configuration>> section for more information.
 
 == Security
 
index aed67e88ab081999a503f5866f13e0e5979a092e..5edfd4aa5f6eac0e4a328a029a265e35316ba654 100644 (file)
                     ]
                 },
                 "rust-analyzer.assist.importGroup": {
-                    "markdownDescription": "Group inserted imports by the [following order](https://rust-analyzer.github.io/manual.html#auto-import). Groups are separated by newlines.",
+                    "markdownDescription": "Group inserted imports by the https://rust-analyzer.github.io/manual.html#auto-import[following order]. Groups are separated by newlines.",
                     "default": true,
                     "type": "boolean"
                 },
index ece700138252b43ec8fd1452d78de22fa37d5c3b..0e08b60a93c416cb89c309fcd31a1ae52d87ab20 100644 (file)
@@ -292,7 +292,7 @@ export function toggleInlayHints(ctx: Ctx): Cmd {
         await vscode
             .workspace
             .getConfiguration(`${ctx.config.rootSection}.inlayHints`)
-            .update('enable', !ctx.config.inlayHints.enable, vscode.ConfigurationTarget.Workspace);
+            .update('enable', !ctx.config.inlayHints.enable, vscode.ConfigurationTarget.Global);
     };
 }
 
index fbd9434d906c632570f4aca96e6fa3f506eddfed..ddab32d02055eb152ac36572e8fa788e5edfef9e 100644 (file)
@@ -124,9 +124,12 @@ export async function download(opts: DownloadOpts) {
         log.info(`Renamed old server binary ${opts.dest.fsPath} to ${oldServerPath.fsPath}`);
     } catch (err) {
         const fsErr = err as vscode.FileSystemError;
-        // This is supposed to return `FileNotFound`, alas...
-        if (!fsErr.code || fsErr.code !== "FileNotFound" && fsErr.code !== "EntryNotFound") {
-            log.error(`Cannot rename existing server instance: ${err}`);
+
+        // This is supposed to return `FileNotFound` (spelled as `EntryNotFound`)
+        // but instead `code` is `Unknown` and `name` is `EntryNotFound (FileSystemError) (FileSystemError)`.
+        // https://github.com/rust-analyzer/rust-analyzer/pull/10222
+        if (!fsErr.code || fsErr.code !== "EntryNotFound" && fsErr.name.indexOf("EntryNotFound") === -1) {
+            log.error(`Cannot rename existing server instance: ${err}"`);
         }
     }
     try {
index 64ab12b42a9ac59f9a6367b04b4e078c5705243a..922957f57d1833f209d0538ff7c093e89184f3c2 100644 (file)
@@ -8,7 +8,7 @@
 use crate::flags;
 
 // Latest stable, feel free to send a PR if this lags behind.
-const REQUIRED_RUST_VERSION: u32 = 53;
+const REQUIRED_RUST_VERSION: u32 = 55;
 
 impl flags::Install {
     pub(crate) fn run(self) -> Result<()> {
index 37de5b36f18bd677bcddcd8408b23da845977463..e4f540d27f5668143a3feea84be2d206feed07af 100644 (file)
@@ -45,7 +45,7 @@ pub(crate) fn run(self) -> Result<()> {
             let src = project_root().join("./docs/user/").join(adoc);
             let dst = website_root.join(adoc);
 
-            let contents = read_file(src)?.replace("\n\n===", "\n\n// IMPORTANT: master copy of this document lives in the https://github.com/rust-analyzer/rust-analyzer repository\n\n==");
+            let contents = read_file(src)?;
             write_file(dst, contents)?;
         }