diff options
Diffstat (limited to 'crates')
-rw-r--r-- | crates/ide/src/references/rename.rs | 241 | ||||
-rw-r--r-- | crates/ide_assists/src/handlers/unmerge_use.rs | 16 |
2 files changed, 174 insertions, 83 deletions
diff --git a/crates/ide/src/references/rename.rs b/crates/ide/src/references/rename.rs index 50cc1f963..6b3d02bf4 100644 --- a/crates/ide/src/references/rename.rs +++ b/crates/ide/src/references/rename.rs | |||
@@ -5,9 +5,9 @@ | |||
5 | use std::fmt::{self, Display}; | 5 | use std::fmt::{self, Display}; |
6 | 6 | ||
7 | use either::Either; | 7 | use either::Either; |
8 | use hir::{AsAssocItem, InFile, Module, ModuleDef, ModuleSource, Semantics}; | 8 | use hir::{AsAssocItem, FieldSource, HasSource, InFile, ModuleSource, Semantics}; |
9 | use ide_db::{ | 9 | use ide_db::{ |
10 | base_db::{AnchoredPathBuf, FileId}, | 10 | base_db::{AnchoredPathBuf, FileId, FileRange}, |
11 | defs::{Definition, NameClass, NameRefClass}, | 11 | defs::{Definition, NameClass, NameRefClass}, |
12 | search::FileReference, | 12 | search::FileReference, |
13 | RootDatabase, | 13 | RootDatabase, |
@@ -20,7 +20,7 @@ use syntax::{ | |||
20 | 20 | ||
21 | use text_edit::TextEdit; | 21 | use text_edit::TextEdit; |
22 | 22 | ||
23 | use crate::{display::TryToNav, FilePosition, FileSystemEdit, RangeInfo, SourceChange, TextRange}; | 23 | use crate::{FilePosition, FileSystemEdit, RangeInfo, SourceChange, TextRange}; |
24 | 24 | ||
25 | type RenameResult<T> = Result<T, RenameError>; | 25 | type RenameResult<T> = Result<T, RenameError>; |
26 | #[derive(Debug)] | 26 | #[derive(Debug)] |
@@ -52,26 +52,9 @@ pub(crate) fn prepare_rename( | |||
52 | let syntax = source_file.syntax(); | 52 | let syntax = source_file.syntax(); |
53 | 53 | ||
54 | let def = find_definition(&sema, syntax, position)?; | 54 | let def = find_definition(&sema, syntax, position)?; |
55 | match def { | 55 | let frange = def_name_range(&&sema, def) |
56 | Definition::SelfType(_) => bail!("Cannot rename `Self`"), | ||
57 | Definition::ModuleDef(ModuleDef::BuiltinType(_)) => bail!("Cannot rename builtin type"), | ||
58 | Definition::ModuleDef(ModuleDef::Module(_)) => (), | ||
59 | _ => { | ||
60 | let nav = def | ||
61 | .try_to_nav(sema.db) | ||
62 | .ok_or_else(|| format_err!("No references found at position"))?; | ||
63 | nav.focus_range.ok_or_else(|| format_err!("No identifier available to rename"))?; | ||
64 | } | ||
65 | }; | ||
66 | let name_like = sema | ||
67 | .find_node_at_offset_with_descend(syntax, position.offset) | ||
68 | .ok_or_else(|| format_err!("No references found at position"))?; | 56 | .ok_or_else(|| format_err!("No references found at position"))?; |
69 | let node = match &name_like { | 57 | Ok(RangeInfo::new(frange.range, ())) |
70 | ast::NameLike::Name(it) => it.syntax(), | ||
71 | ast::NameLike::NameRef(it) => it.syntax(), | ||
72 | ast::NameLike::Lifetime(it) => it.syntax(), | ||
73 | }; | ||
74 | Ok(RangeInfo::new(sema.original_range(node).range, ())) | ||
75 | } | 58 | } |
76 | 59 | ||
77 | // Feature: Rename | 60 | // Feature: Rename |
@@ -103,10 +86,24 @@ pub(crate) fn rename_with_semantics( | |||
103 | let syntax = source_file.syntax(); | 86 | let syntax = source_file.syntax(); |
104 | 87 | ||
105 | let def = find_definition(sema, syntax, position)?; | 88 | let def = find_definition(sema, syntax, position)?; |
89 | |||
90 | if let Definition::Local(local) = def { | ||
91 | if let Some(self_param) = local.as_self_param(sema.db) { | ||
92 | cov_mark::hit!(rename_self_to_param); | ||
93 | return rename_self_to_param(sema, local, self_param, new_name); | ||
94 | } | ||
95 | if new_name == "self" { | ||
96 | cov_mark::hit!(rename_to_self); | ||
97 | return rename_to_self(sema, local); | ||
98 | } | ||
99 | } | ||
100 | |||
106 | match def { | 101 | match def { |
107 | Definition::ModuleDef(ModuleDef::Module(module)) => rename_mod(sema, module, new_name), | 102 | Definition::ModuleDef(hir::ModuleDef::Module(module)) => rename_mod(sema, module, new_name), |
108 | Definition::SelfType(_) => bail!("Cannot rename `Self`"), | 103 | Definition::SelfType(_) => bail!("Cannot rename `Self`"), |
109 | Definition::ModuleDef(ModuleDef::BuiltinType(_)) => bail!("Cannot rename builtin type"), | 104 | Definition::ModuleDef(hir::ModuleDef::BuiltinType(_)) => { |
105 | bail!("Cannot rename builtin type") | ||
106 | } | ||
110 | def => rename_reference(sema, def, new_name), | 107 | def => rename_reference(sema, def, new_name), |
111 | } | 108 | } |
112 | } | 109 | } |
@@ -128,26 +125,26 @@ pub(crate) fn will_rename_file( | |||
128 | enum IdentifierKind { | 125 | enum IdentifierKind { |
129 | Ident, | 126 | Ident, |
130 | Lifetime, | 127 | Lifetime, |
131 | ToSelf, | ||
132 | Underscore, | 128 | Underscore, |
133 | } | 129 | } |
134 | 130 | ||
135 | fn check_identifier(new_name: &str) -> RenameResult<IdentifierKind> { | 131 | impl IdentifierKind { |
136 | match lex_single_syntax_kind(new_name) { | 132 | fn classify(new_name: &str) -> RenameResult<IdentifierKind> { |
137 | Some(res) => match res { | 133 | match lex_single_syntax_kind(new_name) { |
138 | (SyntaxKind::IDENT, _) => Ok(IdentifierKind::Ident), | 134 | Some(res) => match res { |
139 | (T![_], _) => Ok(IdentifierKind::Underscore), | 135 | (SyntaxKind::IDENT, _) => Ok(IdentifierKind::Ident), |
140 | (T![self], _) => Ok(IdentifierKind::ToSelf), | 136 | (T![_], _) => Ok(IdentifierKind::Underscore), |
141 | (SyntaxKind::LIFETIME_IDENT, _) if new_name != "'static" && new_name != "'_" => { | 137 | (SyntaxKind::LIFETIME_IDENT, _) if new_name != "'static" && new_name != "'_" => { |
142 | Ok(IdentifierKind::Lifetime) | 138 | Ok(IdentifierKind::Lifetime) |
143 | } | 139 | } |
144 | (SyntaxKind::LIFETIME_IDENT, _) => { | 140 | (SyntaxKind::LIFETIME_IDENT, _) => { |
145 | bail!("Invalid name `{}`: not a lifetime identifier", new_name) | 141 | bail!("Invalid name `{}`: not a lifetime identifier", new_name) |
146 | } | 142 | } |
147 | (_, Some(syntax_error)) => bail!("Invalid name `{}`: {}", new_name, syntax_error), | 143 | (_, Some(syntax_error)) => bail!("Invalid name `{}`: {}", new_name, syntax_error), |
148 | (_, None) => bail!("Invalid name `{}`: not an identifier", new_name), | 144 | (_, None) => bail!("Invalid name `{}`: not an identifier", new_name), |
149 | }, | 145 | }, |
150 | None => bail!("Invalid name `{}`: not an identifier", new_name), | 146 | None => bail!("Invalid name `{}`: not an identifier", new_name), |
147 | } | ||
151 | } | 148 | } |
152 | } | 149 | } |
153 | 150 | ||
@@ -194,10 +191,10 @@ fn find_definition( | |||
194 | 191 | ||
195 | fn rename_mod( | 192 | fn rename_mod( |
196 | sema: &Semantics<RootDatabase>, | 193 | sema: &Semantics<RootDatabase>, |
197 | module: Module, | 194 | module: hir::Module, |
198 | new_name: &str, | 195 | new_name: &str, |
199 | ) -> RenameResult<SourceChange> { | 196 | ) -> RenameResult<SourceChange> { |
200 | if IdentifierKind::Ident != check_identifier(new_name)? { | 197 | if IdentifierKind::classify(new_name)? != IdentifierKind::Ident { |
201 | bail!("Invalid name `{0}`: cannot rename module to {0}", new_name); | 198 | bail!("Invalid name `{0}`: cannot rename module to {0}", new_name); |
202 | } | 199 | } |
203 | 200 | ||
@@ -227,7 +224,7 @@ fn rename_mod( | |||
227 | _ => never!("Module source node is missing a name"), | 224 | _ => never!("Module source node is missing a name"), |
228 | } | 225 | } |
229 | } | 226 | } |
230 | let def = Definition::ModuleDef(ModuleDef::Module(module)); | 227 | let def = Definition::ModuleDef(hir::ModuleDef::Module(module)); |
231 | let usages = def.usages(sema).all(); | 228 | let usages = def.usages(sema).all(); |
232 | let ref_edits = usages.iter().map(|(&file_id, references)| { | 229 | let ref_edits = usages.iter().map(|(&file_id, references)| { |
233 | (file_id, source_edit_from_references(references, def, new_name)) | 230 | (file_id, source_edit_from_references(references, def, new_name)) |
@@ -242,14 +239,14 @@ fn rename_reference( | |||
242 | mut def: Definition, | 239 | mut def: Definition, |
243 | new_name: &str, | 240 | new_name: &str, |
244 | ) -> RenameResult<SourceChange> { | 241 | ) -> RenameResult<SourceChange> { |
245 | let ident_kind = check_identifier(new_name)?; | 242 | let ident_kind = IdentifierKind::classify(new_name)?; |
246 | 243 | ||
247 | if matches!( | 244 | if matches!( |
248 | def, // is target a lifetime? | 245 | def, // is target a lifetime? |
249 | Definition::GenericParam(hir::GenericParam::LifetimeParam(_)) | Definition::Label(_) | 246 | Definition::GenericParam(hir::GenericParam::LifetimeParam(_)) | Definition::Label(_) |
250 | ) { | 247 | ) { |
251 | match ident_kind { | 248 | match ident_kind { |
252 | IdentifierKind::Ident | IdentifierKind::ToSelf | IdentifierKind::Underscore => { | 249 | IdentifierKind::Ident | IdentifierKind::Underscore => { |
253 | cov_mark::hit!(rename_not_a_lifetime_ident_ref); | 250 | cov_mark::hit!(rename_not_a_lifetime_ident_ref); |
254 | bail!("Invalid name `{}`: not a lifetime identifier", new_name); | 251 | bail!("Invalid name `{}`: not a lifetime identifier", new_name); |
255 | } | 252 | } |
@@ -261,25 +258,6 @@ fn rename_reference( | |||
261 | cov_mark::hit!(rename_not_an_ident_ref); | 258 | cov_mark::hit!(rename_not_an_ident_ref); |
262 | bail!("Invalid name `{}`: not an identifier", new_name); | 259 | bail!("Invalid name `{}`: not an identifier", new_name); |
263 | } | 260 | } |
264 | (IdentifierKind::ToSelf, Definition::Local(local)) => { | ||
265 | if local.is_self(sema.db) { | ||
266 | // no-op | ||
267 | cov_mark::hit!(rename_self_to_self); | ||
268 | return Ok(SourceChange::default()); | ||
269 | } else { | ||
270 | cov_mark::hit!(rename_to_self); | ||
271 | return rename_to_self(sema, local); | ||
272 | } | ||
273 | } | ||
274 | (ident_kind, Definition::Local(local)) => { | ||
275 | if let Some(self_param) = local.as_self_param(sema.db) { | ||
276 | cov_mark::hit!(rename_self_to_param); | ||
277 | return rename_self_to_param(sema, local, self_param, new_name, ident_kind); | ||
278 | } else { | ||
279 | cov_mark::hit!(rename_local); | ||
280 | } | ||
281 | } | ||
282 | (IdentifierKind::ToSelf, _) => bail!("Invalid name `{}`: not an identifier", new_name), | ||
283 | (IdentifierKind::Ident, _) => cov_mark::hit!(rename_non_local), | 261 | (IdentifierKind::Ident, _) => cov_mark::hit!(rename_non_local), |
284 | (IdentifierKind::Underscore, _) => (), | 262 | (IdentifierKind::Underscore, _) => (), |
285 | } | 263 | } |
@@ -293,21 +271,21 @@ fn rename_reference( | |||
293 | .and_then(|it| it.containing_trait_impl(sema.db)) | 271 | .and_then(|it| it.containing_trait_impl(sema.db)) |
294 | .and_then(|it| { | 272 | .and_then(|it| { |
295 | it.items(sema.db).into_iter().find_map(|it| match (it, mod_def) { | 273 | it.items(sema.db).into_iter().find_map(|it| match (it, mod_def) { |
296 | (hir::AssocItem::Function(trait_func), ModuleDef::Function(func)) | 274 | (hir::AssocItem::Function(trait_func), hir::ModuleDef::Function(func)) |
297 | if trait_func.name(sema.db) == func.name(sema.db) => | 275 | if trait_func.name(sema.db) == func.name(sema.db) => |
298 | { | 276 | { |
299 | Some(Definition::ModuleDef(ModuleDef::Function(trait_func))) | 277 | Some(Definition::ModuleDef(hir::ModuleDef::Function(trait_func))) |
300 | } | 278 | } |
301 | (hir::AssocItem::Const(trait_konst), ModuleDef::Const(konst)) | 279 | (hir::AssocItem::Const(trait_konst), hir::ModuleDef::Const(konst)) |
302 | if trait_konst.name(sema.db) == konst.name(sema.db) => | 280 | if trait_konst.name(sema.db) == konst.name(sema.db) => |
303 | { | 281 | { |
304 | Some(Definition::ModuleDef(ModuleDef::Const(trait_konst))) | 282 | Some(Definition::ModuleDef(hir::ModuleDef::Const(trait_konst))) |
305 | } | 283 | } |
306 | ( | 284 | ( |
307 | hir::AssocItem::TypeAlias(trait_type_alias), | 285 | hir::AssocItem::TypeAlias(trait_type_alias), |
308 | ModuleDef::TypeAlias(type_alias), | 286 | hir::ModuleDef::TypeAlias(type_alias), |
309 | ) if trait_type_alias.name(sema.db) == type_alias.name(sema.db) => { | 287 | ) if trait_type_alias.name(sema.db) == type_alias.name(sema.db) => { |
310 | Some(Definition::ModuleDef(ModuleDef::TypeAlias(trait_type_alias))) | 288 | Some(Definition::ModuleDef(hir::ModuleDef::TypeAlias(trait_type_alias))) |
311 | } | 289 | } |
312 | _ => None, | 290 | _ => None, |
313 | }) | 291 | }) |
@@ -398,8 +376,15 @@ fn rename_self_to_param( | |||
398 | local: hir::Local, | 376 | local: hir::Local, |
399 | self_param: hir::SelfParam, | 377 | self_param: hir::SelfParam, |
400 | new_name: &str, | 378 | new_name: &str, |
401 | identifier_kind: IdentifierKind, | ||
402 | ) -> RenameResult<SourceChange> { | 379 | ) -> RenameResult<SourceChange> { |
380 | if new_name == "self" { | ||
381 | // Let's do nothing rather than complain. | ||
382 | cov_mark::hit!(rename_self_to_self); | ||
383 | return Ok(SourceChange::default()); | ||
384 | } | ||
385 | |||
386 | let identifier_kind = IdentifierKind::classify(new_name)?; | ||
387 | |||
403 | let InFile { file_id, value: self_param } = | 388 | let InFile { file_id, value: self_param } = |
404 | self_param.source(sema.db).ok_or_else(|| format_err!("cannot find function source"))?; | 389 | self_param.source(sema.db).ok_or_else(|| format_err!("cannot find function source"))?; |
405 | 390 | ||
@@ -557,12 +542,11 @@ fn source_edit_from_def( | |||
557 | def: Definition, | 542 | def: Definition, |
558 | new_name: &str, | 543 | new_name: &str, |
559 | ) -> RenameResult<(FileId, TextEdit)> { | 544 | ) -> RenameResult<(FileId, TextEdit)> { |
560 | let nav = | 545 | let frange: FileRange = def_name_range(sema, def) |
561 | def.try_to_nav(sema.db).ok_or_else(|| format_err!("No references found at position"))?; | 546 | .ok_or_else(|| format_err!("No identifier available to rename"))?; |
562 | 547 | ||
563 | let mut replacement_text = String::new(); | 548 | let mut replacement_text = String::new(); |
564 | let mut repl_range = | 549 | let mut repl_range = frange.range; |
565 | nav.focus_range.ok_or_else(|| format_err!("No identifier available to rename"))?; | ||
566 | if let Definition::Local(local) = def { | 550 | if let Definition::Local(local) = def { |
567 | if let Either::Left(pat) = local.source(sema.db).value { | 551 | if let Either::Left(pat) = local.source(sema.db).value { |
568 | if matches!( | 552 | if matches!( |
@@ -582,7 +566,101 @@ fn source_edit_from_def( | |||
582 | replacement_text.push_str(new_name); | 566 | replacement_text.push_str(new_name); |
583 | } | 567 | } |
584 | let edit = TextEdit::replace(repl_range, replacement_text); | 568 | let edit = TextEdit::replace(repl_range, replacement_text); |
585 | Ok((nav.file_id, edit)) | 569 | Ok((frange.file_id, edit)) |
570 | } | ||
571 | |||
572 | fn def_name_range(sema: &Semantics<RootDatabase>, def: Definition) -> Option<FileRange> { | ||
573 | // FIXME: the `original_file_range` calls here are wrong -- they never fail, | ||
574 | // and _fall back_ to the entirety of the macro call. Such fall back is | ||
575 | // incorrect for renames. The safe behavior would be to return an error for | ||
576 | // such cases. The correct behavior would be to return an auxiliary list of | ||
577 | // "can't rename these occurrences in macros" items, and then show some kind | ||
578 | // of a dialog to the user. | ||
579 | |||
580 | let res = match def { | ||
581 | Definition::Macro(mac) => { | ||
582 | let src = mac.source(sema.db)?; | ||
583 | let name = match &src.value { | ||
584 | Either::Left(it) => it.name()?, | ||
585 | Either::Right(it) => it.name()?, | ||
586 | }; | ||
587 | src.with_value(name.syntax()).original_file_range(sema.db) | ||
588 | } | ||
589 | Definition::Field(field) => { | ||
590 | let src = field.source(sema.db)?; | ||
591 | |||
592 | match &src.value { | ||
593 | FieldSource::Named(record_field) => { | ||
594 | let name = record_field.name()?; | ||
595 | src.with_value(name.syntax()).original_file_range(sema.db) | ||
596 | } | ||
597 | FieldSource::Pos(_) => { | ||
598 | return None; | ||
599 | } | ||
600 | } | ||
601 | } | ||
602 | Definition::ModuleDef(module_def) => match module_def { | ||
603 | hir::ModuleDef::Module(module) => { | ||
604 | let src = module.declaration_source(sema.db)?; | ||
605 | let name = src.value.name()?; | ||
606 | src.with_value(name.syntax()).original_file_range(sema.db) | ||
607 | } | ||
608 | hir::ModuleDef::Function(it) => name_range(it, sema)?, | ||
609 | hir::ModuleDef::Adt(adt) => match adt { | ||
610 | hir::Adt::Struct(it) => name_range(it, sema)?, | ||
611 | hir::Adt::Union(it) => name_range(it, sema)?, | ||
612 | hir::Adt::Enum(it) => name_range(it, sema)?, | ||
613 | }, | ||
614 | hir::ModuleDef::Variant(it) => name_range(it, sema)?, | ||
615 | hir::ModuleDef::Const(it) => name_range(it, sema)?, | ||
616 | hir::ModuleDef::Static(it) => name_range(it, sema)?, | ||
617 | hir::ModuleDef::Trait(it) => name_range(it, sema)?, | ||
618 | hir::ModuleDef::TypeAlias(it) => name_range(it, sema)?, | ||
619 | hir::ModuleDef::BuiltinType(_) => return None, | ||
620 | }, | ||
621 | Definition::SelfType(_) => return None, | ||
622 | Definition::Local(local) => { | ||
623 | let src = local.source(sema.db); | ||
624 | let name = match &src.value { | ||
625 | Either::Left(bind_pat) => bind_pat.name()?, | ||
626 | Either::Right(_) => return None, | ||
627 | }; | ||
628 | src.with_value(name.syntax()).original_file_range(sema.db) | ||
629 | } | ||
630 | Definition::GenericParam(generic_param) => match generic_param { | ||
631 | hir::GenericParam::TypeParam(type_param) => { | ||
632 | let src = type_param.source(sema.db)?; | ||
633 | let name = match &src.value { | ||
634 | Either::Left(_) => return None, | ||
635 | Either::Right(type_param) => type_param.name()?, | ||
636 | }; | ||
637 | src.with_value(name.syntax()).original_file_range(sema.db) | ||
638 | } | ||
639 | hir::GenericParam::LifetimeParam(lifetime_param) => { | ||
640 | let src = lifetime_param.source(sema.db)?; | ||
641 | let lifetime = src.value.lifetime()?; | ||
642 | src.with_value(lifetime.syntax()).original_file_range(sema.db) | ||
643 | } | ||
644 | hir::GenericParam::ConstParam(it) => name_range(it, sema)?, | ||
645 | }, | ||
646 | Definition::Label(label) => { | ||
647 | let src = label.source(sema.db); | ||
648 | let lifetime = src.value.lifetime()?; | ||
649 | src.with_value(lifetime.syntax()).original_file_range(sema.db) | ||
650 | } | ||
651 | }; | ||
652 | return Some(res); | ||
653 | |||
654 | fn name_range<D>(def: D, sema: &Semantics<RootDatabase>) -> Option<FileRange> | ||
655 | where | ||
656 | D: HasSource, | ||
657 | D::Ast: ast::NameOwner, | ||
658 | { | ||
659 | let src = def.source(sema.db)?; | ||
660 | let name = src.value.name()?; | ||
661 | let res = src.with_value(name.syntax()).original_file_range(sema.db); | ||
662 | Some(res) | ||
663 | } | ||
586 | } | 664 | } |
587 | 665 | ||
588 | #[cfg(test)] | 666 | #[cfg(test)] |
@@ -659,7 +737,7 @@ mod tests { | |||
659 | fn test_prepare_rename_namelikes() { | 737 | fn test_prepare_rename_namelikes() { |
660 | check_prepare(r"fn name$0<'lifetime>() {}", expect![[r#"3..7: name"#]]); | 738 | check_prepare(r"fn name$0<'lifetime>() {}", expect![[r#"3..7: name"#]]); |
661 | check_prepare(r"fn name<'lifetime$0>() {}", expect![[r#"8..17: 'lifetime"#]]); | 739 | check_prepare(r"fn name<'lifetime$0>() {}", expect![[r#"8..17: 'lifetime"#]]); |
662 | check_prepare(r"fn name<'lifetime>() { name$0(); }", expect![[r#"23..27: name"#]]); | 740 | check_prepare(r"fn name<'lifetime>() { name$0(); }", expect![[r#"3..7: name"#]]); |
663 | } | 741 | } |
664 | 742 | ||
665 | #[test] | 743 | #[test] |
@@ -691,7 +769,7 @@ fn baz() { | |||
691 | x.0$0 = 5; | 769 | x.0$0 = 5; |
692 | } | 770 | } |
693 | "#, | 771 | "#, |
694 | expect![[r#"No identifier available to rename"#]], | 772 | expect![[r#"No references found at position"#]], |
695 | ); | 773 | ); |
696 | } | 774 | } |
697 | 775 | ||
@@ -703,7 +781,7 @@ fn foo() { | |||
703 | let x: i32$0 = 0; | 781 | let x: i32$0 = 0; |
704 | } | 782 | } |
705 | "#, | 783 | "#, |
706 | expect![[r#"Cannot rename builtin type"#]], | 784 | expect![[r#"No references found at position"#]], |
707 | ); | 785 | ); |
708 | } | 786 | } |
709 | 787 | ||
@@ -719,7 +797,7 @@ impl Foo { | |||
719 | } | 797 | } |
720 | } | 798 | } |
721 | "#, | 799 | "#, |
722 | expect![[r#"Cannot rename `Self`"#]], | 800 | expect![[r#"No references found at position"#]], |
723 | ); | 801 | ); |
724 | } | 802 | } |
725 | 803 | ||
@@ -801,7 +879,6 @@ impl Foo { | |||
801 | 879 | ||
802 | #[test] | 880 | #[test] |
803 | fn test_rename_for_local() { | 881 | fn test_rename_for_local() { |
804 | cov_mark::check!(rename_local); | ||
805 | check( | 882 | check( |
806 | "k", | 883 | "k", |
807 | r#" | 884 | r#" |
diff --git a/crates/ide_assists/src/handlers/unmerge_use.rs b/crates/ide_assists/src/handlers/unmerge_use.rs index 8d271e056..14e862cd0 100644 --- a/crates/ide_assists/src/handlers/unmerge_use.rs +++ b/crates/ide_assists/src/handlers/unmerge_use.rs | |||
@@ -73,7 +73,11 @@ fn resolve_full_path(tree: &ast::UseTree) -> Option<ast::Path> { | |||
73 | for path in paths { | 73 | for path in paths { |
74 | final_path = ast::make::path_concat(path, final_path) | 74 | final_path = ast::make::path_concat(path, final_path) |
75 | } | 75 | } |
76 | Some(final_path) | 76 | if final_path.segment().map_or(false, |it| it.self_token().is_some()) { |
77 | final_path.qualifier() | ||
78 | } else { | ||
79 | Some(final_path) | ||
80 | } | ||
77 | } | 81 | } |
78 | 82 | ||
79 | #[cfg(test)] | 83 | #[cfg(test)] |
@@ -223,4 +227,14 @@ pub use std::fmt::Display; | |||
223 | ", | 227 | ", |
224 | ); | 228 | ); |
225 | } | 229 | } |
230 | |||
231 | #[test] | ||
232 | fn unmerge_use_item_on_self() { | ||
233 | check_assist( | ||
234 | unmerge_use, | ||
235 | r"use std::process::{Command, self$0};", | ||
236 | r"use std::process::{Command}; | ||
237 | use std::process;", | ||
238 | ); | ||
239 | } | ||
226 | } | 240 | } |