aboutsummaryrefslogtreecommitdiff
path: root/crates
diff options
context:
space:
mode:
Diffstat (limited to 'crates')
-rw-r--r--crates/assists/src/handlers/extract_struct_from_enum_variant.rs59
-rw-r--r--crates/assists/src/handlers/inline_local_variable.rs99
-rw-r--r--crates/assists/src/handlers/remove_dbg.rs12
-rw-r--r--crates/assists/src/handlers/remove_unused_param.rs41
-rw-r--r--crates/assists/src/handlers/reorder_impl.rs201
-rw-r--r--crates/assists/src/lib.rs2
-rw-r--r--crates/assists/src/tests/generated.rs35
-rw-r--r--crates/completion/src/completions/attribute.rs21
-rw-r--r--crates/completion/src/completions/dot.rs8
-rw-r--r--crates/completion/src/completions/postfix.rs26
-rw-r--r--crates/completion/src/completions/trait_impl.rs6
-rw-r--r--crates/completion/src/patterns.rs10
-rw-r--r--crates/flycheck/Cargo.toml2
-rw-r--r--crates/hir/src/code_model.rs18
-rw-r--r--crates/hir/src/from_id.rs2
-rw-r--r--crates/hir_def/src/body/lower.rs4
-rw-r--r--crates/hir_def/src/expr.rs2
-rw-r--r--crates/hir_def/src/nameres/collector.rs8
-rw-r--r--crates/hir_def/src/per_ns.rs2
-rw-r--r--crates/hir_def/src/resolver.rs12
-rw-r--r--crates/hir_def/src/visibility.rs2
-rw-r--r--crates/hir_expand/src/db.rs2
-rw-r--r--crates/hir_ty/src/diagnostics/expr.rs4
-rw-r--r--crates/hir_ty/src/diagnostics/match_check.rs4
-rw-r--r--crates/hir_ty/src/display.rs100
-rw-r--r--crates/hir_ty/src/lower.rs6
-rw-r--r--crates/hir_ty/src/tests/display_source_code.rs15
-rw-r--r--crates/hir_ty/src/tests/traits.rs8
-rw-r--r--crates/ide/src/call_hierarchy.rs51
-rw-r--r--crates/ide/src/display/navigation_target.rs14
-rw-r--r--crates/ide/src/doc_links.rs34
-rw-r--r--crates/ide/src/goto_definition.rs93
-rw-r--r--crates/ide/src/hover.rs47
-rw-r--r--crates/ide/src/inlay_hints.rs62
-rw-r--r--crates/ide/src/lib.rs10
-rw-r--r--crates/ide/src/references.rs93
-rw-r--r--crates/ide/src/references/rename.rs341
-rw-r--r--crates/ide/src/runnables.rs110
-rw-r--r--crates/ide/src/syntax_highlighting.rs821
-rw-r--r--crates/ide/src/syntax_highlighting/format.rs94
-rw-r--r--crates/ide/src/syntax_highlighting/highlight.rs530
-rw-r--r--crates/ide/src/syntax_highlighting/highlights.rs92
-rw-r--r--crates/ide/src/syntax_highlighting/html.rs28
-rw-r--r--crates/ide/src/syntax_highlighting/inject.rs158
-rw-r--r--crates/ide/src/syntax_highlighting/injection.rs240
-rw-r--r--crates/ide/src/syntax_highlighting/injector.rs78
-rw-r--r--crates/ide/src/syntax_highlighting/macro_rules.rs10
-rw-r--r--crates/ide/src/syntax_highlighting/tags.rs173
-rw-r--r--crates/ide/src/syntax_highlighting/test_data/highlight_assoc_functions.html28
-rw-r--r--crates/ide/src/syntax_highlighting/test_data/highlight_doctest.html71
-rw-r--r--crates/ide/src/syntax_highlighting/test_data/highlight_extern_crate.html4
-rw-r--r--crates/ide/src/syntax_highlighting/test_data/highlight_injection.html20
-rw-r--r--crates/ide/src/syntax_highlighting/test_data/highlight_strings.html106
-rw-r--r--crates/ide/src/syntax_highlighting/test_data/highlight_unsafe.html88
-rw-r--r--crates/ide/src/syntax_highlighting/test_data/highlighting.html260
-rw-r--r--crates/ide/src/syntax_highlighting/test_data/injection.html16
-rw-r--r--crates/ide/src/syntax_highlighting/test_data/rainbow_highlighting.html20
-rw-r--r--crates/ide/src/syntax_highlighting/tests.rs5
-rw-r--r--crates/ide_db/src/defs.rs36
-rw-r--r--crates/ide_db/src/helpers.rs89
-rw-r--r--crates/ide_db/src/helpers/famous_defs_fixture.rs120
-rw-r--r--crates/ide_db/src/imports_locator.rs2
-rw-r--r--crates/ide_db/src/search.rs112
-rw-r--r--crates/mbe/src/lib.rs4
-rw-r--r--crates/mbe/src/mbe_expander/matcher.rs2
-rw-r--r--crates/mbe/src/mbe_expander/transcriber.rs8
-rw-r--r--crates/mbe/src/syntax_bridge.rs12
-rw-r--r--crates/mbe/src/tests.rs2
-rw-r--r--crates/parser/src/grammar/expressions/atom.rs12
-rw-r--r--crates/parser/src/grammar/items.rs26
-rw-r--r--crates/parser/src/grammar/items/traits.rs2
-rw-r--r--crates/parser/src/grammar/items/use_item.rs2
-rw-r--r--crates/parser/src/grammar/patterns.rs2
-rw-r--r--crates/parser/src/grammar/type_args.rs6
-rw-r--r--crates/parser/src/grammar/type_params.rs4
-rw-r--r--crates/proc_macro_api/src/msg.rs2
-rw-r--r--crates/proc_macro_srv/Cargo.toml2
-rw-r--r--crates/proc_macro_srv/src/proc_macro/bridge/rpc.rs2
-rw-r--r--crates/proc_macro_srv/src/rustc_server.rs2
-rw-r--r--crates/project_model/Cargo.toml2
-rw-r--r--crates/rust-analyzer/src/caps.rs24
-rw-r--r--crates/rust-analyzer/src/cli/analysis_stats.rs39
-rw-r--r--crates/rust-analyzer/src/config.rs12
-rw-r--r--crates/rust-analyzer/src/global_state.rs3
-rw-r--r--crates/rust-analyzer/src/handlers.rs69
-rw-r--r--crates/rust-analyzer/src/lib.rs1
-rw-r--r--crates/rust-analyzer/src/lsp_utils.rs10
-rw-r--r--crates/rust-analyzer/src/main_loop.rs82
-rw-r--r--crates/rust-analyzer/src/markdown.rs2
-rw-r--r--crates/rust-analyzer/src/op_queue.rs25
-rw-r--r--crates/rust-analyzer/src/reload.rs69
-rw-r--r--crates/rust-analyzer/src/semantic_tokens.rs8
-rw-r--r--crates/rust-analyzer/src/to_proto.rs85
-rw-r--r--crates/rust-analyzer/tests/rust-analyzer/main.rs144
-rw-r--r--crates/ssr/src/search.rs20
-rw-r--r--crates/stdx/src/lib.rs18
-rw-r--r--crates/syntax/src/algo.rs4
-rw-r--r--crates/syntax/src/ast/make.rs2
-rw-r--r--crates/syntax/src/ast/node_ext.rs2
-rw-r--r--crates/syntax/src/parsing/lexer.rs8
-rw-r--r--crates/syntax/src/validation.rs6
-rw-r--r--crates/syntax/test_data/parser/inline/ok/0002_use_tree_list.rast98
-rw-r--r--crates/syntax/test_data/parser/inline/ok/0002_use_tree_list.rs2
-rw-r--r--crates/syntax/test_data/parser/inline/ok/0039_type_arg.rast28
-rw-r--r--crates/syntax/test_data/parser/inline/ok/0039_type_arg.rs2
-rw-r--r--crates/test_utils/src/lib.rs2
-rw-r--r--crates/vfs/src/anchored_path.rs10
-rw-r--r--crates/vfs/src/file_set.rs57
-rw-r--r--crates/vfs/src/lib.rs70
-rw-r--r--crates/vfs/src/loader.rs80
-rw-r--r--crates/vfs/src/path_interner.rs14
-rw-r--r--crates/vfs/src/vfs_path.rs94
112 files changed, 3591 insertions, 2358 deletions
diff --git a/crates/assists/src/handlers/extract_struct_from_enum_variant.rs b/crates/assists/src/handlers/extract_struct_from_enum_variant.rs
index 40028fc01..e3ef04932 100644
--- a/crates/assists/src/handlers/extract_struct_from_enum_variant.rs
+++ b/crates/assists/src/handlers/extract_struct_from_enum_variant.rs
@@ -2,12 +2,16 @@ use std::iter;
2 2
3use either::Either; 3use either::Either;
4use hir::{AsName, Module, ModuleDef, Name, Variant}; 4use hir::{AsName, Module, ModuleDef, Name, Variant};
5use ide_db::helpers::{ 5use ide_db::{
6 insert_use::{insert_use, ImportScope}, 6 defs::Definition,
7 mod_path_to_ast, 7 helpers::{
8 insert_use::{insert_use, ImportScope},
9 mod_path_to_ast,
10 },
11 search::FileReference,
12 RootDatabase,
8}; 13};
9use ide_db::{defs::Definition, search::Reference, RootDatabase}; 14use rustc_hash::FxHashSet;
10use rustc_hash::{FxHashMap, FxHashSet};
11use syntax::{ 15use syntax::{
12 algo::{find_node_at_offset, SyntaxRewriter}, 16 algo::{find_node_at_offset, SyntaxRewriter},
13 ast::{self, edit::IndentLevel, make, AstNode, NameOwner, VisibilityOwner}, 17 ast::{self, edit::IndentLevel, make, AstNode, NameOwner, VisibilityOwner},
@@ -58,29 +62,29 @@ pub(crate) fn extract_struct_from_enum_variant(
58 let mut visited_modules_set = FxHashSet::default(); 62 let mut visited_modules_set = FxHashSet::default();
59 let current_module = enum_hir.module(ctx.db()); 63 let current_module = enum_hir.module(ctx.db());
60 visited_modules_set.insert(current_module); 64 visited_modules_set.insert(current_module);
61 let mut rewriters = FxHashMap::default(); 65 let mut def_rewriter = None;
62 for reference in usages { 66 for (file_id, references) in usages {
63 let rewriter = rewriters 67 let mut rewriter = SyntaxRewriter::default();
64 .entry(reference.file_range.file_id) 68 let source_file = ctx.sema.parse(file_id);
65 .or_insert_with(SyntaxRewriter::default); 69 for reference in references {
66 let source_file = ctx.sema.parse(reference.file_range.file_id); 70 update_reference(
67 update_reference( 71 ctx,
68 ctx, 72 &mut rewriter,
69 rewriter, 73 reference,
70 reference, 74 &source_file,
71 &source_file, 75 &enum_module_def,
72 &enum_module_def, 76 &variant_hir_name,
73 &variant_hir_name, 77 &mut visited_modules_set,
74 &mut visited_modules_set, 78 );
75 ); 79 }
76 } 80 if file_id == ctx.frange.file_id {
77 let mut rewriter = 81 def_rewriter = Some(rewriter);
78 rewriters.remove(&ctx.frange.file_id).unwrap_or_else(SyntaxRewriter::default); 82 continue;
79 for (file_id, rewriter) in rewriters { 83 }
80 builder.edit_file(file_id); 84 builder.edit_file(file_id);
81 builder.rewrite(rewriter); 85 builder.rewrite(rewriter);
82 } 86 }
83 builder.edit_file(ctx.frange.file_id); 87 let mut rewriter = def_rewriter.unwrap_or_default();
84 update_variant(&mut rewriter, &variant); 88 update_variant(&mut rewriter, &variant);
85 extract_struct_def( 89 extract_struct_def(
86 &mut rewriter, 90 &mut rewriter,
@@ -90,6 +94,7 @@ pub(crate) fn extract_struct_from_enum_variant(
90 &variant.parent_enum().syntax().clone().into(), 94 &variant.parent_enum().syntax().clone().into(),
91 enum_ast.visibility(), 95 enum_ast.visibility(),
92 ); 96 );
97 builder.edit_file(ctx.frange.file_id);
93 builder.rewrite(rewriter); 98 builder.rewrite(rewriter);
94 }, 99 },
95 ) 100 )
@@ -205,13 +210,13 @@ fn update_variant(rewriter: &mut SyntaxRewriter, variant: &ast::Variant) -> Opti
205fn update_reference( 210fn update_reference(
206 ctx: &AssistContext, 211 ctx: &AssistContext,
207 rewriter: &mut SyntaxRewriter, 212 rewriter: &mut SyntaxRewriter,
208 reference: Reference, 213 reference: FileReference,
209 source_file: &SourceFile, 214 source_file: &SourceFile,
210 enum_module_def: &ModuleDef, 215 enum_module_def: &ModuleDef,
211 variant_hir_name: &Name, 216 variant_hir_name: &Name,
212 visited_modules_set: &mut FxHashSet<Module>, 217 visited_modules_set: &mut FxHashSet<Module>,
213) -> Option<()> { 218) -> Option<()> {
214 let offset = reference.file_range.range.start(); 219 let offset = reference.range.start();
215 let (segment, expr) = if let Some(path_expr) = 220 let (segment, expr) = if let Some(path_expr) =
216 find_node_at_offset::<ast::PathExpr>(source_file.syntax(), offset) 221 find_node_at_offset::<ast::PathExpr>(source_file.syntax(), offset)
217 { 222 {
diff --git a/crates/assists/src/handlers/inline_local_variable.rs b/crates/assists/src/handlers/inline_local_variable.rs
index d559be9cb..dc798daaa 100644
--- a/crates/assists/src/handlers/inline_local_variable.rs
+++ b/crates/assists/src/handlers/inline_local_variable.rs
@@ -1,4 +1,7 @@
1use ide_db::{defs::Definition, search::ReferenceKind}; 1use ide_db::{
2 defs::Definition,
3 search::{FileReference, ReferenceKind},
4};
2use syntax::{ 5use syntax::{
3 ast::{self, AstNode, AstToken}, 6 ast::{self, AstNode, AstToken},
4 TextRange, 7 TextRange,
@@ -44,8 +47,8 @@ pub(crate) fn inline_local_variable(acc: &mut Assists, ctx: &AssistContext) -> O
44 47
45 let def = ctx.sema.to_def(&bind_pat)?; 48 let def = ctx.sema.to_def(&bind_pat)?;
46 let def = Definition::Local(def); 49 let def = Definition::Local(def);
47 let refs = def.usages(&ctx.sema).all(); 50 let usages = def.usages(&ctx.sema).all();
48 if refs.is_empty() { 51 if usages.is_empty() {
49 mark::hit!(test_not_applicable_if_variable_unused); 52 mark::hit!(test_not_applicable_if_variable_unused);
50 return None; 53 return None;
51 }; 54 };
@@ -63,48 +66,45 @@ pub(crate) fn inline_local_variable(acc: &mut Assists, ctx: &AssistContext) -> O
63 let_stmt.syntax().text_range() 66 let_stmt.syntax().text_range()
64 }; 67 };
65 68
66 let mut wrap_in_parens = vec![true; refs.len()]; 69 let wrap_in_parens = usages
67 70 .references
68 for (i, desc) in refs.iter().enumerate() { 71 .values()
69 let usage_node = ctx 72 .flatten()
70 .covering_node_for_range(desc.file_range.range) 73 .map(|&FileReference { range, .. }| {
71 .ancestors() 74 let usage_node =
72 .find_map(ast::PathExpr::cast)?; 75 ctx.covering_node_for_range(range).ancestors().find_map(ast::PathExpr::cast)?;
73 let usage_parent_option = usage_node.syntax().parent().and_then(ast::Expr::cast); 76 let usage_parent_option = usage_node.syntax().parent().and_then(ast::Expr::cast);
74 let usage_parent = match usage_parent_option { 77 let usage_parent = match usage_parent_option {
75 Some(u) => u, 78 Some(u) => u,
76 None => { 79 None => return Ok(false),
77 wrap_in_parens[i] = false; 80 };
78 continue; 81
79 } 82 Ok(!matches!((&initializer_expr, usage_parent),
80 }; 83 (ast::Expr::CallExpr(_), _)
81 84 | (ast::Expr::IndexExpr(_), _)
82 wrap_in_parens[i] = match (&initializer_expr, usage_parent) { 85 | (ast::Expr::MethodCallExpr(_), _)
83 (ast::Expr::CallExpr(_), _) 86 | (ast::Expr::FieldExpr(_), _)
84 | (ast::Expr::IndexExpr(_), _) 87 | (ast::Expr::TryExpr(_), _)
85 | (ast::Expr::MethodCallExpr(_), _) 88 | (ast::Expr::RefExpr(_), _)
86 | (ast::Expr::FieldExpr(_), _) 89 | (ast::Expr::Literal(_), _)
87 | (ast::Expr::TryExpr(_), _) 90 | (ast::Expr::TupleExpr(_), _)
88 | (ast::Expr::RefExpr(_), _) 91 | (ast::Expr::ArrayExpr(_), _)
89 | (ast::Expr::Literal(_), _) 92 | (ast::Expr::ParenExpr(_), _)
90 | (ast::Expr::TupleExpr(_), _) 93 | (ast::Expr::PathExpr(_), _)
91 | (ast::Expr::ArrayExpr(_), _) 94 | (ast::Expr::BlockExpr(_), _)
92 | (ast::Expr::ParenExpr(_), _) 95 | (ast::Expr::EffectExpr(_), _)
93 | (ast::Expr::PathExpr(_), _) 96 | (_, ast::Expr::CallExpr(_))
94 | (ast::Expr::BlockExpr(_), _) 97 | (_, ast::Expr::TupleExpr(_))
95 | (ast::Expr::EffectExpr(_), _) 98 | (_, ast::Expr::ArrayExpr(_))
96 | (_, ast::Expr::CallExpr(_)) 99 | (_, ast::Expr::ParenExpr(_))
97 | (_, ast::Expr::TupleExpr(_)) 100 | (_, ast::Expr::ForExpr(_))
98 | (_, ast::Expr::ArrayExpr(_)) 101 | (_, ast::Expr::WhileExpr(_))
99 | (_, ast::Expr::ParenExpr(_)) 102 | (_, ast::Expr::BreakExpr(_))
100 | (_, ast::Expr::ForExpr(_)) 103 | (_, ast::Expr::ReturnExpr(_))
101 | (_, ast::Expr::WhileExpr(_)) 104 | (_, ast::Expr::MatchExpr(_))
102 | (_, ast::Expr::BreakExpr(_)) 105 ))
103 | (_, ast::Expr::ReturnExpr(_)) 106 })
104 | (_, ast::Expr::MatchExpr(_)) => false, 107 .collect::<Result<Vec<_>, _>>()?;
105 _ => true,
106 };
107 }
108 108
109 let init_str = initializer_expr.syntax().text().to_string(); 109 let init_str = initializer_expr.syntax().text().to_string();
110 let init_in_paren = format!("({})", &init_str); 110 let init_in_paren = format!("({})", &init_str);
@@ -116,15 +116,16 @@ pub(crate) fn inline_local_variable(acc: &mut Assists, ctx: &AssistContext) -> O
116 target, 116 target,
117 move |builder| { 117 move |builder| {
118 builder.delete(delete_range); 118 builder.delete(delete_range);
119 for (desc, should_wrap) in refs.iter().zip(wrap_in_parens) { 119 for (reference, should_wrap) in usages.references.values().flatten().zip(wrap_in_parens)
120 {
120 let replacement = 121 let replacement =
121 if should_wrap { init_in_paren.clone() } else { init_str.clone() }; 122 if should_wrap { init_in_paren.clone() } else { init_str.clone() };
122 match desc.kind { 123 match reference.kind {
123 ReferenceKind::FieldShorthandForLocal => { 124 ReferenceKind::FieldShorthandForLocal => {
124 mark::hit!(inline_field_shorthand); 125 mark::hit!(inline_field_shorthand);
125 builder.insert(desc.file_range.range.end(), format!(": {}", replacement)) 126 builder.insert(reference.range.end(), format!(": {}", replacement))
126 } 127 }
127 _ => builder.replace(desc.file_range.range, replacement), 128 _ => builder.replace(reference.range, replacement),
128 } 129 }
129 } 130 }
130 }, 131 },
diff --git a/crates/assists/src/handlers/remove_dbg.rs b/crates/assists/src/handlers/remove_dbg.rs
index 0320c2f12..6114091f2 100644
--- a/crates/assists/src/handlers/remove_dbg.rs
+++ b/crates/assists/src/handlers/remove_dbg.rs
@@ -1,6 +1,6 @@
1use syntax::{ 1use syntax::{
2 ast::{self, AstNode}, 2 ast::{self, AstNode},
3 match_ast, SyntaxElement, SyntaxKind, TextRange, TextSize, T, 3 match_ast, SyntaxElement, TextRange, TextSize, T,
4}; 4};
5 5
6use crate::{AssistContext, AssistId, AssistKind, Assists}; 6use crate::{AssistContext, AssistId, AssistKind, Assists};
@@ -136,14 +136,14 @@ fn needs_parentheses_around_macro_contents(macro_contents: Vec<SyntaxElement>) -
136 symbol_kind => { 136 symbol_kind => {
137 let symbol_not_in_bracket = unpaired_brackets_in_contents.is_empty(); 137 let symbol_not_in_bracket = unpaired_brackets_in_contents.is_empty();
138 if symbol_not_in_bracket 138 if symbol_not_in_bracket
139 && symbol_kind != SyntaxKind::COLON // paths 139 && symbol_kind != T![:] // paths
140 && (symbol_kind != SyntaxKind::DOT // field/method access 140 && (symbol_kind != T![.] // field/method access
141 || macro_contents // range expressions consist of two SyntaxKind::Dot in macro invocations 141 || macro_contents // range expressions consist of two SyntaxKind::Dot in macro invocations
142 .peek() 142 .peek()
143 .map(|element| element.kind() == SyntaxKind::DOT) 143 .map(|element| element.kind() == T![.])
144 .unwrap_or(false)) 144 .unwrap_or(false))
145 && symbol_kind != SyntaxKind::QUESTION // try operator 145 && symbol_kind != T![?] // try operator
146 && (symbol_kind.is_punct() || symbol_kind == SyntaxKind::AS_KW) 146 && (symbol_kind.is_punct() || symbol_kind == T![as])
147 { 147 {
148 return true; 148 return true;
149 } 149 }
diff --git a/crates/assists/src/handlers/remove_unused_param.rs b/crates/assists/src/handlers/remove_unused_param.rs
index 56e8b5229..c961680e2 100644
--- a/crates/assists/src/handlers/remove_unused_param.rs
+++ b/crates/assists/src/handlers/remove_unused_param.rs
@@ -1,8 +1,8 @@
1use ide_db::{defs::Definition, search::Reference}; 1use ide_db::{base_db::FileId, defs::Definition, search::FileReference};
2use syntax::{ 2use syntax::{
3 algo::find_node_at_range, 3 algo::find_node_at_range,
4 ast::{self, ArgListOwner}, 4 ast::{self, ArgListOwner},
5 AstNode, SyntaxKind, SyntaxNode, TextRange, T, 5 AstNode, SourceFile, SyntaxKind, SyntaxNode, TextRange, T,
6}; 6};
7use test_utils::mark; 7use test_utils::mark;
8use SyntaxKind::WHITESPACE; 8use SyntaxKind::WHITESPACE;
@@ -58,32 +58,41 @@ pub(crate) fn remove_unused_param(acc: &mut Assists, ctx: &AssistContext) -> Opt
58 param.syntax().text_range(), 58 param.syntax().text_range(),
59 |builder| { 59 |builder| {
60 builder.delete(range_to_remove(param.syntax())); 60 builder.delete(range_to_remove(param.syntax()));
61 for usage in fn_def.usages(&ctx.sema).all() { 61 for (file_id, references) in fn_def.usages(&ctx.sema).all() {
62 process_usage(ctx, builder, usage, param_position); 62 process_usages(ctx, builder, file_id, references, param_position);
63 } 63 }
64 }, 64 },
65 ) 65 )
66} 66}
67 67
68fn process_usage( 68fn process_usages(
69 ctx: &AssistContext, 69 ctx: &AssistContext,
70 builder: &mut AssistBuilder, 70 builder: &mut AssistBuilder,
71 usage: Reference, 71 file_id: FileId,
72 references: Vec<FileReference>,
72 arg_to_remove: usize, 73 arg_to_remove: usize,
73) -> Option<()> { 74) {
74 let source_file = ctx.sema.parse(usage.file_range.file_id); 75 let source_file = ctx.sema.parse(file_id);
75 let call_expr: ast::CallExpr = 76 builder.edit_file(file_id);
76 find_node_at_range(source_file.syntax(), usage.file_range.range)?; 77 for usage in references {
78 if let Some(text_range) = process_usage(&source_file, usage, arg_to_remove) {
79 builder.delete(text_range);
80 }
81 }
82}
83
84fn process_usage(
85 source_file: &SourceFile,
86 FileReference { range, .. }: FileReference,
87 arg_to_remove: usize,
88) -> Option<TextRange> {
89 let call_expr: ast::CallExpr = find_node_at_range(source_file.syntax(), range)?;
77 let call_expr_range = call_expr.expr()?.syntax().text_range(); 90 let call_expr_range = call_expr.expr()?.syntax().text_range();
78 if !call_expr_range.contains_range(usage.file_range.range) { 91 if !call_expr_range.contains_range(range) {
79 return None; 92 return None;
80 } 93 }
81 let arg = call_expr.arg_list()?.args().nth(arg_to_remove)?; 94 let arg = call_expr.arg_list()?.args().nth(arg_to_remove)?;
82 95 Some(range_to_remove(arg.syntax()))
83 builder.edit_file(usage.file_range.file_id);
84 builder.delete(range_to_remove(arg.syntax()));
85
86 Some(())
87} 96}
88 97
89fn range_to_remove(node: &SyntaxNode) -> TextRange { 98fn range_to_remove(node: &SyntaxNode) -> TextRange {
diff --git a/crates/assists/src/handlers/reorder_impl.rs b/crates/assists/src/handlers/reorder_impl.rs
new file mode 100644
index 000000000..309f291c8
--- /dev/null
+++ b/crates/assists/src/handlers/reorder_impl.rs
@@ -0,0 +1,201 @@
1use itertools::Itertools;
2use rustc_hash::FxHashMap;
3
4use hir::{PathResolution, Semantics};
5use ide_db::RootDatabase;
6use syntax::{
7 algo,
8 ast::{self, NameOwner},
9 AstNode,
10};
11use test_utils::mark;
12
13use crate::{AssistContext, AssistId, AssistKind, Assists};
14
15// Assist: reorder_impl
16//
17// Reorder the methods of an `impl Trait`. The methods will be ordered
18// in the same order as in the trait definition.
19//
20// ```
21// trait Foo {
22// fn a() {}
23// fn b() {}
24// fn c() {}
25// }
26//
27// struct Bar;
28// $0impl Foo for Bar {
29// fn b() {}
30// fn c() {}
31// fn a() {}
32// }
33// ```
34// ->
35// ```
36// trait Foo {
37// fn a() {}
38// fn b() {}
39// fn c() {}
40// }
41//
42// struct Bar;
43// impl Foo for Bar {
44// fn a() {}
45// fn b() {}
46// fn c() {}
47// }
48// ```
49//
50pub(crate) fn reorder_impl(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
51 let impl_ast = ctx.find_node_at_offset::<ast::Impl>()?;
52 let items = impl_ast.assoc_item_list()?;
53 let methods = get_methods(&items);
54
55 let path = impl_ast
56 .trait_()
57 .and_then(|t| match t {
58 ast::Type::PathType(path) => Some(path),
59 _ => None,
60 })?
61 .path()?;
62
63 let ranks = compute_method_ranks(&path, ctx)?;
64 let sorted: Vec<_> = methods
65 .iter()
66 .cloned()
67 .sorted_by_key(|f| {
68 f.name().and_then(|n| ranks.get(&n.to_string()).copied()).unwrap_or(usize::max_value())
69 })
70 .collect();
71
72 // Don't edit already sorted methods:
73 if methods == sorted {
74 mark::hit!(not_applicable_if_sorted);
75 return None;
76 }
77
78 let target = items.syntax().text_range();
79 acc.add(AssistId("reorder_impl", AssistKind::RefactorRewrite), "Sort methods", target, |edit| {
80 let mut rewriter = algo::SyntaxRewriter::default();
81 for (old, new) in methods.iter().zip(&sorted) {
82 rewriter.replace(old.syntax(), new.syntax());
83 }
84 edit.rewrite(rewriter);
85 })
86}
87
88fn compute_method_ranks(path: &ast::Path, ctx: &AssistContext) -> Option<FxHashMap<String, usize>> {
89 let td = trait_definition(path, &ctx.sema)?;
90
91 Some(
92 td.items(ctx.db())
93 .iter()
94 .flat_map(|i| match i {
95 hir::AssocItem::Function(f) => Some(f),
96 _ => None,
97 })
98 .enumerate()
99 .map(|(idx, func)| ((func.name(ctx.db()).to_string(), idx)))
100 .collect(),
101 )
102}
103
104fn trait_definition(path: &ast::Path, sema: &Semantics<RootDatabase>) -> Option<hir::Trait> {
105 match sema.resolve_path(path)? {
106 PathResolution::Def(hir::ModuleDef::Trait(trait_)) => Some(trait_),
107 _ => None,
108 }
109}
110
111fn get_methods(items: &ast::AssocItemList) -> Vec<ast::Fn> {
112 items
113 .assoc_items()
114 .flat_map(|i| match i {
115 ast::AssocItem::Fn(f) => Some(f),
116 _ => None,
117 })
118 .filter(|f| f.name().is_some())
119 .collect()
120}
121
122#[cfg(test)]
123mod tests {
124 use test_utils::mark;
125
126 use crate::tests::{check_assist, check_assist_not_applicable};
127
128 use super::*;
129
130 #[test]
131 fn not_applicable_if_sorted() {
132 mark::check!(not_applicable_if_sorted);
133 check_assist_not_applicable(
134 reorder_impl,
135 r#"
136trait Bar {
137 fn a() {}
138 fn z() {}
139 fn b() {}
140}
141struct Foo;
142$0impl Bar for Foo {
143 fn a() {}
144 fn z() {}
145 fn b() {}
146}
147 "#,
148 )
149 }
150
151 #[test]
152 fn not_applicable_if_empty() {
153 check_assist_not_applicable(
154 reorder_impl,
155 r#"
156trait Bar {};
157struct Foo;
158$0impl Bar for Foo {}
159 "#,
160 )
161 }
162
163 #[test]
164 fn reorder_impl_trait_methods() {
165 check_assist(
166 reorder_impl,
167 r#"
168trait Bar {
169 fn a() {}
170 fn c() {}
171 fn b() {}
172 fn d() {}
173}
174
175struct Foo;
176$0impl Bar for Foo {
177 fn d() {}
178 fn b() {}
179 fn c() {}
180 fn a() {}
181}
182 "#,
183 r#"
184trait Bar {
185 fn a() {}
186 fn c() {}
187 fn b() {}
188 fn d() {}
189}
190
191struct Foo;
192impl Bar for Foo {
193 fn a() {}
194 fn c() {}
195 fn b() {}
196 fn d() {}
197}
198 "#,
199 )
200 }
201}
diff --git a/crates/assists/src/lib.rs b/crates/assists/src/lib.rs
index 90009c55a..1080294ab 100644
--- a/crates/assists/src/lib.rs
+++ b/crates/assists/src/lib.rs
@@ -146,6 +146,7 @@ mod handlers {
146 mod remove_mut; 146 mod remove_mut;
147 mod remove_unused_param; 147 mod remove_unused_param;
148 mod reorder_fields; 148 mod reorder_fields;
149 mod reorder_impl;
149 mod replace_derive_with_manual_impl; 150 mod replace_derive_with_manual_impl;
150 mod replace_if_let_with_match; 151 mod replace_if_let_with_match;
151 mod replace_impl_trait_with_generic; 152 mod replace_impl_trait_with_generic;
@@ -202,6 +203,7 @@ mod handlers {
202 remove_mut::remove_mut, 203 remove_mut::remove_mut,
203 remove_unused_param::remove_unused_param, 204 remove_unused_param::remove_unused_param,
204 reorder_fields::reorder_fields, 205 reorder_fields::reorder_fields,
206 reorder_impl::reorder_impl,
205 replace_derive_with_manual_impl::replace_derive_with_manual_impl, 207 replace_derive_with_manual_impl::replace_derive_with_manual_impl,
206 replace_if_let_with_match::replace_if_let_with_match, 208 replace_if_let_with_match::replace_if_let_with_match,
207 replace_if_let_with_match::replace_match_with_if_let, 209 replace_if_let_with_match::replace_match_with_if_let,
diff --git a/crates/assists/src/tests/generated.rs b/crates/assists/src/tests/generated.rs
index e28837b53..217f577eb 100644
--- a/crates/assists/src/tests/generated.rs
+++ b/crates/assists/src/tests/generated.rs
@@ -896,6 +896,41 @@ const test: Foo = Foo {foo: 1, bar: 0}
896} 896}
897 897
898#[test] 898#[test]
899fn doctest_reorder_impl() {
900 check_doc_test(
901 "reorder_impl",
902 r#####"
903trait Foo {
904 fn a() {}
905 fn b() {}
906 fn c() {}
907}
908
909struct Bar;
910$0impl Foo for Bar {
911 fn b() {}
912 fn c() {}
913 fn a() {}
914}
915"#####,
916 r#####"
917trait Foo {
918 fn a() {}
919 fn b() {}
920 fn c() {}
921}
922
923struct Bar;
924impl Foo for Bar {
925 fn a() {}
926 fn b() {}
927 fn c() {}
928}
929"#####,
930 )
931}
932
933#[test]
899fn doctest_replace_derive_with_manual_impl() { 934fn doctest_replace_derive_with_manual_impl() {
900 check_doc_test( 935 check_doc_test(
901 "replace_derive_with_manual_impl", 936 "replace_derive_with_manual_impl",
diff --git a/crates/completion/src/completions/attribute.rs b/crates/completion/src/completions/attribute.rs
index 3a29b5203..e5522980d 100644
--- a/crates/completion/src/completions/attribute.rs
+++ b/crates/completion/src/completions/attribute.rs
@@ -5,7 +5,7 @@
5 5
6use itertools::Itertools; 6use itertools::Itertools;
7use rustc_hash::FxHashSet; 7use rustc_hash::FxHashSet;
8use syntax::{ast, AstNode, SyntaxKind}; 8use syntax::{ast, AstNode, T};
9 9
10use crate::{ 10use crate::{
11 context::CompletionContext, 11 context::CompletionContext,
@@ -21,15 +21,17 @@ pub(crate) fn complete_attribute(acc: &mut Completions, ctx: &CompletionContext)
21 21
22 let attribute = ctx.attribute_under_caret.as_ref()?; 22 let attribute = ctx.attribute_under_caret.as_ref()?;
23 match (attribute.path(), attribute.token_tree()) { 23 match (attribute.path(), attribute.token_tree()) {
24 (Some(path), Some(token_tree)) => match path.to_string().as_str() { 24 (Some(path), Some(token_tree)) => {
25 "derive" => complete_derive(acc, ctx, token_tree), 25 let path = path.syntax().text();
26 "feature" => complete_lint(acc, ctx, token_tree, FEATURES), 26 if path == "derive" {
27 "allow" | "warn" | "deny" | "forbid" => { 27 complete_derive(acc, ctx, token_tree)
28 } else if path == "feature" {
29 complete_lint(acc, ctx, token_tree, FEATURES)
30 } else if path == "allow" || path == "warn" || path == "deny" || path == "forbid" {
28 complete_lint(acc, ctx, token_tree.clone(), DEFAULT_LINT_COMPLETIONS); 31 complete_lint(acc, ctx, token_tree.clone(), DEFAULT_LINT_COMPLETIONS);
29 complete_lint(acc, ctx, token_tree, CLIPPY_LINTS); 32 complete_lint(acc, ctx, token_tree, CLIPPY_LINTS);
30 } 33 }
31 _ => {} 34 }
32 },
33 (_, Some(_token_tree)) => {} 35 (_, Some(_token_tree)) => {}
34 _ => complete_attribute_start(acc, ctx, attribute), 36 _ => complete_attribute_start(acc, ctx, attribute),
35 } 37 }
@@ -203,8 +205,7 @@ fn complete_lint(
203fn parse_comma_sep_input(derive_input: ast::TokenTree) -> Result<FxHashSet<String>, ()> { 205fn parse_comma_sep_input(derive_input: ast::TokenTree) -> Result<FxHashSet<String>, ()> {
204 match (derive_input.left_delimiter_token(), derive_input.right_delimiter_token()) { 206 match (derive_input.left_delimiter_token(), derive_input.right_delimiter_token()) {
205 (Some(left_paren), Some(right_paren)) 207 (Some(left_paren), Some(right_paren))
206 if left_paren.kind() == SyntaxKind::L_PAREN 208 if left_paren.kind() == T!['('] && right_paren.kind() == T![')'] =>
207 && right_paren.kind() == SyntaxKind::R_PAREN =>
208 { 209 {
209 let mut input_derives = FxHashSet::default(); 210 let mut input_derives = FxHashSet::default();
210 let mut current_derive = String::new(); 211 let mut current_derive = String::new();
@@ -216,7 +217,7 @@ fn parse_comma_sep_input(derive_input: ast::TokenTree) -> Result<FxHashSet<Strin
216 .skip(1) 217 .skip(1)
217 .take_while(|token| token != &right_paren) 218 .take_while(|token| token != &right_paren)
218 { 219 {
219 if SyntaxKind::COMMA == token.kind() { 220 if T![,] == token.kind() {
220 if !current_derive.is_empty() { 221 if !current_derive.is_empty() {
221 input_derives.insert(current_derive); 222 input_derives.insert(current_derive);
222 current_derive = String::new(); 223 current_derive = String::new();
diff --git a/crates/completion/src/completions/dot.rs b/crates/completion/src/completions/dot.rs
index 2e25c8ba2..d04eef65a 100644
--- a/crates/completion/src/completions/dot.rs
+++ b/crates/completion/src/completions/dot.rs
@@ -373,20 +373,20 @@ fn foo(a: A) {
373 fn macro_expansion_resilient() { 373 fn macro_expansion_resilient() {
374 check( 374 check(
375 r#" 375 r#"
376macro_rules! dbg { 376macro_rules! d {
377 () => {}; 377 () => {};
378 ($val:expr) => { 378 ($val:expr) => {
379 match $val { tmp => { tmp } } 379 match $val { tmp => { tmp } }
380 }; 380 };
381 // Trailing comma with single argument is ignored 381 // Trailing comma with single argument is ignored
382 ($val:expr,) => { $crate::dbg!($val) }; 382 ($val:expr,) => { $crate::d!($val) };
383 ($($val:expr),+ $(,)?) => { 383 ($($val:expr),+ $(,)?) => {
384 ($($crate::dbg!($val)),+,) 384 ($($crate::d!($val)),+,)
385 }; 385 };
386} 386}
387struct A { the_field: u32 } 387struct A { the_field: u32 }
388fn foo(a: A) { 388fn foo(a: A) {
389 dbg!(a.$0) 389 d!(a.$0)
390} 390}
391"#, 391"#,
392 expect![[r#" 392 expect![[r#"
diff --git a/crates/completion/src/completions/postfix.rs b/crates/completion/src/completions/postfix.rs
index 87f0c0b2a..9c34ed0b6 100644
--- a/crates/completion/src/completions/postfix.rs
+++ b/crates/completion/src/completions/postfix.rs
@@ -35,11 +35,14 @@ pub(crate) fn complete_postfix(acc: &mut Completions, ctx: &CompletionContext) {
35 None => return, 35 None => return,
36 }; 36 };
37 37
38 let ref_removed_ty =
39 std::iter::successors(Some(receiver_ty.clone()), |ty| ty.remove_ref()).last().unwrap();
40
38 let cap = match ctx.config.snippet_cap { 41 let cap = match ctx.config.snippet_cap {
39 Some(it) => it, 42 Some(it) => it,
40 None => return, 43 None => return,
41 }; 44 };
42 let try_enum = TryEnum::from_ty(&ctx.sema, &receiver_ty); 45 let try_enum = TryEnum::from_ty(&ctx.sema, &ref_removed_ty);
43 if let Some(try_enum) = &try_enum { 46 if let Some(try_enum) = &try_enum {
44 match try_enum { 47 match try_enum {
45 TryEnum::Result => { 48 TryEnum::Result => {
@@ -496,6 +499,27 @@ fn main() {
496 fn postfix_completion_for_references() { 499 fn postfix_completion_for_references() {
497 check_edit("dbg", r#"fn main() { &&42.$0 }"#, r#"fn main() { dbg!(&&42) }"#); 500 check_edit("dbg", r#"fn main() { &&42.$0 }"#, r#"fn main() { dbg!(&&42) }"#);
498 check_edit("refm", r#"fn main() { &&42.$0 }"#, r#"fn main() { &&&mut 42 }"#); 501 check_edit("refm", r#"fn main() { &&42.$0 }"#, r#"fn main() { &&&mut 42 }"#);
502 check_edit(
503 "ifl",
504 r#"
505enum Option<T> { Some(T), None }
506
507fn main() {
508 let bar = &Option::Some(true);
509 bar.$0
510}
511"#,
512 r#"
513enum Option<T> { Some(T), None }
514
515fn main() {
516 let bar = &Option::Some(true);
517 if let Some($1) = bar {
518 $0
519}
520}
521"#,
522 )
499 } 523 }
500 524
501 #[test] 525 #[test]
diff --git a/crates/completion/src/completions/trait_impl.rs b/crates/completion/src/completions/trait_impl.rs
index aa9c845da..135ae49dc 100644
--- a/crates/completion/src/completions/trait_impl.rs
+++ b/crates/completion/src/completions/trait_impl.rs
@@ -93,11 +93,11 @@ fn completion_match(ctx: &CompletionContext) -> Option<(ImplCompletionKind, Synt
93 // `impl .. { const $0 }` 93 // `impl .. { const $0 }`
94 // ERROR 0 94 // ERROR 0
95 // CONST_KW <- * 95 // CONST_KW <- *
96 SyntaxKind::CONST_KW => 0, 96 T![const] => 0,
97 // `impl .. { fn/type $0 }` 97 // `impl .. { fn/type $0 }`
98 // FN/TYPE_ALIAS 0 98 // FN/TYPE_ALIAS 0
99 // FN_KW <- * 99 // FN_KW <- *
100 SyntaxKind::FN_KW | SyntaxKind::TYPE_KW => 0, 100 T![fn] | T![type] => 0,
101 // `impl .. { fn/type/const foo$0 }` 101 // `impl .. { fn/type/const foo$0 }`
102 // FN/TYPE_ALIAS/CONST 1 102 // FN/TYPE_ALIAS/CONST 1
103 // NAME 0 103 // NAME 0
@@ -121,7 +121,7 @@ fn completion_match(ctx: &CompletionContext) -> Option<(ImplCompletionKind, Synt
121 let impl_def = ast::Impl::cast(impl_item.parent()?.parent()?)?; 121 let impl_def = ast::Impl::cast(impl_item.parent()?.parent()?)?;
122 let kind = match impl_item.kind() { 122 let kind = match impl_item.kind() {
123 // `impl ... { const $0 fn/type/const }` 123 // `impl ... { const $0 fn/type/const }`
124 _ if token.kind() == SyntaxKind::CONST_KW => ImplCompletionKind::Const, 124 _ if token.kind() == T![const] => ImplCompletionKind::Const,
125 SyntaxKind::CONST | SyntaxKind::ERROR => ImplCompletionKind::Const, 125 SyntaxKind::CONST | SyntaxKind::ERROR => ImplCompletionKind::Const,
126 SyntaxKind::TYPE_ALIAS => ImplCompletionKind::TypeAlias, 126 SyntaxKind::TYPE_ALIAS => ImplCompletionKind::TypeAlias,
127 SyntaxKind::FN => ImplCompletionKind::Fn, 127 SyntaxKind::FN => ImplCompletionKind::Fn,
diff --git a/crates/completion/src/patterns.rs b/crates/completion/src/patterns.rs
index f148b9402..f3ce91dd1 100644
--- a/crates/completion/src/patterns.rs
+++ b/crates/completion/src/patterns.rs
@@ -5,7 +5,7 @@ use syntax::{
5 ast::{self, LoopBodyOwner}, 5 ast::{self, LoopBodyOwner},
6 match_ast, AstNode, Direction, NodeOrToken, SyntaxElement, 6 match_ast, AstNode, Direction, NodeOrToken, SyntaxElement,
7 SyntaxKind::*, 7 SyntaxKind::*,
8 SyntaxNode, SyntaxToken, 8 SyntaxNode, SyntaxToken, T,
9}; 9};
10 10
11#[cfg(test)] 11#[cfg(test)]
@@ -119,7 +119,7 @@ pub(crate) fn unsafe_is_prev(element: SyntaxElement) -> bool {
119 element 119 element
120 .into_token() 120 .into_token()
121 .and_then(|it| previous_non_trivia_token(it)) 121 .and_then(|it| previous_non_trivia_token(it))
122 .filter(|it| it.kind() == UNSAFE_KW) 122 .filter(|it| it.kind() == T![unsafe])
123 .is_some() 123 .is_some()
124} 124}
125#[test] 125#[test]
@@ -131,7 +131,7 @@ pub(crate) fn if_is_prev(element: SyntaxElement) -> bool {
131 element 131 element
132 .into_token() 132 .into_token()
133 .and_then(|it| previous_non_trivia_token(it)) 133 .and_then(|it| previous_non_trivia_token(it))
134 .filter(|it| it.kind() == IF_KW) 134 .filter(|it| it.kind() == T![if])
135 .is_some() 135 .is_some()
136} 136}
137 137
@@ -139,7 +139,7 @@ pub(crate) fn fn_is_prev(element: SyntaxElement) -> bool {
139 element 139 element
140 .into_token() 140 .into_token()
141 .and_then(|it| previous_non_trivia_token(it)) 141 .and_then(|it| previous_non_trivia_token(it))
142 .filter(|it| it.kind() == FN_KW) 142 .filter(|it| it.kind() == T![fn])
143 .is_some() 143 .is_some()
144} 144}
145#[test] 145#[test]
@@ -154,7 +154,7 @@ pub(crate) fn for_is_prev2(element: SyntaxElement) -> bool {
154 .into_token() 154 .into_token()
155 .and_then(|it| previous_non_trivia_token(it)) 155 .and_then(|it| previous_non_trivia_token(it))
156 .and_then(|it| previous_non_trivia_token(it)) 156 .and_then(|it| previous_non_trivia_token(it))
157 .filter(|it| it.kind() == FOR_KW) 157 .filter(|it| it.kind() == T![for])
158 .is_some() 158 .is_some()
159} 159}
160#[test] 160#[test]
diff --git a/crates/flycheck/Cargo.toml b/crates/flycheck/Cargo.toml
index 1bad64a1b..1d19c7886 100644
--- a/crates/flycheck/Cargo.toml
+++ b/crates/flycheck/Cargo.toml
@@ -12,7 +12,7 @@ doctest = false
12[dependencies] 12[dependencies]
13crossbeam-channel = "0.5.0" 13crossbeam-channel = "0.5.0"
14log = "0.4.8" 14log = "0.4.8"
15cargo_metadata = "=0.12.0" 15cargo_metadata = "0.12.2"
16serde_json = "1.0.48" 16serde_json = "1.0.48"
17jod-thread = "0.1.1" 17jod-thread = "0.1.1"
18 18
diff --git a/crates/hir/src/code_model.rs b/crates/hir/src/code_model.rs
index cc1938333..6cbf5cecf 100644
--- a/crates/hir/src/code_model.rs
+++ b/crates/hir/src/code_model.rs
@@ -1263,6 +1263,24 @@ pub enum GenericParam {
1263} 1263}
1264impl_from!(TypeParam, LifetimeParam, ConstParam for GenericParam); 1264impl_from!(TypeParam, LifetimeParam, ConstParam for GenericParam);
1265 1265
1266impl GenericParam {
1267 pub fn module(self, db: &dyn HirDatabase) -> Module {
1268 match self {
1269 GenericParam::TypeParam(it) => it.module(db),
1270 GenericParam::LifetimeParam(it) => it.module(db),
1271 GenericParam::ConstParam(it) => it.module(db),
1272 }
1273 }
1274
1275 pub fn name(self, db: &dyn HirDatabase) -> Name {
1276 match self {
1277 GenericParam::TypeParam(it) => it.name(db),
1278 GenericParam::LifetimeParam(it) => it.name(db),
1279 GenericParam::ConstParam(it) => it.name(db),
1280 }
1281 }
1282}
1283
1266#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] 1284#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
1267pub struct TypeParam { 1285pub struct TypeParam {
1268 pub(crate) id: TypeParamId, 1286 pub(crate) id: TypeParamId,
diff --git a/crates/hir/src/from_id.rs b/crates/hir/src/from_id.rs
index 3e47a5e9d..c8c5fecd7 100644
--- a/crates/hir/src/from_id.rs
+++ b/crates/hir/src/from_id.rs
@@ -1,6 +1,6 @@
1//! Utility module for converting between hir_def ids and code_model wrappers. 1//! Utility module for converting between hir_def ids and code_model wrappers.
2//! 2//!
3//! It's unclear if we need this long-term, but it's definitelly useful while we 3//! It's unclear if we need this long-term, but it's definitely useful while we
4//! are splitting the hir. 4//! are splitting the hir.
5 5
6use hir_def::{ 6use hir_def::{
diff --git a/crates/hir_def/src/body/lower.rs b/crates/hir_def/src/body/lower.rs
index 6be1eaade..3dc33f248 100644
--- a/crates/hir_def/src/body/lower.rs
+++ b/crates/hir_def/src/body/lower.rs
@@ -581,7 +581,7 @@ impl ExprCollector<'_> {
581 match res.value { 581 match res.value {
582 Some((mark, expansion)) => { 582 Some((mark, expansion)) => {
583 // FIXME: Statements are too complicated to recover from error for now. 583 // FIXME: Statements are too complicated to recover from error for now.
584 // It is because we don't have any hygenine for local variable expansion right now. 584 // It is because we don't have any hygiene for local variable expansion right now.
585 if T::can_cast(syntax::SyntaxKind::MACRO_STMTS) && res.err.is_some() { 585 if T::can_cast(syntax::SyntaxKind::MACRO_STMTS) && res.err.is_some() {
586 self.expander.exit(self.db, mark); 586 self.expander.exit(self.db, mark);
587 collector(self, None); 587 collector(self, None);
@@ -959,7 +959,7 @@ impl ExprCollector<'_> {
959 959
960 fn collect_tuple_pat(&mut self, args: AstChildren<ast::Pat>) -> (Vec<PatId>, Option<usize>) { 960 fn collect_tuple_pat(&mut self, args: AstChildren<ast::Pat>) -> (Vec<PatId>, Option<usize>) {
961 // Find the location of the `..`, if there is one. Note that we do not 961 // Find the location of the `..`, if there is one. Note that we do not
962 // consider the possiblity of there being multiple `..` here. 962 // consider the possibility of there being multiple `..` here.
963 let ellipsis = args.clone().position(|p| matches!(p, ast::Pat::RestPat(_))); 963 let ellipsis = args.clone().position(|p| matches!(p, ast::Pat::RestPat(_)));
964 // We want to skip the `..` pattern here, since we account for it above. 964 // We want to skip the `..` pattern here, since we account for it above.
965 let args = args 965 let args = args
diff --git a/crates/hir_def/src/expr.rs b/crates/hir_def/src/expr.rs
index 6a481769d..76f5721e5 100644
--- a/crates/hir_def/src/expr.rs
+++ b/crates/hir_def/src/expr.rs
@@ -1,6 +1,6 @@
1//! This module describes hir-level representation of expressions. 1//! This module describes hir-level representation of expressions.
2//! 2//!
3//! This representaion is: 3//! This representation is:
4//! 4//!
5//! 1. Identity-based. Each expression has an `id`, so we can distinguish 5//! 1. Identity-based. Each expression has an `id`, so we can distinguish
6//! between different `1` in `1 + 1`. 6//! between different `1` in `1 + 1`.
diff --git a/crates/hir_def/src/nameres/collector.rs b/crates/hir_def/src/nameres/collector.rs
index 77017e4ea..f027fd48d 100644
--- a/crates/hir_def/src/nameres/collector.rs
+++ b/crates/hir_def/src/nameres/collector.rs
@@ -267,7 +267,7 @@ impl DefCollector<'_> {
267 267
268 // Resolve all indeterminate resolved imports again 268 // Resolve all indeterminate resolved imports again
269 // As some of the macros will expand newly import shadowing partial resolved imports 269 // As some of the macros will expand newly import shadowing partial resolved imports
270 // FIXME: We maybe could skip this, if we handle the Indetermine imports in `resolve_imports` 270 // FIXME: We maybe could skip this, if we handle the indeterminate imports in `resolve_imports`
271 // correctly 271 // correctly
272 let partial_resolved = self.resolved_imports.iter().filter_map(|directive| { 272 let partial_resolved = self.resolved_imports.iter().filter_map(|directive| {
273 if let PartialResolvedImport::Indeterminate(_) = directive.status { 273 if let PartialResolvedImport::Indeterminate(_) = directive.status {
@@ -402,7 +402,7 @@ impl DefCollector<'_> {
402 402
403 /// Define a proc macro 403 /// Define a proc macro
404 /// 404 ///
405 /// A proc macro is similar to normal macro scope, but it would not visiable in legacy textual scoped. 405 /// A proc macro is similar to normal macro scope, but it would not visible in legacy textual scoped.
406 /// And unconditionally exported. 406 /// And unconditionally exported.
407 fn define_proc_macro(&mut self, name: Name, macro_: MacroDefId) { 407 fn define_proc_macro(&mut self, name: Name, macro_: MacroDefId) {
408 self.update( 408 self.update(
@@ -592,7 +592,7 @@ impl DefCollector<'_> {
592 // XXX: urgh, so this works by accident! Here, we look at 592 // XXX: urgh, so this works by accident! Here, we look at
593 // the enum data, and, in theory, this might require us to 593 // the enum data, and, in theory, this might require us to
594 // look back at the crate_def_map, creating a cycle. For 594 // look back at the crate_def_map, creating a cycle. For
595 // example, `enum E { crate::some_macro!(); }`. Luckely, the 595 // example, `enum E { crate::some_macro!(); }`. Luckily, the
596 // only kind of macro that is allowed inside enum is a 596 // only kind of macro that is allowed inside enum is a
597 // `cfg_macro`, and we don't need to run name resolution for 597 // `cfg_macro`, and we don't need to run name resolution for
598 // it, but this is sheer luck! 598 // it, but this is sheer luck!
@@ -655,7 +655,7 @@ impl DefCollector<'_> {
655 &mut self, 655 &mut self,
656 module_id: LocalModuleId, 656 module_id: LocalModuleId,
657 resolutions: &[(Option<Name>, PerNs)], 657 resolutions: &[(Option<Name>, PerNs)],
658 // All resolutions are imported with this visibility; the visibilies in 658 // All resolutions are imported with this visibility; the visibilities in
659 // the `PerNs` values are ignored and overwritten 659 // the `PerNs` values are ignored and overwritten
660 vis: Visibility, 660 vis: Visibility,
661 import_type: ImportType, 661 import_type: ImportType,
diff --git a/crates/hir_def/src/per_ns.rs b/crates/hir_def/src/per_ns.rs
index 74665c588..a594afce6 100644
--- a/crates/hir_def/src/per_ns.rs
+++ b/crates/hir_def/src/per_ns.rs
@@ -7,7 +7,7 @@ use hir_expand::MacroDefId;
7 7
8use crate::{item_scope::ItemInNs, visibility::Visibility, ModuleDefId}; 8use crate::{item_scope::ItemInNs, visibility::Visibility, ModuleDefId};
9 9
10#[derive(Debug, Copy, Clone, PartialEq, Eq)] 10#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
11pub struct PerNs { 11pub struct PerNs {
12 pub types: Option<(ModuleDefId, Visibility)>, 12 pub types: Option<(ModuleDefId, Visibility)>,
13 pub values: Option<(ModuleDefId, Visibility)>, 13 pub values: Option<(ModuleDefId, Visibility)>,
diff --git a/crates/hir_def/src/resolver.rs b/crates/hir_def/src/resolver.rs
index 129f1dbac..61059c349 100644
--- a/crates/hir_def/src/resolver.rs
+++ b/crates/hir_def/src/resolver.rs
@@ -27,7 +27,7 @@ use crate::{
27 27
28#[derive(Debug, Clone, Default)] 28#[derive(Debug, Clone, Default)]
29pub struct Resolver { 29pub struct Resolver {
30 // FIXME: all usages generally call `.rev`, so maybe reverse once in consturciton? 30 // FIXME: all usages generally call `.rev`, so maybe reverse once in construction?
31 scopes: Vec<Scope>, 31 scopes: Vec<Scope>,
32} 32}
33 33
@@ -490,6 +490,7 @@ pub enum ScopeDef {
490 490
491impl Scope { 491impl Scope {
492 fn process_names(&self, db: &dyn DefDatabase, f: &mut dyn FnMut(Name, ScopeDef)) { 492 fn process_names(&self, db: &dyn DefDatabase, f: &mut dyn FnMut(Name, ScopeDef)) {
493 let mut seen = FxHashSet::default();
493 match self { 494 match self {
494 Scope::ModuleScope(m) => { 495 Scope::ModuleScope(m) => {
495 // FIXME: should we provide `self` here? 496 // FIXME: should we provide `self` here?
@@ -503,7 +504,9 @@ impl Scope {
503 f(name.clone(), ScopeDef::PerNs(def)); 504 f(name.clone(), ScopeDef::PerNs(def));
504 }); 505 });
505 m.crate_def_map[m.module_id].scope.legacy_macros().for_each(|(name, macro_)| { 506 m.crate_def_map[m.module_id].scope.legacy_macros().for_each(|(name, macro_)| {
506 f(name.clone(), ScopeDef::PerNs(PerNs::macros(macro_, Visibility::Public))); 507 let scope = PerNs::macros(macro_, Visibility::Public);
508 seen.insert((name.clone(), scope));
509 f(name.clone(), ScopeDef::PerNs(scope));
507 }); 510 });
508 m.crate_def_map.extern_prelude.iter().for_each(|(name, &def)| { 511 m.crate_def_map.extern_prelude.iter().for_each(|(name, &def)| {
509 f(name.clone(), ScopeDef::PerNs(PerNs::types(def, Visibility::Public))); 512 f(name.clone(), ScopeDef::PerNs(PerNs::types(def, Visibility::Public)));
@@ -514,7 +517,10 @@ impl Scope {
514 if let Some(prelude) = m.crate_def_map.prelude { 517 if let Some(prelude) = m.crate_def_map.prelude {
515 let prelude_def_map = db.crate_def_map(prelude.krate); 518 let prelude_def_map = db.crate_def_map(prelude.krate);
516 prelude_def_map[prelude.local_id].scope.entries().for_each(|(name, def)| { 519 prelude_def_map[prelude.local_id].scope.entries().for_each(|(name, def)| {
517 f(name.clone(), ScopeDef::PerNs(def)); 520 let seen_tuple = (name.clone(), def);
521 if !seen.contains(&seen_tuple) {
522 f(seen_tuple.0, ScopeDef::PerNs(def));
523 }
518 }); 524 });
519 } 525 }
520 } 526 }
diff --git a/crates/hir_def/src/visibility.rs b/crates/hir_def/src/visibility.rs
index e6e0853a3..f3bc9d680 100644
--- a/crates/hir_def/src/visibility.rs
+++ b/crates/hir_def/src/visibility.rs
@@ -85,7 +85,7 @@ impl RawVisibility {
85} 85}
86 86
87/// Visibility of an item, with the path resolved. 87/// Visibility of an item, with the path resolved.
88#[derive(Debug, Copy, Clone, PartialEq, Eq)] 88#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
89pub enum Visibility { 89pub enum Visibility {
90 /// Visibility is restricted to a certain module. 90 /// Visibility is restricted to a certain module.
91 Module(ModuleId), 91 Module(ModuleId),
diff --git a/crates/hir_expand/src/db.rs b/crates/hir_expand/src/db.rs
index ab2637b8c..c62086390 100644
--- a/crates/hir_expand/src/db.rs
+++ b/crates/hir_expand/src/db.rs
@@ -40,7 +40,7 @@ impl TokenExpander {
40 // FIXME switch these to ExpandResult as well 40 // FIXME switch these to ExpandResult as well
41 TokenExpander::BuiltinDerive(it) => it.expand(db, id, tt).into(), 41 TokenExpander::BuiltinDerive(it) => it.expand(db, id, tt).into(),
42 TokenExpander::ProcMacro(_) => { 42 TokenExpander::ProcMacro(_) => {
43 // We store the result in salsa db to prevent non-determinisc behavior in 43 // We store the result in salsa db to prevent non-deterministic behavior in
44 // some proc-macro implementation 44 // some proc-macro implementation
45 // See #4315 for details 45 // See #4315 for details
46 db.expand_proc_macro(id.into()).into() 46 db.expand_proc_macro(id.into()).into()
diff --git a/crates/hir_ty/src/diagnostics/expr.rs b/crates/hir_ty/src/diagnostics/expr.rs
index a1c484fdf..107417c27 100644
--- a/crates/hir_ty/src/diagnostics/expr.rs
+++ b/crates/hir_ty/src/diagnostics/expr.rs
@@ -379,7 +379,7 @@ pub fn record_literal_missing_fields(
379 id: ExprId, 379 id: ExprId,
380 expr: &Expr, 380 expr: &Expr,
381) -> Option<(VariantId, Vec<LocalFieldId>, /*exhaustive*/ bool)> { 381) -> Option<(VariantId, Vec<LocalFieldId>, /*exhaustive*/ bool)> {
382 let (fields, exhausitve) = match expr { 382 let (fields, exhaustive) = match expr {
383 Expr::RecordLit { path: _, fields, spread } => (fields, spread.is_none()), 383 Expr::RecordLit { path: _, fields, spread } => (fields, spread.is_none()),
384 _ => return None, 384 _ => return None,
385 }; 385 };
@@ -400,7 +400,7 @@ pub fn record_literal_missing_fields(
400 if missed_fields.is_empty() { 400 if missed_fields.is_empty() {
401 return None; 401 return None;
402 } 402 }
403 Some((variant_def, missed_fields, exhausitve)) 403 Some((variant_def, missed_fields, exhaustive))
404} 404}
405 405
406pub fn record_pattern_missing_fields( 406pub fn record_pattern_missing_fields(
diff --git a/crates/hir_ty/src/diagnostics/match_check.rs b/crates/hir_ty/src/diagnostics/match_check.rs
index 62c329731..61c47eec8 100644
--- a/crates/hir_ty/src/diagnostics/match_check.rs
+++ b/crates/hir_ty/src/diagnostics/match_check.rs
@@ -14,7 +14,7 @@
14//! The algorithm implemented here is a modified version of the one described in 14//! The algorithm implemented here is a modified version of the one described in
15//! <http://moscova.inria.fr/~maranget/papers/warn/index.html>. 15//! <http://moscova.inria.fr/~maranget/papers/warn/index.html>.
16//! However, to save future implementors from reading the original paper, we 16//! However, to save future implementors from reading the original paper, we
17//! summarise the algorithm here to hopefully save time and be a little clearer 17//! summarize the algorithm here to hopefully save time and be a little clearer
18//! (without being so rigorous). 18//! (without being so rigorous).
19//! 19//!
20//! The core of the algorithm revolves about a "usefulness" check. In particular, we 20//! The core of the algorithm revolves about a "usefulness" check. In particular, we
@@ -132,7 +132,7 @@
132//! The algorithm is inductive (on the number of columns: i.e., components of tuple patterns). 132//! The algorithm is inductive (on the number of columns: i.e., components of tuple patterns).
133//! That means we're going to check the components from left-to-right, so the algorithm 133//! That means we're going to check the components from left-to-right, so the algorithm
134//! operates principally on the first component of the matrix and new pattern-stack `p`. 134//! operates principally on the first component of the matrix and new pattern-stack `p`.
135//! This algorithm is realised in the `is_useful` function. 135//! This algorithm is realized in the `is_useful` function.
136//! 136//!
137//! Base case (`n = 0`, i.e., an empty tuple pattern): 137//! Base case (`n = 0`, i.e., an empty tuple pattern):
138//! - If `P` already contains an empty pattern (i.e., if the number of patterns `m > 0`), then 138//! - If `P` already contains an empty pattern (i.e., if the number of patterns `m > 0`), then
diff --git a/crates/hir_ty/src/display.rs b/crates/hir_ty/src/display.rs
index e9e949c47..d2f1b4014 100644
--- a/crates/hir_ty/src/display.rs
+++ b/crates/hir_ty/src/display.rs
@@ -1,14 +1,15 @@
1//! FIXME: write short doc here 1//! FIXME: write short doc here
2 2
3use std::fmt; 3use std::{borrow::Cow, fmt};
4 4
5use crate::{ 5use crate::{
6 db::HirDatabase, utils::generics, ApplicationTy, CallableDefId, FnSig, GenericPredicate, 6 db::HirDatabase, utils::generics, ApplicationTy, CallableDefId, FnSig, GenericPredicate,
7 Lifetime, Obligation, OpaqueTyId, ProjectionTy, Substs, TraitRef, Ty, TypeCtor, 7 Lifetime, Obligation, OpaqueTy, OpaqueTyId, ProjectionTy, Substs, TraitRef, Ty, TypeCtor,
8}; 8};
9use arrayvec::ArrayVec;
9use hir_def::{ 10use hir_def::{
10 find_path, generics::TypeParamProvenance, item_scope::ItemInNs, AdtId, AssocContainerId, 11 db::DefDatabase, find_path, generics::TypeParamProvenance, item_scope::ItemInNs, AdtId,
11 Lookup, ModuleId, 12 AssocContainerId, HasModule, Lookup, ModuleId, TraitId,
12}; 13};
13use hir_expand::name::Name; 14use hir_expand::name::Name;
14 15
@@ -257,25 +258,45 @@ impl HirDisplay for ApplicationTy {
257 t.hir_fmt(f)?; 258 t.hir_fmt(f)?;
258 write!(f, "; _]")?; 259 write!(f, "; _]")?;
259 } 260 }
260 TypeCtor::RawPtr(m) => { 261 TypeCtor::RawPtr(m) | TypeCtor::Ref(m) => {
261 let t = self.parameters.as_single(); 262 let t = self.parameters.as_single();
263 let ty_display =
264 t.into_displayable(f.db, f.max_size, f.omit_verbose_types, f.display_target);
262 265
263 write!(f, "*{}", m.as_keyword_for_ptr())?; 266 if matches!(self.ctor, TypeCtor::RawPtr(_)) {
264 if matches!(t, Ty::Dyn(predicates) if predicates.len() > 1) { 267 write!(f, "*{}", m.as_keyword_for_ptr())?;
265 write!(f, "(")?;
266 t.hir_fmt(f)?;
267 write!(f, ")")?;
268 } else { 268 } else {
269 t.hir_fmt(f)?; 269 write!(f, "&{}", m.as_keyword_for_ref())?;
270 }
271
272 let datas;
273 let predicates = match t {
274 Ty::Dyn(predicates) if predicates.len() > 1 => {
275 Cow::Borrowed(predicates.as_ref())
276 }
277 &Ty::Opaque(OpaqueTy {
278 opaque_ty_id: OpaqueTyId::ReturnTypeImplTrait(func, idx),
279 ref parameters,
280 }) => {
281 datas =
282 f.db.return_type_impl_traits(func).expect("impl trait id without data");
283 let data = (*datas)
284 .as_ref()
285 .map(|rpit| rpit.impl_traits[idx as usize].bounds.clone());
286 let bounds = data.subst(parameters);
287 Cow::Owned(bounds.value)
288 }
289 _ => Cow::Borrowed(&[][..]),
290 };
291
292 if let [GenericPredicate::Implemented(trait_ref), _] = predicates.as_ref() {
293 let trait_ = trait_ref.trait_;
294 if fn_traits(f.db.upcast(), trait_).any(|it| it == trait_) {
295 return write!(f, "{}", ty_display);
296 }
270 } 297 }
271 }
272 TypeCtor::Ref(m) => {
273 let t = self.parameters.as_single();
274 let ty_display =
275 t.into_displayable(f.db, f.max_size, f.omit_verbose_types, f.display_target);
276 298
277 write!(f, "&{}", m.as_keyword_for_ref())?; 299 if predicates.len() > 1 {
278 if matches!(t, Ty::Dyn(predicates) if predicates.len() > 1) {
279 write!(f, "(")?; 300 write!(f, "(")?;
280 write!(f, "{}", ty_display)?; 301 write!(f, "{}", ty_display)?;
281 write!(f, ")")?; 302 write!(f, ")")?;
@@ -595,6 +616,17 @@ impl HirDisplay for FnSig {
595 } 616 }
596} 617}
597 618
619fn fn_traits(db: &dyn DefDatabase, trait_: TraitId) -> impl Iterator<Item = TraitId> {
620 let krate = trait_.lookup(db).container.module(db).krate;
621 let fn_traits = [
622 db.lang_item(krate, "fn".into()),
623 db.lang_item(krate, "fn_mut".into()),
624 db.lang_item(krate, "fn_once".into()),
625 ];
626 // FIXME: Replace ArrayVec when into_iter is a thing on arrays
627 ArrayVec::from(fn_traits).into_iter().flatten().flat_map(|it| it.as_trait())
628}
629
598pub fn write_bounds_like_dyn_trait( 630pub fn write_bounds_like_dyn_trait(
599 predicates: &[GenericPredicate], 631 predicates: &[GenericPredicate],
600 f: &mut HirFormatter, 632 f: &mut HirFormatter,
@@ -607,10 +639,15 @@ pub fn write_bounds_like_dyn_trait(
607 // predicate for that trait). 639 // predicate for that trait).
608 let mut first = true; 640 let mut first = true;
609 let mut angle_open = false; 641 let mut angle_open = false;
642 let mut is_fn_trait = false;
610 for p in predicates.iter() { 643 for p in predicates.iter() {
611 match p { 644 match p {
612 GenericPredicate::Implemented(trait_ref) => { 645 GenericPredicate::Implemented(trait_ref) => {
613 if angle_open { 646 let trait_ = trait_ref.trait_;
647 if !is_fn_trait {
648 is_fn_trait = fn_traits(f.db.upcast(), trait_).any(|it| it == trait_);
649 }
650 if !is_fn_trait && angle_open {
614 write!(f, ">")?; 651 write!(f, ">")?;
615 angle_open = false; 652 angle_open = false;
616 } 653 }
@@ -620,14 +657,27 @@ pub fn write_bounds_like_dyn_trait(
620 // We assume that the self type is $0 (i.e. the 657 // We assume that the self type is $0 (i.e. the
621 // existential) here, which is the only thing that's 658 // existential) here, which is the only thing that's
622 // possible in actual Rust, and hence don't print it 659 // possible in actual Rust, and hence don't print it
623 write!(f, "{}", f.db.trait_data(trait_ref.trait_).name)?; 660 write!(f, "{}", f.db.trait_data(trait_).name)?;
624 if trait_ref.substs.len() > 1 { 661 if let [_, params @ ..] = &*trait_ref.substs.0 {
625 write!(f, "<")?; 662 if is_fn_trait {
626 f.write_joined(&trait_ref.substs[1..], ", ")?; 663 if let Some(args) = params.first().and_then(|it| it.as_tuple()) {
627 // there might be assoc type bindings, so we leave the angle brackets open 664 write!(f, "(")?;
628 angle_open = true; 665 f.write_joined(&*args.0, ", ")?;
666 write!(f, ")")?;
667 }
668 } else if !params.is_empty() {
669 write!(f, "<")?;
670 f.write_joined(params, ", ")?;
671 // there might be assoc type bindings, so we leave the angle brackets open
672 angle_open = true;
673 }
629 } 674 }
630 } 675 }
676 GenericPredicate::Projection(projection_pred) if is_fn_trait => {
677 is_fn_trait = false;
678 write!(f, " -> ")?;
679 projection_pred.ty.hir_fmt(f)?;
680 }
631 GenericPredicate::Projection(projection_pred) => { 681 GenericPredicate::Projection(projection_pred) => {
632 // in types in actual Rust, these will always come 682 // in types in actual Rust, these will always come
633 // after the corresponding Implemented predicate 683 // after the corresponding Implemented predicate
diff --git a/crates/hir_ty/src/lower.rs b/crates/hir_ty/src/lower.rs
index 222f61a11..9594cce8b 100644
--- a/crates/hir_ty/src/lower.rs
+++ b/crates/hir_ty/src/lower.rs
@@ -491,16 +491,16 @@ impl Ty {
491 fn from_hir_path_inner( 491 fn from_hir_path_inner(
492 ctx: &TyLoweringContext<'_>, 492 ctx: &TyLoweringContext<'_>,
493 segment: PathSegment<'_>, 493 segment: PathSegment<'_>,
494 typable: TyDefId, 494 typeable: TyDefId,
495 infer_args: bool, 495 infer_args: bool,
496 ) -> Ty { 496 ) -> Ty {
497 let generic_def = match typable { 497 let generic_def = match typeable {
498 TyDefId::BuiltinType(_) => None, 498 TyDefId::BuiltinType(_) => None,
499 TyDefId::AdtId(it) => Some(it.into()), 499 TyDefId::AdtId(it) => Some(it.into()),
500 TyDefId::TypeAliasId(it) => Some(it.into()), 500 TyDefId::TypeAliasId(it) => Some(it.into()),
501 }; 501 };
502 let substs = substs_from_path_segment(ctx, segment, generic_def, infer_args); 502 let substs = substs_from_path_segment(ctx, segment, generic_def, infer_args);
503 ctx.db.ty(typable).subst(&substs) 503 ctx.db.ty(typeable).subst(&substs)
504 } 504 }
505 505
506 /// Collect generic arguments from a path into a `Substs`. See also 506 /// Collect generic arguments from a path into a `Substs`. See also
diff --git a/crates/hir_ty/src/tests/display_source_code.rs b/crates/hir_ty/src/tests/display_source_code.rs
index b502135d8..3d29021aa 100644
--- a/crates/hir_ty/src/tests/display_source_code.rs
+++ b/crates/hir_ty/src/tests/display_source_code.rs
@@ -39,3 +39,18 @@ fn main() {
39"#, 39"#,
40 ); 40 );
41} 41}
42
43#[test]
44fn render_raw_ptr_impl_ty() {
45 check_types_source_code(
46 r#"
47trait Sized {}
48trait Unpin {}
49fn foo() -> *const (impl Unpin + Sized) { loop {} }
50fn main() {
51 let foo = foo();
52 foo;
53} //^ *const (impl Unpin + Sized)
54"#,
55 );
56}
diff --git a/crates/hir_ty/src/tests/traits.rs b/crates/hir_ty/src/tests/traits.rs
index 41d097519..e5a3f95a6 100644
--- a/crates/hir_ty/src/tests/traits.rs
+++ b/crates/hir_ty/src/tests/traits.rs
@@ -3038,16 +3038,16 @@ fn infer_box_fn_arg() {
3038 406..417 '&self.inner': &*mut T 3038 406..417 '&self.inner': &*mut T
3039 407..411 'self': &Box<T> 3039 407..411 'self': &Box<T>
3040 407..417 'self.inner': *mut T 3040 407..417 'self.inner': *mut T
3041 478..575 '{ ...(&s) }': FnOnce::Output<dyn FnOnce<(&Option<i32>,)>, (&Option<i32>,)> 3041 478..575 '{ ...(&s) }': FnOnce::Output<dyn FnOnce(&Option<i32>), (&Option<i32>,)>
3042 488..489 's': Option<i32> 3042 488..489 's': Option<i32>
3043 492..504 'Option::None': Option<i32> 3043 492..504 'Option::None': Option<i32>
3044 514..515 'f': Box<dyn FnOnce<(&Option<i32>,)>> 3044 514..515 'f': Box<dyn FnOnce(&Option<i32>)>
3045 549..562 'box (|ps| {})': Box<|{unknown}| -> ()> 3045 549..562 'box (|ps| {})': Box<|{unknown}| -> ()>
3046 554..561 '|ps| {}': |{unknown}| -> () 3046 554..561 '|ps| {}': |{unknown}| -> ()
3047 555..557 'ps': {unknown} 3047 555..557 'ps': {unknown}
3048 559..561 '{}': () 3048 559..561 '{}': ()
3049 568..569 'f': Box<dyn FnOnce<(&Option<i32>,)>> 3049 568..569 'f': Box<dyn FnOnce(&Option<i32>)>
3050 568..573 'f(&s)': FnOnce::Output<dyn FnOnce<(&Option<i32>,)>, (&Option<i32>,)> 3050 568..573 'f(&s)': FnOnce::Output<dyn FnOnce(&Option<i32>), (&Option<i32>,)>
3051 570..572 '&s': &Option<i32> 3051 570..572 '&s': &Option<i32>
3052 571..572 's': Option<i32> 3052 571..572 's': Option<i32>
3053 "#]], 3053 "#]],
diff --git a/crates/ide/src/call_hierarchy.rs b/crates/ide/src/call_hierarchy.rs
index 4d8983cb2..e8999a7f3 100644
--- a/crates/ide/src/call_hierarchy.rs
+++ b/crates/ide/src/call_hierarchy.rs
@@ -5,7 +5,7 @@ use indexmap::IndexMap;
5use hir::Semantics; 5use hir::Semantics;
6use ide_db::call_info::FnCallNode; 6use ide_db::call_info::FnCallNode;
7use ide_db::RootDatabase; 7use ide_db::RootDatabase;
8use syntax::{ast, match_ast, AstNode, TextRange}; 8use syntax::{ast, AstNode, TextRange};
9 9
10use crate::{ 10use crate::{
11 display::TryToNav, goto_definition, references, FilePosition, NavigationTarget, RangeInfo, 11 display::TryToNav, goto_definition, references, FilePosition, NavigationTarget, RangeInfo,
@@ -47,28 +47,23 @@ pub(crate) fn incoming_calls(db: &RootDatabase, position: FilePosition) -> Optio
47 47
48 let mut calls = CallLocations::default(); 48 let mut calls = CallLocations::default();
49 49
50 for reference in refs.info.references() { 50 for (&file_id, references) in refs.info.references().iter() {
51 let file_id = reference.file_range.file_id;
52 let file = sema.parse(file_id); 51 let file = sema.parse(file_id);
53 let file = file.syntax(); 52 let file = file.syntax();
54 let token = file.token_at_offset(reference.file_range.range.start()).next()?; 53 for reference in references {
55 let token = sema.descend_into_macros(token); 54 let token = file.token_at_offset(reference.range.start()).next()?;
56 let syntax = token.parent(); 55 let token = sema.descend_into_macros(token);
57 56 let syntax = token.parent();
58 // This target is the containing function 57
59 if let Some(nav) = syntax.ancestors().find_map(|node| { 58 // This target is the containing function
60 match_ast! { 59 if let Some(nav) = syntax.ancestors().find_map(|node| {
61 match node { 60 let fn_ = ast::Fn::cast(node)?;
62 ast::Fn(it) => { 61 let def = sema.to_def(&fn_)?;
63 let def = sema.to_def(&it)?; 62 def.try_to_nav(sema.db)
64 def.try_to_nav(sema.db) 63 }) {
65 }, 64 let relative_range = reference.range;
66 _ => None, 65 calls.add(&nav, relative_range);
67 }
68 } 66 }
69 }) {
70 let relative_range = reference.file_range.range;
71 calls.add(&nav, relative_range);
72 } 67 }
73 } 68 }
74 69
@@ -91,17 +86,12 @@ pub(crate) fn outgoing_calls(db: &RootDatabase, position: FilePosition) -> Optio
91 .filter_map(|node| FnCallNode::with_node_exact(&node)) 86 .filter_map(|node| FnCallNode::with_node_exact(&node))
92 .filter_map(|call_node| { 87 .filter_map(|call_node| {
93 let name_ref = call_node.name_ref()?; 88 let name_ref = call_node.name_ref()?;
94 89 let func_target = match call_node {
95 if let Some(func_target) = match &call_node {
96 FnCallNode::CallExpr(expr) => { 90 FnCallNode::CallExpr(expr) => {
97 //FIXME: Type::as_callable is broken 91 //FIXME: Type::as_callable is broken
98 let callable = sema.type_of_expr(&expr.expr()?)?.as_callable(db)?; 92 let callable = sema.type_of_expr(&expr.expr()?)?.as_callable(db)?;
99 match callable.kind() { 93 match callable.kind() {
100 hir::CallableKind::Function(it) => { 94 hir::CallableKind::Function(it) => it.try_to_nav(db),
101 let fn_def: hir::Function = it.into();
102 let nav = fn_def.try_to_nav(db)?;
103 Some(nav)
104 }
105 _ => None, 95 _ => None,
106 } 96 }
107 } 97 }
@@ -109,11 +99,8 @@ pub(crate) fn outgoing_calls(db: &RootDatabase, position: FilePosition) -> Optio
109 let function = sema.resolve_method_call(&expr)?; 99 let function = sema.resolve_method_call(&expr)?;
110 function.try_to_nav(db) 100 function.try_to_nav(db)
111 } 101 }
112 } { 102 }?;
113 Some((func_target, name_ref.syntax().text_range())) 103 Some((func_target, name_ref.syntax().text_range()))
114 } else {
115 None
116 }
117 }) 104 })
118 .for_each(|(nav, range)| calls.add(&nav, range)); 105 .for_each(|(nav, range)| calls.add(&nav, range));
119 106
diff --git a/crates/ide/src/display/navigation_target.rs b/crates/ide/src/display/navigation_target.rs
index e24c78301..4eecae697 100644
--- a/crates/ide/src/display/navigation_target.rs
+++ b/crates/ide/src/display/navigation_target.rs
@@ -215,10 +215,8 @@ impl TryToNav for Definition {
215 Definition::ModuleDef(it) => it.try_to_nav(db), 215 Definition::ModuleDef(it) => it.try_to_nav(db),
216 Definition::SelfType(it) => it.try_to_nav(db), 216 Definition::SelfType(it) => it.try_to_nav(db),
217 Definition::Local(it) => Some(it.to_nav(db)), 217 Definition::Local(it) => Some(it.to_nav(db)),
218 Definition::TypeParam(it) => it.try_to_nav(db), 218 Definition::GenericParam(it) => it.try_to_nav(db),
219 Definition::LifetimeParam(it) => it.try_to_nav(db),
220 Definition::Label(it) => Some(it.to_nav(db)), 219 Definition::Label(it) => Some(it.to_nav(db)),
221 Definition::ConstParam(it) => it.try_to_nav(db),
222 } 220 }
223 } 221 }
224} 222}
@@ -389,6 +387,16 @@ impl TryToNav for hir::AssocItem {
389 } 387 }
390} 388}
391 389
390impl TryToNav for hir::GenericParam {
391 fn try_to_nav(&self, db: &RootDatabase) -> Option<NavigationTarget> {
392 match self {
393 hir::GenericParam::TypeParam(it) => it.try_to_nav(db),
394 hir::GenericParam::ConstParam(it) => it.try_to_nav(db),
395 hir::GenericParam::LifetimeParam(it) => it.try_to_nav(db),
396 }
397 }
398}
399
392impl ToNav for hir::Local { 400impl ToNav for hir::Local {
393 fn to_nav(&self, db: &RootDatabase) -> NavigationTarget { 401 fn to_nav(&self, db: &RootDatabase) -> NavigationTarget {
394 let src = self.source(db); 402 let src = self.source(db);
diff --git a/crates/ide/src/doc_links.rs b/crates/ide/src/doc_links.rs
index 1ff818de2..de10406bc 100644
--- a/crates/ide/src/doc_links.rs
+++ b/crates/ide/src/doc_links.rs
@@ -1,6 +1,6 @@
1//! Resolves and rewrites links in markdown documentation. 1//! Resolves and rewrites links in markdown documentation.
2 2
3use std::{convert::TryFrom, iter::once}; 3use std::{convert::TryFrom, iter::once, ops::Range};
4 4
5use itertools::Itertools; 5use itertools::Itertools;
6use pulldown_cmark::{BrokenLink, CowStr, Event, InlineStr, LinkType, Options, Parser, Tag}; 6use pulldown_cmark::{BrokenLink, CowStr, Event, InlineStr, LinkType, Options, Parser, Tag};
@@ -39,7 +39,7 @@ pub(crate) fn rewrite_links(db: &RootDatabase, markdown: &str, definition: &Defi
39 if target.contains("://") { 39 if target.contains("://") {
40 (target.to_string(), title.to_string()) 40 (target.to_string(), title.to_string())
41 } else { 41 } else {
42 // Two posibilities: 42 // Two possibilities:
43 // * path-based links: `../../module/struct.MyStruct.html` 43 // * path-based links: `../../module/struct.MyStruct.html`
44 // * module-based links (AKA intra-doc links): `super::super::module::MyStruct` 44 // * module-based links (AKA intra-doc links): `super::super::module::MyStruct`
45 if let Some(rewritten) = rewrite_intra_doc_link(db, *definition, target, title) { 45 if let Some(rewritten) = rewrite_intra_doc_link(db, *definition, target, title) {
@@ -61,6 +61,30 @@ pub(crate) fn rewrite_links(db: &RootDatabase, markdown: &str, definition: &Defi
61 out 61 out
62} 62}
63 63
64pub(crate) fn extract_definitions_from_markdown(
65 markdown: &str,
66) -> Vec<(String, Option<hir::Namespace>, Range<usize>)> {
67 let mut res = vec![];
68 let mut cb = |link: BrokenLink| {
69 Some((
70 /*url*/ link.reference.to_owned().into(),
71 /*title*/ link.reference.to_owned().into(),
72 ))
73 };
74 let doc = Parser::new_with_broken_link_callback(markdown, Options::empty(), Some(&mut cb));
75 for (event, range) in doc.into_offset_iter() {
76 match event {
77 Event::Start(Tag::Link(_link_type, ref target, ref title)) => {
78 let link = if target.is_empty() { title } else { target };
79 let (link, ns) = parse_link(link);
80 res.push((link.to_string(), ns, range));
81 }
82 _ => {}
83 }
84 }
85 res
86}
87
64/// Remove all links in markdown documentation. 88/// Remove all links in markdown documentation.
65pub(crate) fn remove_links(markdown: &str) -> String { 89pub(crate) fn remove_links(markdown: &str) -> String {
66 let mut drop_link = false; 90 let mut drop_link = false;
@@ -192,9 +216,7 @@ fn rewrite_intra_doc_link(
192 Definition::Field(it) => it.resolve_doc_path(db, link, ns), 216 Definition::Field(it) => it.resolve_doc_path(db, link, ns),
193 Definition::SelfType(_) 217 Definition::SelfType(_)
194 | Definition::Local(_) 218 | Definition::Local(_)
195 | Definition::TypeParam(_) 219 | Definition::GenericParam(_)
196 | Definition::ConstParam(_)
197 | Definition::LifetimeParam(_)
198 | Definition::Label(_) => return None, 220 | Definition::Label(_) => return None,
199 }?; 221 }?;
200 let krate = resolved.module(db)?.krate(); 222 let krate = resolved.module(db)?.krate();
@@ -420,7 +442,7 @@ fn get_symbol_fragment(db: &dyn HirDatabase, field_or_assoc: &FieldOrAssocItem)
420 function.as_assoc_item(db).map(|assoc| assoc.container(db)), 442 function.as_assoc_item(db).map(|assoc| assoc.container(db)),
421 Some(AssocItemContainer::Trait(..)) 443 Some(AssocItemContainer::Trait(..))
422 ); 444 );
423 // This distinction may get more complicated when specialisation is available. 445 // This distinction may get more complicated when specialization is available.
424 // Rustdoc makes this decision based on whether a method 'has defaultness'. 446 // Rustdoc makes this decision based on whether a method 'has defaultness'.
425 // Currently this is only the case for provided trait methods. 447 // Currently this is only the case for provided trait methods.
426 if is_trait_method && !function.has_body(db) { 448 if is_trait_method && !function.has_body(db) {
diff --git a/crates/ide/src/goto_definition.rs b/crates/ide/src/goto_definition.rs
index 95b4cb9e3..cd4afc804 100644
--- a/crates/ide/src/goto_definition.rs
+++ b/crates/ide/src/goto_definition.rs
@@ -1,14 +1,18 @@
1use either::Either; 1use either::Either;
2use hir::Semantics; 2use hir::{HasAttrs, ModuleDef, Semantics};
3use ide_db::{ 3use ide_db::{
4 base_db::FileId, 4 base_db::FileId,
5 defs::{NameClass, NameRefClass}, 5 defs::{Definition, NameClass, NameRefClass},
6 symbol_index, RootDatabase, 6 symbol_index, RootDatabase,
7}; 7};
8use syntax::{ast, match_ast, AstNode, SyntaxKind::*, SyntaxToken, TokenAtOffset, T}; 8use syntax::{
9 ast, match_ast, AstNode, AstToken, SyntaxKind::*, SyntaxToken, TextSize, TokenAtOffset, T,
10};
9 11
10use crate::{ 12use crate::{
11 display::{ToNav, TryToNav}, 13 display::{ToNav, TryToNav},
14 doc_links::extract_definitions_from_markdown,
15 runnables::doc_owner_to_def,
12 FilePosition, NavigationTarget, RangeInfo, SymbolKind, 16 FilePosition, NavigationTarget, RangeInfo, SymbolKind,
13}; 17};
14 18
@@ -30,6 +34,10 @@ pub(crate) fn goto_definition(
30 let original_token = pick_best(file.token_at_offset(position.offset))?; 34 let original_token = pick_best(file.token_at_offset(position.offset))?;
31 let token = sema.descend_into_macros(original_token.clone()); 35 let token = sema.descend_into_macros(original_token.clone());
32 let parent = token.parent(); 36 let parent = token.parent();
37 if let Some(comment) = ast::Comment::cast(token.clone()) {
38 let nav = def_for_doc_comment(&sema, position, &comment)?.try_to_nav(db)?;
39 return Some(RangeInfo::new(original_token.text_range(), vec![nav]));
40 }
33 41
34 let nav_targets = match_ast! { 42 let nav_targets = match_ast! {
35 match parent { 43 match parent {
@@ -68,11 +76,58 @@ pub(crate) fn goto_definition(
68 Some(RangeInfo::new(original_token.text_range(), nav_targets)) 76 Some(RangeInfo::new(original_token.text_range(), nav_targets))
69} 77}
70 78
79fn def_for_doc_comment(
80 sema: &Semantics<RootDatabase>,
81 position: FilePosition,
82 doc_comment: &ast::Comment,
83) -> Option<hir::ModuleDef> {
84 let parent = doc_comment.syntax().parent();
85 let (link, ns) = extract_positioned_link_from_comment(position, doc_comment)?;
86
87 let def = doc_owner_to_def(sema, parent)?;
88 match def {
89 Definition::ModuleDef(def) => match def {
90 ModuleDef::Module(it) => it.resolve_doc_path(sema.db, &link, ns),
91 ModuleDef::Function(it) => it.resolve_doc_path(sema.db, &link, ns),
92 ModuleDef::Adt(it) => it.resolve_doc_path(sema.db, &link, ns),
93 ModuleDef::Variant(it) => it.resolve_doc_path(sema.db, &link, ns),
94 ModuleDef::Const(it) => it.resolve_doc_path(sema.db, &link, ns),
95 ModuleDef::Static(it) => it.resolve_doc_path(sema.db, &link, ns),
96 ModuleDef::Trait(it) => it.resolve_doc_path(sema.db, &link, ns),
97 ModuleDef::TypeAlias(it) => it.resolve_doc_path(sema.db, &link, ns),
98 ModuleDef::BuiltinType(_) => return None,
99 },
100 Definition::Macro(it) => it.resolve_doc_path(sema.db, &link, ns),
101 Definition::Field(it) => it.resolve_doc_path(sema.db, &link, ns),
102 Definition::SelfType(_)
103 | Definition::Local(_)
104 | Definition::GenericParam(_)
105 | Definition::Label(_) => return None,
106 }
107}
108
109fn extract_positioned_link_from_comment(
110 position: FilePosition,
111 comment: &ast::Comment,
112) -> Option<(String, Option<hir::Namespace>)> {
113 let comment_range = comment.syntax().text_range();
114 let doc_comment = comment.doc_comment()?;
115 let def_links = extract_definitions_from_markdown(doc_comment);
116 let (def_link, ns, _) = def_links.iter().min_by_key(|(_, _, def_link_range)| {
117 let matched_position = comment_range.start() + TextSize::from(def_link_range.start as u32);
118 match position.offset.checked_sub(matched_position) {
119 Some(distance) => distance,
120 None => comment_range.end(),
121 }
122 })?;
123 Some((def_link.to_string(), ns.clone()))
124}
125
71fn pick_best(tokens: TokenAtOffset<SyntaxToken>) -> Option<SyntaxToken> { 126fn pick_best(tokens: TokenAtOffset<SyntaxToken>) -> Option<SyntaxToken> {
72 return tokens.max_by_key(priority); 127 return tokens.max_by_key(priority);
73 fn priority(n: &SyntaxToken) -> usize { 128 fn priority(n: &SyntaxToken) -> usize {
74 match n.kind() { 129 match n.kind() {
75 IDENT | INT_NUMBER | LIFETIME_IDENT | T![self] => 2, 130 IDENT | INT_NUMBER | LIFETIME_IDENT | T![self] | COMMENT => 2,
76 kind if kind.is_trivia() => 0, 131 kind if kind.is_trivia() => 0,
77 _ => 1, 132 _ => 1,
78 } 133 }
@@ -1145,4 +1200,34 @@ fn foo<'foo>(_: &'foo ()) {
1145}"#, 1200}"#,
1146 ) 1201 )
1147 } 1202 }
1203
1204 #[test]
1205 fn goto_def_for_intra_doc_link_same_file() {
1206 check(
1207 r#"
1208/// Blah, [`bar`](bar) .. [`foo`](foo)$0 has [`bar`](bar)
1209pub fn bar() { }
1210
1211/// You might want to see [`std::fs::read()`] too.
1212pub fn foo() { }
1213 //^^^
1214
1215}"#,
1216 )
1217 }
1218
1219 #[test]
1220 fn goto_def_for_intra_doc_link_inner() {
1221 check(
1222 r#"
1223//- /main.rs
1224mod m;
1225struct S;
1226 //^
1227
1228//- /m.rs
1229//! [`super::S$0`]
1230"#,
1231 )
1232 }
1148} 1233}
diff --git a/crates/ide/src/hover.rs b/crates/ide/src/hover.rs
index 8cb4a51d8..317b6f011 100644
--- a/crates/ide/src/hover.rs
+++ b/crates/ide/src/hover.rs
@@ -1,6 +1,6 @@
1use hir::{ 1use hir::{
2 Adt, AsAssocItem, AssocItemContainer, FieldSource, HasAttrs, HasSource, HirDisplay, Module, 2 Adt, AsAssocItem, AssocItemContainer, FieldSource, GenericParam, HasAttrs, HasSource,
3 ModuleDef, ModuleSource, Semantics, 3 HirDisplay, Module, ModuleDef, ModuleSource, Semantics,
4}; 4};
5use ide_db::base_db::SourceDatabase; 5use ide_db::base_db::SourceDatabase;
6use ide_db::{ 6use ide_db::{
@@ -17,7 +17,7 @@ use crate::{
17 doc_links::{remove_links, rewrite_links}, 17 doc_links::{remove_links, rewrite_links},
18 markdown_remove::remove_markdown, 18 markdown_remove::remove_markdown,
19 markup::Markup, 19 markup::Markup,
20 runnables::{runnable, runnable_fn}, 20 runnables::{runnable_fn, runnable_mod},
21 FileId, FilePosition, NavigationTarget, RangeInfo, Runnable, 21 FileId, FilePosition, NavigationTarget, RangeInfo, Runnable,
22}; 22};
23 23
@@ -175,12 +175,7 @@ fn show_implementations_action(db: &RootDatabase, def: Definition) -> Option<Hov
175 Definition::SelfType(it) => it.target_ty(db).as_adt(), 175 Definition::SelfType(it) => it.target_ty(db).as_adt(),
176 _ => None, 176 _ => None,
177 }?; 177 }?;
178 match adt { 178 adt.try_to_nav(db).map(to_action)
179 Adt::Struct(it) => it.try_to_nav(db),
180 Adt::Union(it) => it.try_to_nav(db),
181 Adt::Enum(it) => it.try_to_nav(db),
182 }
183 .map(to_action)
184} 179}
185 180
186fn runnable_action( 181fn runnable_action(
@@ -192,7 +187,7 @@ fn runnable_action(
192 Definition::ModuleDef(it) => match it { 187 Definition::ModuleDef(it) => match it {
193 ModuleDef::Module(it) => match it.definition_source(sema.db).value { 188 ModuleDef::Module(it) => match it.definition_source(sema.db).value {
194 ModuleSource::Module(it) => { 189 ModuleSource::Module(it) => {
195 runnable(&sema, it.syntax().clone()).map(|it| HoverAction::Runnable(it)) 190 runnable_mod(&sema, it).map(|it| HoverAction::Runnable(it))
196 } 191 }
197 _ => None, 192 _ => None,
198 }, 193 },
@@ -220,12 +215,12 @@ fn goto_type_action(db: &RootDatabase, def: Definition) -> Option<HoverAction> {
220 } 215 }
221 }; 216 };
222 217
223 if let Definition::TypeParam(it) = def { 218 if let Definition::GenericParam(GenericParam::TypeParam(it)) = def {
224 it.trait_bounds(db).into_iter().for_each(|it| push_new_def(it.into())); 219 it.trait_bounds(db).into_iter().for_each(|it| push_new_def(it.into()));
225 } else { 220 } else {
226 let ty = match def { 221 let ty = match def {
227 Definition::Local(it) => it.ty(db), 222 Definition::Local(it) => it.ty(db),
228 Definition::ConstParam(it) => it.ty(db), 223 Definition::GenericParam(GenericParam::ConstParam(it)) => it.ty(db),
229 _ => return None, 224 _ => return None,
230 }; 225 };
231 226
@@ -357,9 +352,11 @@ fn hover_for_definition(db: &RootDatabase, def: Definition) -> Option<Markup> {
357 }) 352 })
358 } 353 }
359 Definition::Label(it) => Some(Markup::fenced_block(&it.name(db))), 354 Definition::Label(it) => Some(Markup::fenced_block(&it.name(db))),
360 Definition::LifetimeParam(it) => Some(Markup::fenced_block(&it.name(db))), 355 Definition::GenericParam(it) => match it {
361 Definition::TypeParam(type_param) => Some(Markup::fenced_block(&type_param.display(db))), 356 GenericParam::TypeParam(it) => Some(Markup::fenced_block(&it.display(db))),
362 Definition::ConstParam(it) => from_def_source(db, it, None), 357 GenericParam::LifetimeParam(it) => Some(Markup::fenced_block(&it.name(db))),
358 GenericParam::ConstParam(it) => from_def_source(db, it, None),
359 },
363 }; 360 };
364 361
365 fn from_def_source<A, D>(db: &RootDatabase, def: D, mod_path: Option<String>) -> Option<Markup> 362 fn from_def_source<A, D>(db: &RootDatabase, def: D, mod_path: Option<String>) -> Option<Markup>
@@ -1951,16 +1948,16 @@ struct S {
1951/// Test cases: 1948/// Test cases:
1952/// case 1. bare URL: https://www.example.com/ 1949/// case 1. bare URL: https://www.example.com/
1953/// case 2. inline URL with title: [example](https://www.example.com/) 1950/// case 2. inline URL with title: [example](https://www.example.com/)
1954/// case 3. code refrence: [`Result`] 1951/// case 3. code reference: [`Result`]
1955/// case 4. code refrence but miss footnote: [`String`] 1952/// case 4. code reference but miss footnote: [`String`]
1956/// case 5. autolink: <http://www.example.com/> 1953/// case 5. autolink: <http://www.example.com/>
1957/// case 6. email address: <[email protected]> 1954/// case 6. email address: <[email protected]>
1958/// case 7. refrence: [example][example] 1955/// case 7. reference: [example][example]
1959/// case 8. collapsed link: [example][] 1956/// case 8. collapsed link: [example][]
1960/// case 9. shortcut link: [example] 1957/// case 9. shortcut link: [example]
1961/// case 10. inline without URL: [example]() 1958/// case 10. inline without URL: [example]()
1962/// case 11. refrence: [foo][foo] 1959/// case 11. reference: [foo][foo]
1963/// case 12. refrence: [foo][bar] 1960/// case 12. reference: [foo][bar]
1964/// case 13. collapsed link: [foo][] 1961/// case 13. collapsed link: [foo][]
1965/// case 14. shortcut link: [foo] 1962/// case 14. shortcut link: [foo]
1966/// case 15. inline without URL: [foo]() 1963/// case 15. inline without URL: [foo]()
@@ -1987,16 +1984,16 @@ pub fn fo$0o() {}
1987 Test cases: 1984 Test cases:
1988 case 1. bare URL: https://www.example.com/ 1985 case 1. bare URL: https://www.example.com/
1989 case 2. inline URL with title: [example](https://www.example.com/) 1986 case 2. inline URL with title: [example](https://www.example.com/)
1990 case 3. code refrence: `Result` 1987 case 3. code reference: `Result`
1991 case 4. code refrence but miss footnote: `String` 1988 case 4. code reference but miss footnote: `String`
1992 case 5. autolink: http://www.example.com/ 1989 case 5. autolink: http://www.example.com/
1993 case 6. email address: [email protected] 1990 case 6. email address: [email protected]
1994 case 7. refrence: example 1991 case 7. reference: example
1995 case 8. collapsed link: example 1992 case 8. collapsed link: example
1996 case 9. shortcut link: example 1993 case 9. shortcut link: example
1997 case 10. inline without URL: example 1994 case 10. inline without URL: example
1998 case 11. refrence: foo 1995 case 11. reference: foo
1999 case 12. refrence: foo 1996 case 12. reference: foo
2000 case 13. collapsed link: foo 1997 case 13. collapsed link: foo
2001 case 14. shortcut link: foo 1998 case 14. shortcut link: foo
2002 case 15. inline without URL: foo 1999 case 15. inline without URL: foo
diff --git a/crates/ide/src/inlay_hints.rs b/crates/ide/src/inlay_hints.rs
index fe60abfc8..a2039fcc7 100644
--- a/crates/ide/src/inlay_hints.rs
+++ b/crates/ide/src/inlay_hints.rs
@@ -353,9 +353,25 @@ fn is_argument_similar_to_param_name(
353 } 353 }
354 match get_string_representation(argument) { 354 match get_string_representation(argument) {
355 None => false, 355 None => false,
356 Some(repr) => { 356 Some(argument_string) => {
357 let argument_string = repr.trim_start_matches('_'); 357 let num_leading_underscores =
358 argument_string.starts_with(param_name) || argument_string.ends_with(param_name) 358 argument_string.bytes().take_while(|&c| c == b'_').count();
359
360 // Does the argument name begin with the parameter name? Ignore leading underscores.
361 let mut arg_bytes = argument_string.bytes().skip(num_leading_underscores);
362 let starts_with_pattern = param_name.bytes().all(
363 |expected| matches!(arg_bytes.next(), Some(actual) if expected.eq_ignore_ascii_case(&actual)),
364 );
365
366 if starts_with_pattern {
367 return true;
368 }
369
370 // Does the argument name end with the parameter name?
371 let mut arg_bytes = argument_string.bytes().skip(num_leading_underscores);
372 param_name.bytes().rev().all(
373 |expected| matches!(arg_bytes.next_back(), Some(actual) if expected.eq_ignore_ascii_case(&actual)),
374 )
359 } 375 }
360 } 376 }
361} 377}
@@ -901,6 +917,9 @@ fn main() {
901 twiddle(true); 917 twiddle(true);
902 doo(true); 918 doo(true);
903 919
920 const TWIDDLE_UPPERCASE: bool = true;
921 twiddle(TWIDDLE_UPPERCASE);
922
904 let mut param_begin: Param = Param {}; 923 let mut param_begin: Param = Param {};
905 different_order(&param_begin); 924 different_order(&param_begin);
906 different_order(&mut param_begin); 925 different_order(&mut param_begin);
@@ -1382,4 +1401,41 @@ fn main() {
1382"#, 1401"#,
1383 ) 1402 )
1384 } 1403 }
1404
1405 #[test]
1406 fn fn_hints() {
1407 check(
1408 r#"
1409trait Sized {}
1410
1411fn foo() -> impl Fn() { loop {} }
1412fn foo1() -> impl Fn(f64) { loop {} }
1413fn foo2() -> impl Fn(f64, f64) { loop {} }
1414fn foo3() -> impl Fn(f64, f64) -> u32 { loop {} }
1415fn foo4() -> &'static dyn Fn(f64, f64) -> u32 { loop {} }
1416fn foo5() -> &'static dyn Fn(&'static dyn Fn(f64, f64) -> u32, f64) -> u32 { loop {} }
1417fn foo6() -> impl Fn(f64, f64) -> u32 + Sized { loop {} }
1418fn foo7() -> *const (impl Fn(f64, f64) -> u32 + Sized) { loop {} }
1419
1420fn main() {
1421 let foo = foo();
1422 // ^^^ impl Fn()
1423 let foo = foo1();
1424 // ^^^ impl Fn(f64)
1425 let foo = foo2();
1426 // ^^^ impl Fn(f64, f64)
1427 let foo = foo3();
1428 // ^^^ impl Fn(f64, f64) -> u32
1429 let foo = foo4();
1430 // ^^^ &dyn Fn(f64, f64) -> u32
1431 let foo = foo5();
1432 // ^^^ &dyn Fn(&dyn Fn(f64, f64) -> u32, f64) -> u32
1433 let foo = foo6();
1434 // ^^^ impl Fn(f64, f64) -> u32 + Sized
1435 let foo = foo7();
1436 // ^^^ *const (impl Fn(f64, f64) -> u32 + Sized)
1437}
1438"#,
1439 )
1440 }
1385} 1441}
diff --git a/crates/ide/src/lib.rs b/crates/ide/src/lib.rs
index cea2a13c8..1e03832ec 100644
--- a/crates/ide/src/lib.rs
+++ b/crates/ide/src/lib.rs
@@ -76,8 +76,8 @@ pub use crate::{
76 references::{rename::RenameError, Declaration, ReferenceSearchResult}, 76 references::{rename::RenameError, Declaration, ReferenceSearchResult},
77 runnables::{Runnable, RunnableKind, TestId}, 77 runnables::{Runnable, RunnableKind, TestId},
78 syntax_highlighting::{ 78 syntax_highlighting::{
79 tags::{Highlight, HighlightModifier, HighlightModifiers, HighlightTag}, 79 tags::{Highlight, HlMod, HlMods, HlPunct, HlTag},
80 HighlightedRange, 80 HlRange,
81 }, 81 },
82}; 82};
83pub use assists::{Assist, AssistConfig, AssistId, AssistKind, InsertUseConfig}; 83pub use assists::{Assist, AssistConfig, AssistId, AssistKind, InsertUseConfig};
@@ -92,7 +92,7 @@ pub use ide_db::base_db::{
92}; 92};
93pub use ide_db::{ 93pub use ide_db::{
94 call_info::CallInfo, 94 call_info::CallInfo,
95 search::{Reference, ReferenceAccess, ReferenceKind}, 95 search::{FileReference, ReferenceAccess, ReferenceKind},
96}; 96};
97pub use ide_db::{ 97pub use ide_db::{
98 label::Label, 98 label::Label,
@@ -449,12 +449,12 @@ impl Analysis {
449 } 449 }
450 450
451 /// Computes syntax highlighting for the given file 451 /// Computes syntax highlighting for the given file
452 pub fn highlight(&self, file_id: FileId) -> Cancelable<Vec<HighlightedRange>> { 452 pub fn highlight(&self, file_id: FileId) -> Cancelable<Vec<HlRange>> {
453 self.with_db(|db| syntax_highlighting::highlight(db, file_id, None, false)) 453 self.with_db(|db| syntax_highlighting::highlight(db, file_id, None, false))
454 } 454 }
455 455
456 /// Computes syntax highlighting for the given file range. 456 /// Computes syntax highlighting for the given file range.
457 pub fn highlight_range(&self, frange: FileRange) -> Cancelable<Vec<HighlightedRange>> { 457 pub fn highlight_range(&self, frange: FileRange) -> Cancelable<Vec<HlRange>> {
458 self.with_db(|db| { 458 self.with_db(|db| {
459 syntax_highlighting::highlight(db, frange.file_id, Some(frange.range), false) 459 syntax_highlighting::highlight(db, frange.file_id, Some(frange.range), false)
460 }) 460 })
diff --git a/crates/ide/src/references.rs b/crates/ide/src/references.rs
index c95ed669c..7d4757e02 100644
--- a/crates/ide/src/references.rs
+++ b/crates/ide/src/references.rs
@@ -3,7 +3,7 @@
3//! or `ast::NameRef`. If it's a `ast::NameRef`, at the classification step we 3//! or `ast::NameRef`. If it's a `ast::NameRef`, at the classification step we
4//! try to resolve the direct tree parent of this element, otherwise we 4//! try to resolve the direct tree parent of this element, otherwise we
5//! already have a definition and just need to get its HIR together with 5//! already have a definition and just need to get its HIR together with
6//! some information that is needed for futher steps of searching. 6//! some information that is needed for further steps of searching.
7//! After that, we collect files that might contain references and look 7//! After that, we collect files that might contain references and look
8//! for text occurrences of the identifier. If there's an `ast::NameRef` 8//! for text occurrences of the identifier. If there's an `ast::NameRef`
9//! at the index that the match starts at and its tree parent is 9//! at the index that the match starts at and its tree parent is
@@ -13,15 +13,15 @@ pub(crate) mod rename;
13 13
14use hir::Semantics; 14use hir::Semantics;
15use ide_db::{ 15use ide_db::{
16 base_db::FileId,
16 defs::{Definition, NameClass, NameRefClass}, 17 defs::{Definition, NameClass, NameRefClass},
17 search::Reference, 18 search::{FileReference, ReferenceAccess, ReferenceKind, SearchScope, UsageSearchResult},
18 search::{ReferenceAccess, ReferenceKind, SearchScope},
19 RootDatabase, 19 RootDatabase,
20}; 20};
21use syntax::{ 21use syntax::{
22 algo::find_node_at_offset, 22 algo::find_node_at_offset,
23 ast::{self, NameOwner}, 23 ast::{self, NameOwner},
24 match_ast, AstNode, SyntaxKind, SyntaxNode, TextRange, TokenAtOffset, 24 match_ast, AstNode, SyntaxNode, TextRange, TokenAtOffset, T,
25}; 25};
26 26
27use crate::{display::TryToNav, FilePosition, FileRange, NavigationTarget, RangeInfo, SymbolKind}; 27use crate::{display::TryToNav, FilePosition, FileRange, NavigationTarget, RangeInfo, SymbolKind};
@@ -29,7 +29,7 @@ use crate::{display::TryToNav, FilePosition, FileRange, NavigationTarget, RangeI
29#[derive(Debug, Clone)] 29#[derive(Debug, Clone)]
30pub struct ReferenceSearchResult { 30pub struct ReferenceSearchResult {
31 declaration: Declaration, 31 declaration: Declaration,
32 references: Vec<Reference>, 32 references: UsageSearchResult,
33} 33}
34 34
35#[derive(Debug, Clone)] 35#[derive(Debug, Clone)]
@@ -48,10 +48,21 @@ impl ReferenceSearchResult {
48 &self.declaration.nav 48 &self.declaration.nav
49 } 49 }
50 50
51 pub fn references(&self) -> &[Reference] { 51 pub fn references(&self) -> &UsageSearchResult {
52 &self.references 52 &self.references
53 } 53 }
54 54
55 pub fn references_with_declaration(mut self) -> UsageSearchResult {
56 let decl_ref = FileReference {
57 range: self.declaration.nav.focus_or_full_range(),
58 kind: self.declaration.kind,
59 access: self.declaration.access,
60 };
61 let file_id = self.declaration.nav.file_id;
62 self.references.references.entry(file_id).or_default().push(decl_ref);
63 self.references
64 }
65
55 /// Total number of references 66 /// Total number of references
56 /// At least 1 since all valid references should 67 /// At least 1 since all valid references should
57 /// Have a declaration 68 /// Have a declaration
@@ -63,21 +74,11 @@ impl ReferenceSearchResult {
63// allow turning ReferenceSearchResult into an iterator 74// allow turning ReferenceSearchResult into an iterator
64// over References 75// over References
65impl IntoIterator for ReferenceSearchResult { 76impl IntoIterator for ReferenceSearchResult {
66 type Item = Reference; 77 type Item = (FileId, Vec<FileReference>);
67 type IntoIter = std::vec::IntoIter<Reference>; 78 type IntoIter = std::collections::hash_map::IntoIter<FileId, Vec<FileReference>>;
68 79
69 fn into_iter(mut self) -> Self::IntoIter { 80 fn into_iter(self) -> Self::IntoIter {
70 let mut v = Vec::with_capacity(self.len()); 81 self.references_with_declaration().into_iter()
71 v.push(Reference {
72 file_range: FileRange {
73 file_id: self.declaration.nav.file_id,
74 range: self.declaration.nav.focus_or_full_range(),
75 },
76 kind: self.declaration.kind,
77 access: self.declaration.access,
78 });
79 v.append(&mut self.references);
80 v.into_iter()
81 } 82 }
82} 83}
83 84
@@ -109,13 +110,12 @@ pub(crate) fn find_all_refs(
109 110
110 let RangeInfo { range, info: def } = find_name(&sema, &syntax, position, opt_name)?; 111 let RangeInfo { range, info: def } = find_name(&sema, &syntax, position, opt_name)?;
111 112
112 let references = def 113 let mut usages = def.usages(sema).set_scope(search_scope).all();
113 .usages(sema) 114 usages
114 .set_scope(search_scope) 115 .references
115 .all() 116 .values_mut()
116 .into_iter() 117 .for_each(|it| it.retain(|r| search_kind == ReferenceKind::Other || search_kind == r.kind));
117 .filter(|r| search_kind == ReferenceKind::Other || search_kind == r.kind) 118 usages.references.retain(|_, it| !it.is_empty());
118 .collect();
119 119
120 let nav = def.try_to_nav(sema.db)?; 120 let nav = def.try_to_nav(sema.db)?;
121 let decl_range = nav.focus_or_full_range(); 121 let decl_range = nav.focus_or_full_range();
@@ -130,13 +130,16 @@ pub(crate) fn find_all_refs(
130 kind = ReferenceKind::FieldShorthandForLocal; 130 kind = ReferenceKind::FieldShorthandForLocal;
131 } 131 }
132 } 132 }
133 } else if matches!(def, Definition::LifetimeParam(_) | Definition::Label(_)) { 133 } else if matches!(
134 def,
135 Definition::GenericParam(hir::GenericParam::LifetimeParam(_)) | Definition::Label(_)
136 ) {
134 kind = ReferenceKind::Lifetime; 137 kind = ReferenceKind::Lifetime;
135 }; 138 };
136 139
137 let declaration = Declaration { nav, kind, access: decl_access(&def, &syntax, decl_range) }; 140 let declaration = Declaration { nav, kind, access: decl_access(&def, &syntax, decl_range) };
138 141
139 Some(RangeInfo::new(range, ReferenceSearchResult { declaration, references })) 142 Some(RangeInfo::new(range, ReferenceSearchResult { declaration, references: usages }))
140} 143}
141 144
142fn find_name( 145fn find_name(
@@ -200,7 +203,7 @@ fn get_struct_def_name_for_struct_literal_search(
200 position: FilePosition, 203 position: FilePosition,
201) -> Option<ast::Name> { 204) -> Option<ast::Name> {
202 if let TokenAtOffset::Between(ref left, ref right) = syntax.token_at_offset(position.offset) { 205 if let TokenAtOffset::Between(ref left, ref right) = syntax.token_at_offset(position.offset) {
203 if right.kind() != SyntaxKind::L_CURLY && right.kind() != SyntaxKind::L_PAREN { 206 if right.kind() != T!['{'] && right.kind() != T!['('] {
204 return None; 207 return None;
205 } 208 }
206 if let Some(name) = 209 if let Some(name) =
@@ -227,7 +230,7 @@ fn get_enum_def_name_for_struct_literal_search(
227 position: FilePosition, 230 position: FilePosition,
228) -> Option<ast::Name> { 231) -> Option<ast::Name> {
229 if let TokenAtOffset::Between(ref left, ref right) = syntax.token_at_offset(position.offset) { 232 if let TokenAtOffset::Between(ref left, ref right) = syntax.token_at_offset(position.offset) {
230 if right.kind() != SyntaxKind::L_CURLY && right.kind() != SyntaxKind::L_PAREN { 233 if right.kind() != T!['{'] && right.kind() != T!['('] {
231 return None; 234 return None;
232 } 235 }
233 if let Some(name) = 236 if let Some(name) =
@@ -252,8 +255,8 @@ fn try_find_self_references(
252 syntax: &SyntaxNode, 255 syntax: &SyntaxNode,
253 position: FilePosition, 256 position: FilePosition,
254) -> Option<RangeInfo<ReferenceSearchResult>> { 257) -> Option<RangeInfo<ReferenceSearchResult>> {
255 let self_token = 258 let FilePosition { file_id, offset } = position;
256 syntax.token_at_offset(position.offset).find(|t| t.kind() == SyntaxKind::SELF_KW)?; 259 let self_token = syntax.token_at_offset(offset).find(|t| t.kind() == T![self])?;
257 let parent = self_token.parent(); 260 let parent = self_token.parent();
258 match_ast! { 261 match_ast! {
259 match parent { 262 match parent {
@@ -274,7 +277,7 @@ fn try_find_self_references(
274 277
275 let declaration = Declaration { 278 let declaration = Declaration {
276 nav: NavigationTarget { 279 nav: NavigationTarget {
277 file_id: position.file_id, 280 file_id,
278 full_range: self_param.syntax().text_range(), 281 full_range: self_param.syntax().text_range(),
279 focus_range: Some(param_self_token.text_range()), 282 focus_range: Some(param_self_token.text_range()),
280 name: param_self_token.text().clone(), 283 name: param_self_token.text().clone(),
@@ -290,7 +293,7 @@ fn try_find_self_references(
290 ReferenceAccess::Read 293 ReferenceAccess::Read
291 }), 294 }),
292 }; 295 };
293 let references = function 296 let refs = function
294 .body() 297 .body()
295 .map(|body| { 298 .map(|body| {
296 body.syntax() 299 body.syntax()
@@ -304,14 +307,16 @@ fn try_find_self_references(
304 None 307 None
305 } 308 }
306 }) 309 })
307 .map(|token| Reference { 310 .map(|token| FileReference {
308 file_range: FileRange { file_id: position.file_id, range: token.text_range() }, 311 range: token.text_range(),
309 kind: ReferenceKind::SelfKw, 312 kind: ReferenceKind::SelfKw,
310 access: declaration.access, // FIXME: properly check access kind here instead of copying it from the declaration 313 access: declaration.access, // FIXME: properly check access kind here instead of copying it from the declaration
311 }) 314 })
312 .collect() 315 .collect()
313 }) 316 })
314 .unwrap_or_default(); 317 .unwrap_or_default();
318 let mut references = UsageSearchResult::default();
319 references.references.insert(file_id, refs);
315 320
316 Some(RangeInfo::new( 321 Some(RangeInfo::new(
317 param_self_token.text_range(), 322 param_self_token.text_range(),
@@ -1016,12 +1021,14 @@ impl Foo {
1016 actual += "\n\n"; 1021 actual += "\n\n";
1017 } 1022 }
1018 1023
1019 for r in &refs.references { 1024 for (file_id, references) in refs.references {
1020 format_to!(actual, "{:?} {:?} {:?}", r.file_range.file_id, r.file_range.range, r.kind); 1025 for r in references {
1021 if let Some(access) = r.access { 1026 format_to!(actual, "{:?} {:?} {:?}", file_id, r.range, r.kind);
1022 format_to!(actual, " {:?}", access); 1027 if let Some(access) = r.access {
1028 format_to!(actual, " {:?}", access);
1029 }
1030 actual += "\n";
1023 } 1031 }
1024 actual += "\n";
1025 } 1032 }
1026 expect.assert_eq(&actual) 1033 expect.assert_eq(&actual)
1027 } 1034 }
diff --git a/crates/ide/src/references/rename.rs b/crates/ide/src/references/rename.rs
index 53d79333c..c3ae568c2 100644
--- a/crates/ide/src/references/rename.rs
+++ b/crates/ide/src/references/rename.rs
@@ -1,29 +1,30 @@
1//! FIXME: write short doc here 1//! FIXME: write short doc here
2use std::{ 2use std::{
3 convert::TryInto, 3 convert::TryInto,
4 error::Error,
5 fmt::{self, Display}, 4 fmt::{self, Display},
6}; 5};
7 6
8use hir::{Module, ModuleDef, ModuleSource, Semantics}; 7use hir::{Module, ModuleDef, ModuleSource, Semantics};
9use ide_db::base_db::{AnchoredPathBuf, FileId, FileRange, SourceDatabaseExt};
10use ide_db::{ 8use ide_db::{
9 base_db::{AnchoredPathBuf, FileId, FileRange, SourceDatabaseExt},
11 defs::{Definition, NameClass, NameRefClass}, 10 defs::{Definition, NameClass, NameRefClass},
11 search::FileReference,
12 RootDatabase, 12 RootDatabase,
13}; 13};
14use syntax::{ 14use syntax::{
15 algo::find_node_at_offset, 15 algo::find_node_at_offset,
16 ast::{self, NameOwner}, 16 ast::{self, NameOwner},
17 lex_single_syntax_kind, match_ast, AstNode, SyntaxKind, SyntaxNode, SyntaxToken, 17 lex_single_syntax_kind, match_ast, AstNode, SyntaxKind, SyntaxNode, SyntaxToken, T,
18}; 18};
19use test_utils::mark; 19use test_utils::mark;
20use text_edit::TextEdit; 20use text_edit::TextEdit;
21 21
22use crate::{ 22use crate::{
23 references::find_all_refs, FilePosition, FileSystemEdit, RangeInfo, Reference, ReferenceKind, 23 FilePosition, FileSystemEdit, RangeInfo, ReferenceKind, ReferenceSearchResult, SourceChange,
24 SourceChange, SourceFileEdit, TextRange, TextSize, 24 SourceFileEdit, TextRange, TextSize,
25}; 25};
26 26
27type RenameResult<T> = Result<T, RenameError>;
27#[derive(Debug)] 28#[derive(Debug)]
28pub struct RenameError(pub(crate) String); 29pub struct RenameError(pub(crate) String);
29 30
@@ -33,26 +34,30 @@ impl fmt::Display for RenameError {
33 } 34 }
34} 35}
35 36
36impl Error for RenameError {} 37macro_rules! format_err {
38 ($fmt:expr) => {RenameError(format!($fmt))};
39 ($fmt:expr, $($arg:tt)+) => {RenameError(format!($fmt, $($arg)+))}
40}
41
42macro_rules! bail {
43 ($($tokens:tt)*) => {return Err(format_err!($($tokens)*))}
44}
37 45
38pub(crate) fn prepare_rename( 46pub(crate) fn prepare_rename(
39 db: &RootDatabase, 47 db: &RootDatabase,
40 position: FilePosition, 48 position: FilePosition,
41) -> Result<RangeInfo<()>, RenameError> { 49) -> RenameResult<RangeInfo<()>> {
42 let sema = Semantics::new(db); 50 let sema = Semantics::new(db);
43 let source_file = sema.parse(position.file_id); 51 let source_file = sema.parse(position.file_id);
44 let syntax = source_file.syntax(); 52 let syntax = source_file.syntax();
45 if let Some(module) = find_module_at_offset(&sema, position, syntax) { 53 if let Some(module) = find_module_at_offset(&sema, position, syntax) {
46 rename_mod(&sema, position, module, "dummy") 54 rename_mod(&sema, position, module, "dummy")
47 } else if let Some(self_token) = 55 } else if let Some(self_token) =
48 syntax.token_at_offset(position.offset).find(|t| t.kind() == SyntaxKind::SELF_KW) 56 syntax.token_at_offset(position.offset).find(|t| t.kind() == T![self])
49 { 57 {
50 rename_self_to_param(&sema, position, self_token, "dummy") 58 rename_self_to_param(&sema, position, self_token, "dummy")
51 } else { 59 } else {
52 let range = match find_all_refs(&sema, position, None) { 60 let RangeInfo { range, .. } = find_all_refs(&sema, position)?;
53 Some(RangeInfo { range, .. }) => range,
54 None => return Err(RenameError("No references found at position".to_string())),
55 };
56 Ok(RangeInfo::new(range, SourceChange::from(vec![]))) 61 Ok(RangeInfo::new(range, SourceChange::from(vec![])))
57 } 62 }
58 .map(|info| RangeInfo::new(info.range, ())) 63 .map(|info| RangeInfo::new(info.range, ()))
@@ -62,7 +67,7 @@ pub(crate) fn rename(
62 db: &RootDatabase, 67 db: &RootDatabase,
63 position: FilePosition, 68 position: FilePosition,
64 new_name: &str, 69 new_name: &str,
65) -> Result<RangeInfo<SourceChange>, RenameError> { 70) -> RenameResult<RangeInfo<SourceChange>> {
66 let sema = Semantics::new(db); 71 let sema = Semantics::new(db);
67 rename_with_semantics(&sema, position, new_name) 72 rename_with_semantics(&sema, position, new_name)
68} 73}
@@ -71,42 +76,18 @@ pub(crate) fn rename_with_semantics(
71 sema: &Semantics<RootDatabase>, 76 sema: &Semantics<RootDatabase>,
72 position: FilePosition, 77 position: FilePosition,
73 new_name: &str, 78 new_name: &str,
74) -> Result<RangeInfo<SourceChange>, RenameError> { 79) -> RenameResult<RangeInfo<SourceChange>> {
75 let is_lifetime_name = match lex_single_syntax_kind(new_name) {
76 Some(res) => match res {
77 (SyntaxKind::IDENT, _) => false,
78 (SyntaxKind::UNDERSCORE, _) => false,
79 (SyntaxKind::SELF_KW, _) => return rename_to_self(&sema, position),
80 (SyntaxKind::LIFETIME_IDENT, _) if new_name != "'static" && new_name != "'_" => true,
81 (SyntaxKind::LIFETIME_IDENT, _) => {
82 return Err(RenameError(format!(
83 "Invalid name `{0}`: Cannot rename lifetime to {0}",
84 new_name
85 )))
86 }
87 (_, Some(syntax_error)) => {
88 return Err(RenameError(format!("Invalid name `{}`: {}", new_name, syntax_error)))
89 }
90 (_, None) => {
91 return Err(RenameError(format!("Invalid name `{}`: not an identifier", new_name)))
92 }
93 },
94 None => return Err(RenameError(format!("Invalid name `{}`: not an identifier", new_name))),
95 };
96
97 let source_file = sema.parse(position.file_id); 80 let source_file = sema.parse(position.file_id);
98 let syntax = source_file.syntax(); 81 let syntax = source_file.syntax();
99 // this is here to prevent lifetime renames from happening on modules and self 82
100 if is_lifetime_name { 83 if let Some(module) = find_module_at_offset(&sema, position, syntax) {
101 rename_reference(&sema, position, new_name, is_lifetime_name)
102 } else if let Some(module) = find_module_at_offset(&sema, position, syntax) {
103 rename_mod(&sema, position, module, new_name) 84 rename_mod(&sema, position, module, new_name)
104 } else if let Some(self_token) = 85 } else if let Some(self_token) =
105 syntax.token_at_offset(position.offset).find(|t| t.kind() == SyntaxKind::SELF_KW) 86 syntax.token_at_offset(position.offset).find(|t| t.kind() == T![self])
106 { 87 {
107 rename_self_to_param(&sema, position, self_token, new_name) 88 rename_self_to_param(&sema, position, self_token, new_name)
108 } else { 89 } else {
109 rename_reference(&sema, position, new_name, is_lifetime_name) 90 rename_reference(&sema, position, new_name)
110 } 91 }
111} 92}
112 93
@@ -127,6 +108,33 @@ pub(crate) fn will_rename_file(
127 Some(change) 108 Some(change)
128} 109}
129 110
111#[derive(Debug, PartialEq)]
112enum IdentifierKind {
113 Ident,
114 Lifetime,
115 ToSelf,
116 Underscore,
117}
118
119fn check_identifier(new_name: &str) -> RenameResult<IdentifierKind> {
120 match lex_single_syntax_kind(new_name) {
121 Some(res) => match res {
122 (SyntaxKind::IDENT, _) => Ok(IdentifierKind::Ident),
123 (T![_], _) => Ok(IdentifierKind::Underscore),
124 (T![self], _) => Ok(IdentifierKind::ToSelf),
125 (SyntaxKind::LIFETIME_IDENT, _) if new_name != "'static" && new_name != "'_" => {
126 Ok(IdentifierKind::Lifetime)
127 }
128 (SyntaxKind::LIFETIME_IDENT, _) => {
129 bail!("Invalid name `{0}`: Cannot rename lifetime to {0}", new_name)
130 }
131 (_, Some(syntax_error)) => bail!("Invalid name `{}`: {}", new_name, syntax_error),
132 (_, None) => bail!("Invalid name `{}`: not an identifier", new_name),
133 },
134 None => bail!("Invalid name `{}`: not an identifier", new_name),
135 }
136}
137
130fn find_module_at_offset( 138fn find_module_at_offset(
131 sema: &Semantics<RootDatabase>, 139 sema: &Semantics<RootDatabase>,
132 position: FilePosition, 140 position: FilePosition,
@@ -155,39 +163,54 @@ fn find_module_at_offset(
155 Some(module) 163 Some(module)
156} 164}
157 165
158fn source_edit_from_reference( 166fn find_all_refs(
167 sema: &Semantics<RootDatabase>,
168 position: FilePosition,
169) -> RenameResult<RangeInfo<ReferenceSearchResult>> {
170 crate::references::find_all_refs(sema, position, None)
171 .ok_or_else(|| format_err!("No references found at position"))
172}
173
174fn source_edit_from_references(
159 sema: &Semantics<RootDatabase>, 175 sema: &Semantics<RootDatabase>,
160 reference: Reference, 176 file_id: FileId,
177 references: &[FileReference],
161 new_name: &str, 178 new_name: &str,
162) -> SourceFileEdit { 179) -> SourceFileEdit {
163 let mut replacement_text = String::new(); 180 let mut edit = TextEdit::builder();
164 let range = match reference.kind { 181 for reference in references {
165 ReferenceKind::FieldShorthandForField => { 182 let mut replacement_text = String::new();
166 mark::hit!(test_rename_struct_field_for_shorthand); 183 let range = match reference.kind {
167 replacement_text.push_str(new_name); 184 ReferenceKind::FieldShorthandForField => {
168 replacement_text.push_str(": "); 185 mark::hit!(test_rename_struct_field_for_shorthand);
169 TextRange::new(reference.file_range.range.start(), reference.file_range.range.start()) 186 replacement_text.push_str(new_name);
170 } 187 replacement_text.push_str(": ");
171 ReferenceKind::FieldShorthandForLocal => { 188 TextRange::new(reference.range.start(), reference.range.start())
172 mark::hit!(test_rename_local_for_field_shorthand); 189 }
173 replacement_text.push_str(": "); 190 ReferenceKind::FieldShorthandForLocal => {
174 replacement_text.push_str(new_name); 191 mark::hit!(test_rename_local_for_field_shorthand);
175 TextRange::new(reference.file_range.range.end(), reference.file_range.range.end()) 192 replacement_text.push_str(": ");
176 } 193 replacement_text.push_str(new_name);
177 ReferenceKind::RecordFieldExprOrPat => { 194 TextRange::new(reference.range.end(), reference.range.end())
178 mark::hit!(test_rename_field_expr_pat); 195 }
179 replacement_text.push_str(new_name); 196 ReferenceKind::RecordFieldExprOrPat => {
180 edit_text_range_for_record_field_expr_or_pat(sema, reference.file_range, new_name) 197 mark::hit!(test_rename_field_expr_pat);
181 } 198 replacement_text.push_str(new_name);
182 _ => { 199 edit_text_range_for_record_field_expr_or_pat(
183 replacement_text.push_str(new_name); 200 sema,
184 reference.file_range.range 201 FileRange { file_id, range: reference.range },
185 } 202 new_name,
186 }; 203 )
187 SourceFileEdit { 204 }
188 file_id: reference.file_range.file_id, 205 _ => {
189 edit: TextEdit::replace(range, replacement_text), 206 replacement_text.push_str(new_name);
207 reference.range
208 }
209 };
210 edit.replace(range, replacement_text);
190 } 211 }
212
213 SourceFileEdit { file_id, edit: edit.finish() }
191} 214}
192 215
193fn edit_text_range_for_record_field_expr_or_pat( 216fn edit_text_range_for_record_field_expr_or_pat(
@@ -223,7 +246,10 @@ fn rename_mod(
223 position: FilePosition, 246 position: FilePosition,
224 module: Module, 247 module: Module,
225 new_name: &str, 248 new_name: &str,
226) -> Result<RangeInfo<SourceChange>, RenameError> { 249) -> RenameResult<RangeInfo<SourceChange>> {
250 if IdentifierKind::Ident != check_identifier(new_name)? {
251 bail!("Invalid name `{0}`: cannot rename module to {0}", new_name);
252 }
227 let mut source_file_edits = Vec::new(); 253 let mut source_file_edits = Vec::new();
228 let mut file_system_edits = Vec::new(); 254 let mut file_system_edits = Vec::new();
229 255
@@ -254,12 +280,10 @@ fn rename_mod(
254 source_file_edits.push(edit); 280 source_file_edits.push(edit);
255 } 281 }
256 282
257 let RangeInfo { range, info: refs } = find_all_refs(sema, position, None) 283 let RangeInfo { range, info: refs } = find_all_refs(sema, position)?;
258 .ok_or_else(|| RenameError("No references found at position".to_string()))?; 284 let ref_edits = refs.references().iter().map(|(&file_id, references)| {
259 let ref_edits = refs 285 source_edit_from_references(sema, file_id, references, new_name)
260 .references 286 });
261 .into_iter()
262 .map(|reference| source_edit_from_reference(sema, reference, new_name));
263 source_file_edits.extend(ref_edits); 287 source_file_edits.extend(ref_edits);
264 288
265 Ok(RangeInfo::new(range, SourceChange::from_edits(source_file_edits, file_system_edits))) 289 Ok(RangeInfo::new(range, SourceChange::from_edits(source_file_edits, file_system_edits)))
@@ -274,27 +298,26 @@ fn rename_to_self(
274 298
275 let (fn_def, fn_ast) = find_node_at_offset::<ast::Fn>(syn, position.offset) 299 let (fn_def, fn_ast) = find_node_at_offset::<ast::Fn>(syn, position.offset)
276 .and_then(|fn_ast| sema.to_def(&fn_ast).zip(Some(fn_ast))) 300 .and_then(|fn_ast| sema.to_def(&fn_ast).zip(Some(fn_ast)))
277 .ok_or_else(|| RenameError("No surrounding method declaration found".to_string()))?; 301 .ok_or_else(|| format_err!("No surrounding method declaration found"))?;
278 let param_range = fn_ast 302 let param_range = fn_ast
279 .param_list() 303 .param_list()
280 .and_then(|p| p.params().next()) 304 .and_then(|p| p.params().next())
281 .ok_or_else(|| RenameError("Method has no parameters".to_string()))? 305 .ok_or_else(|| format_err!("Method has no parameters"))?
282 .syntax() 306 .syntax()
283 .text_range(); 307 .text_range();
284 if !param_range.contains(position.offset) { 308 if !param_range.contains(position.offset) {
285 return Err(RenameError("Only the first parameter can be self".to_string())); 309 bail!("Only the first parameter can be self");
286 } 310 }
287 311
288 let impl_block = find_node_at_offset::<ast::Impl>(syn, position.offset) 312 let impl_block = find_node_at_offset::<ast::Impl>(syn, position.offset)
289 .and_then(|def| sema.to_def(&def)) 313 .and_then(|def| sema.to_def(&def))
290 .ok_or_else(|| RenameError("No impl block found for function".to_string()))?; 314 .ok_or_else(|| format_err!("No impl block found for function"))?;
291 if fn_def.self_param(sema.db).is_some() { 315 if fn_def.self_param(sema.db).is_some() {
292 return Err(RenameError("Method already has a self parameter".to_string())); 316 bail!("Method already has a self parameter");
293 } 317 }
294 318
295 let params = fn_def.assoc_fn_params(sema.db); 319 let params = fn_def.assoc_fn_params(sema.db);
296 let first_param = 320 let first_param = params.first().ok_or_else(|| format_err!("Method has no parameters"))?;
297 params.first().ok_or_else(|| RenameError("Method has no parameters".into()))?;
298 let first_param_ty = first_param.ty(); 321 let first_param_ty = first_param.ty();
299 let impl_ty = impl_block.target_ty(sema.db); 322 let impl_ty = impl_block.target_ty(sema.db);
300 let (ty, self_param) = if impl_ty.remove_ref().is_some() { 323 let (ty, self_param) = if impl_ty.remove_ref().is_some() {
@@ -307,23 +330,17 @@ fn rename_to_self(
307 }; 330 };
308 331
309 if ty != impl_ty { 332 if ty != impl_ty {
310 return Err(RenameError("Parameter type differs from impl block type".to_string())); 333 bail!("Parameter type differs from impl block type");
311 } 334 }
312 335
313 let RangeInfo { range, info: refs } = find_all_refs(sema, position, None) 336 let RangeInfo { range, info: refs } = find_all_refs(sema, position)?;
314 .ok_or_else(|| RenameError("No reference found at position".to_string()))?;
315
316 let (param_ref, usages): (Vec<Reference>, Vec<Reference>) = refs
317 .into_iter()
318 .partition(|reference| param_range.intersect(reference.file_range.range).is_some());
319
320 if param_ref.is_empty() {
321 return Err(RenameError("Parameter to rename not found".to_string()));
322 }
323 337
324 let mut edits = usages 338 let mut edits = refs
325 .into_iter() 339 .references()
326 .map(|reference| source_edit_from_reference(sema, reference, "self")) 340 .iter()
341 .map(|(&file_id, references)| {
342 source_edit_from_references(sema, file_id, references, "self")
343 })
327 .collect::<Vec<_>>(); 344 .collect::<Vec<_>>();
328 345
329 edits.push(SourceFileEdit { 346 edits.push(SourceFileEdit {
@@ -367,12 +384,22 @@ fn rename_self_to_param(
367 self_token: SyntaxToken, 384 self_token: SyntaxToken,
368 new_name: &str, 385 new_name: &str,
369) -> Result<RangeInfo<SourceChange>, RenameError> { 386) -> Result<RangeInfo<SourceChange>, RenameError> {
387 let ident_kind = check_identifier(new_name)?;
388 match ident_kind {
389 IdentifierKind::Lifetime => bail!("Invalid name `{}`: not an identifier", new_name),
390 IdentifierKind::ToSelf => {
391 // no-op
392 mark::hit!(rename_self_to_self);
393 return Ok(RangeInfo::new(self_token.text_range(), SourceChange::default()));
394 }
395 _ => (),
396 }
370 let source_file = sema.parse(position.file_id); 397 let source_file = sema.parse(position.file_id);
371 let syn = source_file.syntax(); 398 let syn = source_file.syntax();
372 399
373 let text = sema.db.file_text(position.file_id); 400 let text = sema.db.file_text(position.file_id);
374 let fn_def = find_node_at_offset::<ast::Fn>(syn, position.offset) 401 let fn_def = find_node_at_offset::<ast::Fn>(syn, position.offset)
375 .ok_or_else(|| RenameError("No surrounding method declaration found".to_string()))?; 402 .ok_or_else(|| format_err!("No surrounding method declaration found"))?;
376 let search_range = fn_def.syntax().text_range(); 403 let search_range = fn_def.syntax().text_range();
377 404
378 let mut edits: Vec<SourceFileEdit> = vec![]; 405 let mut edits: Vec<SourceFileEdit> = vec![];
@@ -382,12 +409,10 @@ fn rename_self_to_param(
382 if !search_range.contains_inclusive(offset) { 409 if !search_range.contains_inclusive(offset) {
383 continue; 410 continue;
384 } 411 }
385 if let Some(ref usage) = 412 if let Some(ref usage) = syn.token_at_offset(offset).find(|t| t.kind() == T![self]) {
386 syn.token_at_offset(offset).find(|t| t.kind() == SyntaxKind::SELF_KW)
387 {
388 let edit = if let Some(ref self_param) = ast::SelfParam::cast(usage.parent()) { 413 let edit = if let Some(ref self_param) = ast::SelfParam::cast(usage.parent()) {
389 text_edit_from_self_param(syn, self_param, new_name) 414 text_edit_from_self_param(syn, self_param, new_name)
390 .ok_or_else(|| RenameError("No target type found".to_string()))? 415 .ok_or_else(|| format_err!("No target type found"))?
391 } else { 416 } else {
392 TextEdit::replace(usage.text_range(), String::from(new_name)) 417 TextEdit::replace(usage.text_range(), String::from(new_name))
393 }; 418 };
@@ -395,6 +420,10 @@ fn rename_self_to_param(
395 } 420 }
396 } 421 }
397 422
423 if edits.len() > 1 && ident_kind == IdentifierKind::Underscore {
424 bail!("Cannot rename reference to `_` as it is being referenced multiple times");
425 }
426
398 let range = ast::SelfParam::cast(self_token.parent()) 427 let range = ast::SelfParam::cast(self_token.parent())
399 .map_or(self_token.text_range(), |p| p.syntax().text_range()); 428 .map_or(self_token.text_range(), |p| p.syntax().text_range());
400 429
@@ -405,35 +434,43 @@ fn rename_reference(
405 sema: &Semantics<RootDatabase>, 434 sema: &Semantics<RootDatabase>,
406 position: FilePosition, 435 position: FilePosition,
407 new_name: &str, 436 new_name: &str,
408 is_lifetime_name: bool,
409) -> Result<RangeInfo<SourceChange>, RenameError> { 437) -> Result<RangeInfo<SourceChange>, RenameError> {
410 let RangeInfo { range, info: refs } = match find_all_refs(sema, position, None) { 438 let ident_kind = check_identifier(new_name)?;
411 Some(range_info) => range_info, 439 let RangeInfo { range, info: refs } = find_all_refs(sema, position)?;
412 None => return Err(RenameError("No references found at position".to_string())), 440
413 }; 441 match (ident_kind, &refs.declaration.kind) {
414 442 (IdentifierKind::ToSelf, ReferenceKind::Lifetime)
415 match (refs.declaration.kind == ReferenceKind::Lifetime, is_lifetime_name) { 443 | (IdentifierKind::Underscore, ReferenceKind::Lifetime)
416 (true, false) => { 444 | (IdentifierKind::Ident, ReferenceKind::Lifetime) => {
417 return Err(RenameError(format!( 445 mark::hit!(rename_not_a_lifetime_ident_ref);
418 "Invalid name `{}`: not a lifetime identifier", 446 bail!("Invalid name `{}`: not a lifetime identifier", new_name)
419 new_name
420 )))
421 } 447 }
422 (false, true) => { 448 (IdentifierKind::Lifetime, ReferenceKind::Lifetime) => mark::hit!(rename_lifetime),
423 return Err(RenameError(format!("Invalid name `{}`: not an identifier", new_name))) 449 (IdentifierKind::Lifetime, _) => {
450 mark::hit!(rename_not_an_ident_ref);
451 bail!("Invalid name `{}`: not an identifier", new_name)
424 } 452 }
425 _ => (), 453 (IdentifierKind::ToSelf, ReferenceKind::SelfKw) => {
454 unreachable!("rename_self_to_param should've been called instead")
455 }
456 (IdentifierKind::ToSelf, _) => {
457 mark::hit!(rename_to_self);
458 return rename_to_self(sema, position);
459 }
460 (IdentifierKind::Underscore, _) if !refs.references.is_empty() => {
461 mark::hit!(rename_underscore_multiple);
462 bail!("Cannot rename reference to `_` as it is being referenced multiple times")
463 }
464 (IdentifierKind::Ident, _) | (IdentifierKind::Underscore, _) => mark::hit!(rename_ident),
426 } 465 }
427 466
428 let edit = refs 467 let edit = refs
429 .into_iter() 468 .into_iter()
430 .map(|reference| source_edit_from_reference(sema, reference, new_name)) 469 .map(|(file_id, references)| {
470 source_edit_from_references(sema, file_id, &references, new_name)
471 })
431 .collect::<Vec<_>>(); 472 .collect::<Vec<_>>();
432 473
433 if edit.is_empty() {
434 return Err(RenameError("No references found at position".to_string()));
435 }
436
437 Ok(RangeInfo::new(range, SourceChange::from(edit))) 474 Ok(RangeInfo::new(range, SourceChange::from(edit)))
438} 475}
439 476
@@ -462,9 +499,11 @@ mod tests {
462 text_edit_builder.replace(indel.delete, indel.insert); 499 text_edit_builder.replace(indel.delete, indel.insert);
463 } 500 }
464 } 501 }
465 let mut result = analysis.file_text(file_id.unwrap()).unwrap().to_string(); 502 if let Some(file_id) = file_id {
466 text_edit_builder.finish().apply(&mut result); 503 let mut result = analysis.file_text(file_id).unwrap().to_string();
467 assert_eq_text!(ra_fixture_after, &*result); 504 text_edit_builder.finish().apply(&mut result);
505 assert_eq_text!(ra_fixture_after, &*result);
506 }
468 } 507 }
469 Err(err) => { 508 Err(err) => {
470 if ra_fixture_after.starts_with("error:") { 509 if ra_fixture_after.starts_with("error:") {
@@ -530,6 +569,7 @@ mod tests {
530 569
531 #[test] 570 #[test]
532 fn test_rename_to_invalid_identifier_lifetime() { 571 fn test_rename_to_invalid_identifier_lifetime() {
572 mark::check!(rename_not_an_ident_ref);
533 check( 573 check(
534 "'foo", 574 "'foo",
535 r#"fn main() { let i$0 = 1; }"#, 575 r#"fn main() { let i$0 = 1; }"#,
@@ -539,6 +579,7 @@ mod tests {
539 579
540 #[test] 580 #[test]
541 fn test_rename_to_invalid_identifier_lifetime2() { 581 fn test_rename_to_invalid_identifier_lifetime2() {
582 mark::check!(rename_not_a_lifetime_ident_ref);
542 check( 583 check(
543 "foo", 584 "foo",
544 r#"fn main<'a>(_: &'a$0 ()) {}"#, 585 r#"fn main<'a>(_: &'a$0 ()) {}"#,
@@ -547,7 +588,27 @@ mod tests {
547 } 588 }
548 589
549 #[test] 590 #[test]
591 fn test_rename_to_underscore_invalid() {
592 mark::check!(rename_underscore_multiple);
593 check(
594 "_",
595 r#"fn main(foo$0: ()) {foo;}"#,
596 "error: Cannot rename reference to `_` as it is being referenced multiple times",
597 );
598 }
599
600 #[test]
601 fn test_rename_mod_invalid() {
602 check(
603 "'foo",
604 r#"mod foo$0 {}"#,
605 "error: Invalid name `'foo`: cannot rename module to 'foo",
606 );
607 }
608
609 #[test]
550 fn test_rename_for_local() { 610 fn test_rename_for_local() {
611 mark::check!(rename_ident);
551 check( 612 check(
552 "k", 613 "k",
553 r#" 614 r#"
@@ -945,7 +1006,7 @@ use crate::foo$0::FooContent;
945//- /lib.rs 1006//- /lib.rs
946mod fo$0o; 1007mod fo$0o;
947//- /foo/mod.rs 1008//- /foo/mod.rs
948// emtpy 1009// empty
949"#, 1010"#,
950 expect![[r#" 1011 expect![[r#"
951 RangeInfo { 1012 RangeInfo {
@@ -995,7 +1056,7 @@ mod fo$0o;
995mod outer { mod fo$0o; } 1056mod outer { mod fo$0o; }
996 1057
997//- /outer/foo.rs 1058//- /outer/foo.rs
998// emtpy 1059// empty
999"#, 1060"#,
1000 expect![[r#" 1061 expect![[r#"
1001 RangeInfo { 1062 RangeInfo {
@@ -1178,6 +1239,7 @@ fn foo(f: foo::Foo) {
1178 1239
1179 #[test] 1240 #[test]
1180 fn test_parameter_to_self() { 1241 fn test_parameter_to_self() {
1242 mark::check!(rename_to_self);
1181 check( 1243 check(
1182 "self", 1244 "self",
1183 r#" 1245 r#"
@@ -1481,6 +1543,7 @@ fn foo(Foo { i: bar }: foo) -> i32 {
1481 1543
1482 #[test] 1544 #[test]
1483 fn test_rename_lifetimes() { 1545 fn test_rename_lifetimes() {
1546 mark::check!(rename_lifetime);
1484 check( 1547 check(
1485 "'yeeee", 1548 "'yeeee",
1486 r#" 1549 r#"
@@ -1565,4 +1628,24 @@ fn foo<'a>() -> &'a () {
1565"#, 1628"#,
1566 ) 1629 )
1567 } 1630 }
1631
1632 #[test]
1633 fn test_self_to_self() {
1634 mark::check!(rename_self_to_self);
1635 check(
1636 "self",
1637 r#"
1638struct Foo;
1639impl Foo {
1640 fn foo(self$0) {}
1641}
1642"#,
1643 r#"
1644struct Foo;
1645impl Foo {
1646 fn foo(self) {}
1647}
1648"#,
1649 )
1650 }
1568} 1651}
diff --git a/crates/ide/src/runnables.rs b/crates/ide/src/runnables.rs
index 557563d7e..f5ee7de86 100644
--- a/crates/ide/src/runnables.rs
+++ b/crates/ide/src/runnables.rs
@@ -3,7 +3,7 @@ use std::fmt;
3use assists::utils::test_related_attribute; 3use assists::utils::test_related_attribute;
4use cfg::CfgExpr; 4use cfg::CfgExpr;
5use hir::{AsAssocItem, HasAttrs, HasSource, Semantics}; 5use hir::{AsAssocItem, HasAttrs, HasSource, Semantics};
6use ide_db::RootDatabase; 6use ide_db::{defs::Definition, RootDatabase};
7use itertools::Itertools; 7use itertools::Itertools;
8use syntax::{ 8use syntax::{
9 ast::{self, AstNode, AttrsOwner, ModuleItemOwner}, 9 ast::{self, AstNode, AttrsOwner, ModuleItemOwner},
@@ -96,21 +96,26 @@ impl Runnable {
96pub(crate) fn runnables(db: &RootDatabase, file_id: FileId) -> Vec<Runnable> { 96pub(crate) fn runnables(db: &RootDatabase, file_id: FileId) -> Vec<Runnable> {
97 let sema = Semantics::new(db); 97 let sema = Semantics::new(db);
98 let source_file = sema.parse(file_id); 98 let source_file = sema.parse(file_id);
99 source_file.syntax().descendants().filter_map(|i| runnable(&sema, i)).collect() 99 source_file
100} 100 .syntax()
101 101 .descendants()
102pub(crate) fn runnable(sema: &Semantics<RootDatabase>, item: SyntaxNode) -> Option<Runnable> { 102 .filter_map(|item| {
103 let runnable_item = match_ast! { 103 let runnable = match_ast! {
104 match (item.clone()) { 104 match item {
105 ast::Fn(func) => { 105 ast::Fn(func) => {
106 let def = sema.to_def(&func)?; 106 let def = sema.to_def(&func)?;
107 runnable_fn(sema, def) 107 runnable_fn(&sema, def)
108 }, 108 },
109 ast::Module(it) => runnable_mod(sema, it), 109 ast::Module(it) => runnable_mod(&sema, it),
110 _ => None, 110 _ => None,
111 } 111 }
112 }; 112 };
113 runnable_item.or_else(|| runnable_doctest(sema, item)) 113 runnable.or_else(|| match doc_owner_to_def(&sema, item)? {
114 Definition::ModuleDef(def) => module_def_doctest(&sema, def),
115 _ => None,
116 })
117 })
118 .collect()
114} 119}
115 120
116pub(crate) fn runnable_fn(sema: &Semantics<RootDatabase>, def: hir::Function) -> Option<Runnable> { 121pub(crate) fn runnable_fn(sema: &Semantics<RootDatabase>, def: hir::Function) -> Option<Runnable> {
@@ -145,20 +150,49 @@ pub(crate) fn runnable_fn(sema: &Semantics<RootDatabase>, def: hir::Function) ->
145 Some(Runnable { nav, kind, cfg }) 150 Some(Runnable { nav, kind, cfg })
146} 151}
147 152
148fn runnable_doctest(sema: &Semantics<RootDatabase>, item: SyntaxNode) -> Option<Runnable> { 153pub(crate) fn runnab