diff --git a/Cargo.lock b/Cargo.lock index 9498d8bf3e..1c893cd5c9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1817,9 +1817,9 @@ checksum = "56dee185309b50d1f11bfedef0fe6d036842e3fb77413abef29f8f8d1c5d4c1c" [[package]] name = "ungrammar" -version = "1.7.0" +version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7758fccf6038d5c368a17c7224abc85d6508d5ae266d5a3de25faac3cc168509" +checksum = "e33a2183403af89252547c4219a06a6cc8aef6302fee67e10e8431866af3ee72" [[package]] name = "unicase" diff --git a/Cargo.toml b/Cargo.toml index ff0d9e1ce1..46c64d35c2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,5 +1,5 @@ [workspace] -members = [ "xtask/", "lib/*", "crates/*" ] +members = ["xtask/", "lib/*", "crates/*"] [profile.dev] # Disabling debug info speeds up builds a bunch, diff --git a/crates/hir/src/semantics.rs b/crates/hir/src/semantics.rs index cd689c8693..ab213e04c5 100644 --- a/crates/hir/src/semantics.rs +++ b/crates/hir/src/semantics.rs @@ -750,6 +750,7 @@ to_def_impls![ (crate::ConstParam, ast::ConstParam, const_param_to_def), (crate::MacroDef, ast::MacroRules, macro_rules_to_def), (crate::Local, ast::IdentPat, bind_pat_to_def), + (crate::Local, ast::SelfParam, self_param_to_def), (crate::Label, ast::Label, label_to_def), ]; diff --git a/crates/hir/src/semantics/source_to_def.rs b/crates/hir/src/semantics/source_to_def.rs index 4b9ebff729..9bf60c72af 100644 --- a/crates/hir/src/semantics/source_to_def.rs +++ b/crates/hir/src/semantics/source_to_def.rs @@ -114,6 +114,15 @@ impl SourceToDefCtx<'_, '_> { let pat_id = source_map.node_pat(src.as_ref())?; Some((container, pat_id)) } + pub(super) fn self_param_to_def( + &mut self, + src: InFile, + ) -> Option<(DefWithBodyId, PatId)> { + let container = self.find_pat_or_label_container(src.as_ref().map(|it| it.syntax()))?; + let (_body, source_map) = self.db.body_with_source_map(container); + let pat_id = source_map.node_self_param(src.as_ref())?; + Some((container, pat_id)) + } pub(super) fn label_to_def( &mut self, src: InFile, diff --git a/crates/hir_def/src/resolver.rs b/crates/hir_def/src/resolver.rs index 61059c3499..85ddc2c479 100644 --- a/crates/hir_def/src/resolver.rs +++ b/crates/hir_def/src/resolver.rs @@ -258,7 +258,7 @@ impl Resolver { ) -> Option { let n_segments = path.segments.len(); let tmp = name![self]; - let first_name = if path.is_self() { &tmp } else { &path.segments.first()? }; + let first_name = if path.is_self() { &tmp } else { path.segments.first()? }; let skip_to_mod = path.kind != PathKind::Plain && !path.is_self(); for scope in self.scopes.iter().rev() { match scope { diff --git a/crates/ide/src/diagnostics.rs b/crates/ide/src/diagnostics.rs index 2e5395b518..b35bc2bae2 100644 --- a/crates/ide/src/diagnostics.rs +++ b/crates/ide/src/diagnostics.rs @@ -18,7 +18,7 @@ use itertools::Itertools; use rustc_hash::FxHashSet; use syntax::{ ast::{self, AstNode}, - SyntaxNode, TextRange, T, + SyntaxNode, TextRange, }; use text_edit::TextEdit; @@ -232,7 +232,7 @@ fn text_edit_for_remove_unnecessary_braces_with_self_in_use_statement( single_use_tree: &ast::UseTree, ) -> Option { let use_tree_list_node = single_use_tree.syntax().parent()?; - if single_use_tree.path()?.segment()?.syntax().first_child_or_token()?.kind() == T![self] { + if single_use_tree.path()?.segment()?.self_token().is_some() { let start = use_tree_list_node.prev_sibling_or_token()?.text_range().start(); let end = use_tree_list_node.text_range().end(); return Some(TextEdit::delete(TextRange::new(start, end))); diff --git a/crates/ide/src/display/navigation_target.rs b/crates/ide/src/display/navigation_target.rs index 4eecae6975..685052e7f5 100644 --- a/crates/ide/src/display/navigation_target.rs +++ b/crates/ide/src/display/navigation_target.rs @@ -400,24 +400,33 @@ impl TryToNav for hir::GenericParam { impl ToNav for hir::Local { fn to_nav(&self, db: &RootDatabase) -> NavigationTarget { let src = self.source(db); - let node = match &src.value { - Either::Left(bind_pat) => { - bind_pat.name().map_or_else(|| bind_pat.syntax().clone(), |it| it.syntax().clone()) - } - Either::Right(it) => it.syntax().clone(), + let (node, focus_range) = match &src.value { + Either::Left(bind_pat) => ( + bind_pat.syntax().clone(), + bind_pat + .name() + .map(|it| src.with_value(&it.syntax().clone()).original_file_range(db).range), + ), + Either::Right(it) => (it.syntax().clone(), it.self_token().map(|it| it.text_range())), }; let full_range = src.with_value(&node).original_file_range(db); let name = match self.name(db) { Some(it) => it.to_string().into(), None => "".into(), }; - let kind = if self.is_param(db) { SymbolKind::ValueParam } else { SymbolKind::Local }; + let kind = if self.is_self(db) { + SymbolKind::SelfParam + } else if self.is_param(db) { + SymbolKind::ValueParam + } else { + SymbolKind::Local + }; NavigationTarget { file_id: full_range.file_id, name, kind: Some(kind), full_range: full_range.range, - focus_range: None, + focus_range, container_name: None, description: None, docs: None, diff --git a/crates/ide/src/goto_definition.rs b/crates/ide/src/goto_definition.rs index cd4afc8041..988a5668f8 100644 --- a/crates/ide/src/goto_definition.rs +++ b/crates/ide/src/goto_definition.rs @@ -1,7 +1,6 @@ use either::Either; use hir::{HasAttrs, ModuleDef, Semantics}; use ide_db::{ - base_db::FileId, defs::{Definition, NameClass, NameRefClass}, symbol_index, RootDatabase, }; @@ -13,7 +12,7 @@ use crate::{ display::{ToNav, TryToNav}, doc_links::extract_definitions_from_markdown, runnables::doc_owner_to_def, - FilePosition, NavigationTarget, RangeInfo, SymbolKind, + FilePosition, NavigationTarget, RangeInfo, }; // Feature: Go to Definition @@ -49,19 +48,6 @@ pub(crate) fn goto_definition( let nav = def.try_to_nav(sema.db)?; vec![nav] }, - ast::SelfParam(self_param) => { - vec![self_to_nav_target(self_param, position.file_id)?] - }, - ast::PathSegment(segment) => { - segment.self_token()?; - let path = segment.parent_path(); - if path.qualifier().is_some() && !ast::PathExpr::can_cast(path.syntax().parent()?.kind()) { - return None; - } - let func = segment.syntax().ancestors().find_map(ast::Fn::cast)?; - let self_param = func.param_list()?.self_param()?; - vec![self_to_nav_target(self_param, position.file_id)?] - }, ast::Lifetime(lt) => if let Some(name_class) = NameClass::classify_lifetime(&sema, <) { let def = name_class.referenced_or_defined(sema.db); let nav = def.try_to_nav(sema.db)?; @@ -69,6 +55,11 @@ pub(crate) fn goto_definition( } else { reference_definition(&sema, Either::Left(<)).to_vec() }, + ast::SelfParam(self_param) => { + let def = NameClass::classify_self_param(&sema, &self_param)?.referenced_or_defined(sema.db); + let nav = def.try_to_nav(sema.db)?; + vec![nav] + }, _ => return None, } }; @@ -134,20 +125,6 @@ fn pick_best(tokens: TokenAtOffset) -> Option { } } -fn self_to_nav_target(self_param: ast::SelfParam, file_id: FileId) -> Option { - let self_token = self_param.self_token()?; - Some(NavigationTarget { - file_id, - full_range: self_param.syntax().text_range(), - focus_range: Some(self_token.text_range()), - name: self_token.text().clone(), - kind: Some(SymbolKind::SelfParam), - container_name: None, - description: None, - docs: None, - }) -} - #[derive(Debug)] pub(crate) enum ReferenceResult { Exact(NavigationTarget), diff --git a/crates/ide/src/hover.rs b/crates/ide/src/hover.rs index 317b6f0114..6022bd2752 100644 --- a/crates/ide/src/hover.rs +++ b/crates/ide/src/hover.rs @@ -98,6 +98,7 @@ pub(crate) fn hover( ast::NameRef(name_ref) => NameRefClass::classify(&sema, &name_ref).map(|d| d.referenced(sema.db)), ast::Lifetime(lifetime) => NameClass::classify_lifetime(&sema, &lifetime) .map_or_else(|| NameRefClass::classify_lifetime(&sema, &lifetime).map(|d| d.referenced(sema.db)), |d| d.defined(sema.db)), + ast::SelfParam(self_param) => NameClass::classify_self_param(&sema, &self_param).and_then(|d| d.defined(sema.db)), _ => None, } }; @@ -134,17 +135,14 @@ pub(crate) fn hover( return None; } - let node = token.ancestors().find(|n| { - ast::Expr::can_cast(n.kind()) - || ast::Pat::can_cast(n.kind()) - || ast::SelfParam::can_cast(n.kind()) - })?; + let node = token + .ancestors() + .find(|n| ast::Expr::can_cast(n.kind()) || ast::Pat::can_cast(n.kind()))?; let ty = match_ast! { match node { ast::Expr(it) => sema.type_of_expr(&it)?, ast::Pat(it) => sema.type_of_pat(&it)?, - ast::SelfParam(self_param) => sema.type_of_self(&self_param)?, // If this node is a MACRO_CALL, it means that `descend_into_macros` failed to resolve. // (e.g expanding a builtin macro). So we give up here. ast::MacroCall(_it) => return None, @@ -386,7 +384,7 @@ fn pick_best(tokens: TokenAtOffset) -> Option { return tokens.max_by_key(priority); fn priority(n: &SyntaxToken) -> usize { match n.kind() { - IDENT | INT_NUMBER | LIFETIME_IDENT => 3, + IDENT | INT_NUMBER | LIFETIME_IDENT | T![self] => 3, T!['('] | T![')'] => 2, kind if kind.is_trivia() => 0, _ => 1, @@ -3129,6 +3127,39 @@ fn foo(t: T$0){} ); } + #[test] + fn test_hover_self_has_go_to_type() { + check_actions( + r#" +struct Foo; +impl Foo { + fn foo(&self$0) {} +} +"#, + expect![[r#" + [ + GoToType( + [ + HoverGotoTypeData { + mod_path: "test::Foo", + nav: NavigationTarget { + file_id: FileId( + 0, + ), + full_range: 0..11, + focus_range: 7..10, + name: "Foo", + kind: Struct, + description: "struct Foo", + }, + }, + ], + ), + ] + "#]], + ); + } + #[test] fn hover_displays_normalized_crate_names() { check( @@ -3193,6 +3224,7 @@ impl Foo { "#, expect![[r#" *&self* + ```rust &Foo ``` @@ -3212,6 +3244,7 @@ impl Foo { "#, expect![[r#" *self: Arc* + ```rust Arc ``` diff --git a/crates/ide/src/references.rs b/crates/ide/src/references.rs index 7d4757e025..51a2f43272 100644 --- a/crates/ide/src/references.rs +++ b/crates/ide/src/references.rs @@ -11,6 +11,7 @@ pub(crate) mod rename; +use either::Either; use hir::Semantics; use ide_db::{ base_db::FileId, @@ -21,10 +22,10 @@ use ide_db::{ use syntax::{ algo::find_node_at_offset, ast::{self, NameOwner}, - match_ast, AstNode, SyntaxNode, TextRange, TokenAtOffset, T, + AstNode, SyntaxNode, TextRange, TokenAtOffset, T, }; -use crate::{display::TryToNav, FilePosition, FileRange, NavigationTarget, RangeInfo, SymbolKind}; +use crate::{display::TryToNav, FilePosition, FileRange, NavigationTarget, RangeInfo}; #[derive(Debug, Clone)] pub struct ReferenceSearchResult { @@ -90,10 +91,6 @@ pub(crate) fn find_all_refs( let _p = profile::span("find_all_refs"); let syntax = sema.parse(position.file_id).syntax().clone(); - if let Some(res) = try_find_self_references(&syntax, position) { - return Some(res); - } - let (opt_name, search_kind) = if let Some(name) = get_struct_def_name_for_struct_literal_search(&sema, &syntax, position) { @@ -122,13 +119,16 @@ pub(crate) fn find_all_refs( let mut kind = ReferenceKind::Other; if let Definition::Local(local) = def { - if let either::Either::Left(pat) = local.source(sema.db).value { - if matches!( - pat.syntax().parent().and_then(ast::RecordPatField::cast), - Some(pat_field) if pat_field.name_ref().is_none() - ) { - kind = ReferenceKind::FieldShorthandForLocal; + match local.source(sema.db).value { + Either::Left(pat) => { + if matches!( + pat.syntax().parent().and_then(ast::RecordPatField::cast), + Some(pat_field) if pat_field.name_ref().is_none() + ) { + kind = ReferenceKind::FieldShorthandForLocal; + } } + Either::Right(_) => kind = ReferenceKind::SelfParam, } } else if matches!( def, @@ -251,79 +251,6 @@ fn get_enum_def_name_for_struct_literal_search( None } -fn try_find_self_references( - syntax: &SyntaxNode, - position: FilePosition, -) -> Option> { - let FilePosition { file_id, offset } = position; - let self_token = syntax.token_at_offset(offset).find(|t| t.kind() == T![self])?; - let parent = self_token.parent(); - match_ast! { - match parent { - ast::SelfParam(it) => (), - ast::PathSegment(segment) => { - segment.self_token()?; - let path = segment.parent_path(); - if path.qualifier().is_some() && !ast::PathExpr::can_cast(path.syntax().parent()?.kind()) { - return None; - } - }, - _ => return None, - } - }; - let function = parent.ancestors().find_map(ast::Fn::cast)?; - let self_param = function.param_list()?.self_param()?; - let param_self_token = self_param.self_token()?; - - let declaration = Declaration { - nav: NavigationTarget { - file_id, - full_range: self_param.syntax().text_range(), - focus_range: Some(param_self_token.text_range()), - name: param_self_token.text().clone(), - kind: Some(SymbolKind::SelfParam), - container_name: None, - description: None, - docs: None, - }, - kind: ReferenceKind::SelfKw, - access: Some(if self_param.mut_token().is_some() { - ReferenceAccess::Write - } else { - ReferenceAccess::Read - }), - }; - let refs = function - .body() - .map(|body| { - body.syntax() - .descendants() - .filter_map(ast::PathExpr::cast) - .filter_map(|expr| { - let path = expr.path()?; - if path.qualifier().is_none() { - path.segment()?.self_token() - } else { - None - } - }) - .map(|token| FileReference { - range: token.text_range(), - kind: ReferenceKind::SelfKw, - access: declaration.access, // FIXME: properly check access kind here instead of copying it from the declaration - }) - .collect() - }) - .unwrap_or_default(); - let mut references = UsageSearchResult::default(); - references.references.insert(file_id, refs); - - Some(RangeInfo::new( - param_self_token.text_range(), - ReferenceSearchResult { declaration, references }, - )) -} - #[cfg(test)] mod tests { use expect_test::{expect, Expect}; @@ -512,7 +439,7 @@ fn main() { i = 5; }"#, expect![[r#" - i Local FileId(0) 24..25 Other Write + i Local FileId(0) 20..25 24..25 Other Write FileId(0) 50..51 Other Write FileId(0) 54..55 Other Read @@ -536,7 +463,7 @@ fn bar() { } "#, expect![[r#" - spam Local FileId(0) 19..23 Other + spam Local FileId(0) 19..23 19..23 Other FileId(0) 34..38 Other Read FileId(0) 41..45 Other Read @@ -551,7 +478,7 @@ fn bar() { fn foo(i : u32) -> u32 { i$0 } "#, expect![[r#" - i ValueParam FileId(0) 7..8 Other + i ValueParam FileId(0) 7..8 7..8 Other FileId(0) 25..26 Other Read "#]], @@ -565,7 +492,7 @@ fn foo(i : u32) -> u32 { i$0 } fn foo(i$0 : u32) -> u32 { i } "#, expect![[r#" - i ValueParam FileId(0) 7..8 Other + i ValueParam FileId(0) 7..8 7..8 Other FileId(0) 25..26 Other Read "#]], @@ -813,7 +740,7 @@ fn foo() { } "#, expect![[r#" - i Local FileId(0) 23..24 Other Write + i Local FileId(0) 19..24 23..24 Other Write FileId(0) 34..35 Other Write FileId(0) 38..39 Other Read @@ -853,7 +780,7 @@ fn foo() { } "#, expect![[r#" - i Local FileId(0) 19..20 Other + i Local FileId(0) 19..20 19..20 Other FileId(0) 26..27 Other Write "#]], @@ -995,10 +922,10 @@ impl Foo { } "#, expect![[r#" - self SelfParam FileId(0) 47..51 47..51 SelfKw Read + self SelfParam FileId(0) 47..51 47..51 SelfParam - FileId(0) 71..75 SelfKw Read - FileId(0) 152..156 SelfKw Read + FileId(0) 71..75 Other Read + FileId(0) 152..156 Other Read "#]], ); } @@ -1105,7 +1032,7 @@ fn main() { } "#, expect![[r#" - a Local FileId(0) 59..60 Other + a Local FileId(0) 59..60 59..60 Other FileId(0) 80..81 Other Read "#]], @@ -1123,7 +1050,7 @@ fn main() { } "#, expect![[r#" - a Local FileId(0) 59..60 Other + a Local FileId(0) 59..60 59..60 Other FileId(0) 80..81 Other Read "#]], diff --git a/crates/ide/src/references/rename.rs b/crates/ide/src/references/rename.rs index 039efb26fd..9ac4af026b 100644 --- a/crates/ide/src/references/rename.rs +++ b/crates/ide/src/references/rename.rs @@ -444,7 +444,7 @@ fn rename_reference( mark::hit!(rename_not_an_ident_ref); bail!("Invalid name `{}`: not an identifier", new_name) } - (IdentifierKind::ToSelf, ReferenceKind::SelfKw) => { + (IdentifierKind::ToSelf, ReferenceKind::SelfParam) => { unreachable!("rename_self_to_param should've been called instead") } (IdentifierKind::ToSelf, _) => { diff --git a/crates/ide/src/syntax_highlighting/highlight.rs b/crates/ide/src/syntax_highlighting/highlight.rs index 34bae49a85..87578e70ac 100644 --- a/crates/ide/src/syntax_highlighting/highlight.rs +++ b/crates/ide/src/syntax_highlighting/highlight.rs @@ -226,35 +226,16 @@ pub(super) fn element( T![unsafe] => h | HlMod::Unsafe, T![true] | T![false] => HlTag::BoolLiteral.into(), T![self] => { - let self_param_is_mut = element - .parent() - .and_then(ast::SelfParam::cast) - .and_then(|p| p.mut_token()) - .is_some(); - let self_path = &element - .parent() - .as_ref() - .and_then(SyntaxNode::parent) - .and_then(ast::Path::cast) - .and_then(|p| sema.resolve_path(&p)); - let mut h = HlTag::Symbol(SymbolKind::SelfParam).into(); - if self_param_is_mut - || matches!(self_path, - Some(hir::PathResolution::Local(local)) - if local.is_self(db) - && (local.is_mut(db) || local.ty(db).is_mutable_reference()) - ) + let self_param = element.parent().and_then(ast::SelfParam::cast); + if let Some(NameClass::Definition(def)) = self_param + .and_then(|self_param| NameClass::classify_self_param(sema, &self_param)) { - h |= HlMod::Mutable + highlight_def(db, def) | HlMod::Definition + } else if element.ancestors().any(|it| it.kind() == USE_TREE) { + HlTag::Symbol(SymbolKind::SelfParam).into() + } else { + return None; } - - if let Some(hir::PathResolution::Local(local)) = self_path { - if is_consumed_lvalue(element, &local, db) { - h |= HlMod::Consuming; - } - } - - h } T![ref] => element .parent() @@ -345,7 +326,9 @@ fn highlight_def(db: &RootDatabase, def: Definition) -> Highlight { hir::GenericParam::LifetimeParam(_) => HlTag::Symbol(SymbolKind::LifetimeParam), }, Definition::Local(local) => { - let tag = if local.is_param(db) { + let tag = if local.is_self(db) { + HlTag::Symbol(SymbolKind::SelfParam) + } else if local.is_param(db) { HlTag::Symbol(SymbolKind::ValueParam) } else { HlTag::Symbol(SymbolKind::Local) diff --git a/crates/ide/src/syntax_highlighting/test_data/highlight_assoc_functions.html b/crates/ide/src/syntax_highlighting/test_data/highlight_assoc_functions.html index e36e6fc3fa..d421a78038 100644 --- a/crates/ide/src/syntax_highlighting/test_data/highlight_assoc_functions.html +++ b/crates/ide/src/syntax_highlighting/test_data/highlight_assoc_functions.html @@ -42,16 +42,16 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd impl foo { pub fn is_static() {} - pub fn is_not_static(&self) {} + pub fn is_not_static(&self) {} } trait t { fn t_is_static() {} - fn t_is_not_static(&self) {} + fn t_is_not_static(&self) {} } impl t for foo { pub fn is_static() {} - pub fn is_not_static(&self) {} + pub fn is_not_static(&self) {} } \ No newline at end of file diff --git a/crates/ide/src/syntax_highlighting/test_data/highlight_doctest.html b/crates/ide/src/syntax_highlighting/test_data/highlight_doctest.html index 6dadda1c1d..5e877df885 100644 --- a/crates/ide/src/syntax_highlighting/test_data/highlight_doctest.html +++ b/crates/ide/src/syntax_highlighting/test_data/highlight_doctest.html @@ -93,7 +93,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd /// ```sh /// echo 1 /// ``` - pub fn foo(&self) -> bool { + pub fn foo(&self) -> bool { true } } diff --git a/crates/ide/src/syntax_highlighting/test_data/highlight_unsafe.html b/crates/ide/src/syntax_highlighting/test_data/highlight_unsafe.html index 9d4d6d4a08..036cb6c115 100644 --- a/crates/ide/src/syntax_highlighting/test_data/highlight_unsafe.html +++ b/crates/ide/src/syntax_highlighting/test_data/highlight_unsafe.html @@ -46,7 +46,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd struct HasUnsafeFn; impl HasUnsafeFn { - unsafe fn unsafe_method(&self) {} + unsafe fn unsafe_method(&self) {} } struct TypeForStaticMut { @@ -61,11 +61,11 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd } trait DoTheAutoref { - fn calls_autoref(&self); + fn calls_autoref(&self); } impl DoTheAutoref for u16 { - fn calls_autoref(&self) {} + fn calls_autoref(&self) {} } fn main() { diff --git a/crates/ide/src/syntax_highlighting/test_data/highlighting.html b/crates/ide/src/syntax_highlighting/test_data/highlighting.html index 6b7447c46e..2371495666 100644 --- a/crates/ide/src/syntax_highlighting/test_data/highlighting.html +++ b/crates/ide/src/syntax_highlighting/test_data/highlighting.html @@ -66,25 +66,25 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd } trait Bar { - fn bar(&self) -> i32; + fn bar(&self) -> i32; } impl Bar for Foo { - fn bar(&self) -> i32 { + fn bar(&self) -> i32 { self.x } } impl Foo { - fn baz(mut self, f: Foo) -> i32 { + fn baz(mut self, f: Foo) -> i32 { f.baz(self) } - fn qux(&mut self) { + fn qux(&mut self) { self.x = 0; } - fn quop(&self) -> i32 { + fn quop(&self) -> i32 { self.x } } @@ -95,15 +95,15 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd } impl FooCopy { - fn baz(self, f: FooCopy) -> u32 { + fn baz(self, f: FooCopy) -> u32 { f.baz(self) } - fn qux(&mut self) { + fn qux(&mut self) { self.x = 0; } - fn quop(&self) -> u32 { + fn quop(&self) -> u32 { self.x } } @@ -213,7 +213,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd use Option::*; impl<T> Option<T> { - fn and<U>(self, other: Option<U>) -> Option<(T, U)> { + fn and<U>(self, other: Option<U>) -> Option<(T, U)> { match other { None => unimplemented!(), Nope => Nope, diff --git a/crates/ide_db/src/defs.rs b/crates/ide_db/src/defs.rs index d68fe42b02..231e886a91 100644 --- a/crates/ide_db/src/defs.rs +++ b/crates/ide_db/src/defs.rs @@ -10,7 +10,7 @@ use hir::{ Module, ModuleDef, Name, PathResolution, Semantics, Visibility, }; use syntax::{ - ast::{self, AstNode}, + ast::{self, AstNode, PathSegmentKind}, match_ast, SyntaxKind, SyntaxNode, }; @@ -117,6 +117,13 @@ impl NameClass { } } + pub fn classify_self_param( + sema: &Semantics, + self_param: &ast::SelfParam, + ) -> Option { + sema.to_def(self_param).map(Definition::Local).map(NameClass::Definition) + } + pub fn classify(sema: &Semantics, name: &ast::Name) -> Option { let _p = profile::span("classify_name"); @@ -135,24 +142,26 @@ impl NameClass { let path = use_tree.path()?; let path_segment = path.segment()?; let name_ref_class = path_segment - .name_ref() - // The rename might be from a `self` token, so fallback to the name higher - // in the use tree. - .or_else(||{ - if path_segment.self_token().is_none() { - return None; + .kind() + .and_then(|kind| { + match kind { + // The rename might be from a `self` token, so fallback to the name higher + // in the use tree. + PathSegmentKind::SelfKw => { + let use_tree = use_tree + .syntax() + .parent() + .as_ref() + // Skip over UseTreeList + .and_then(SyntaxNode::parent) + .and_then(ast::UseTree::cast)?; + let path = use_tree.path()?; + let path_segment = path.segment()?; + path_segment.name_ref() + }, + PathSegmentKind::Name(name_ref) => Some(name_ref), + _ => return None, } - - let use_tree = use_tree - .syntax() - .parent() - .as_ref() - // Skip over UseTreeList - .and_then(SyntaxNode::parent) - .and_then(ast::UseTree::cast)?; - let path = use_tree.path()?; - let path_segment = path.segment()?; - path_segment.name_ref() }) .and_then(|name_ref| NameRefClass::classify(sema, &name_ref))?; diff --git a/crates/ide_db/src/helpers/insert_use.rs b/crates/ide_db/src/helpers/insert_use.rs index d6b498be36..0c180e9bcd 100644 --- a/crates/ide_db/src/helpers/insert_use.rs +++ b/crates/ide_db/src/helpers/insert_use.rs @@ -444,8 +444,14 @@ fn use_tree_path_cmp(a: &ast::Path, a_has_tl: bool, b: &ast::Path, b_has_tl: boo } fn path_segment_cmp(a: &ast::PathSegment, b: &ast::PathSegment) -> Ordering { - let a = a.name_ref(); - let b = b.name_ref(); + let a = a.kind().and_then(|kind| match kind { + PathSegmentKind::Name(name_ref) => Some(name_ref), + _ => None, + }); + let b = b.kind().and_then(|kind| match kind { + PathSegmentKind::Name(name_ref) => Some(name_ref), + _ => None, + }); a.as_ref().map(ast::NameRef::text).cmp(&b.as_ref().map(ast::NameRef::text)) } diff --git a/crates/ide_db/src/search.rs b/crates/ide_db/src/search.rs index b5fa466427..0ecb13a64b 100644 --- a/crates/ide_db/src/search.rs +++ b/crates/ide_db/src/search.rs @@ -65,7 +65,7 @@ pub enum ReferenceKind { FieldShorthandForLocal, StructLiteral, RecordFieldExprOrPat, - SelfKw, + SelfParam, EnumLiteral, Lifetime, Other, diff --git a/crates/parser/src/grammar/paths.rs b/crates/parser/src/grammar/paths.rs index 5d297e2d62..b10f48fe12 100644 --- a/crates/parser/src/grammar/paths.rs +++ b/crates/parser/src/grammar/paths.rs @@ -82,7 +82,11 @@ fn path_segment(p: &mut Parser, mode: Mode, first: bool) { } // test crate_path // use crate::foo; - T![self] | T![super] | T![crate] => p.bump_any(), + T![self] | T![super] | T![crate] => { + let m = p.start(); + p.bump_any(); + m.complete(p, NAME_REF); + } _ => { p.err_recover("expected identifier", items::ITEM_RECOVERY_SET); if empty { diff --git a/crates/syntax/src/algo.rs b/crates/syntax/src/algo.rs index 384d031e71..39da45cc0f 100644 --- a/crates/syntax/src/algo.rs +++ b/crates/syntax/src/algo.rs @@ -883,7 +883,7 @@ use crate::AstNode; replacements: - Line 2: Node(NAME_REF@5..14) -> crate + Line 2: Token(IDENT@5..14 "text_edit") -> crate Line 2: Token(IDENT@16..24 "TextEdit") -> AstNode Line 2: Token(WHITESPACE@25..27 "\n\n") -> "\n" diff --git a/crates/syntax/src/ast/generated/nodes.rs b/crates/syntax/src/ast/generated/nodes.rs index 9c96d3d073..1d722db3cf 100644 --- a/crates/syntax/src/ast/generated/nodes.rs +++ b/crates/syntax/src/ast/generated/nodes.rs @@ -18,6 +18,9 @@ pub struct NameRef { } impl NameRef { pub fn ident_token(&self) -> Option { support::token(&self.syntax, T![ident]) } + pub fn self_token(&self) -> Option { support::token(&self.syntax, T![self]) } + pub fn super_token(&self) -> Option { support::token(&self.syntax, T![super]) } + pub fn crate_token(&self) -> Option { support::token(&self.syntax, T![crate]) } } #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct Lifetime { @@ -42,9 +45,6 @@ pub struct PathSegment { pub(crate) syntax: SyntaxNode, } impl PathSegment { - pub fn crate_token(&self) -> Option { support::token(&self.syntax, T![crate]) } - pub fn self_token(&self) -> Option { support::token(&self.syntax, T![self]) } - pub fn super_token(&self) -> Option { support::token(&self.syntax, T![super]) } pub fn coloncolon_token(&self) -> Option { support::token(&self.syntax, T![::]) } pub fn name_ref(&self) -> Option { support::child(&self.syntax) } pub fn generic_arg_list(&self) -> Option { support::child(&self.syntax) } diff --git a/crates/syntax/src/ast/node_ext.rs b/crates/syntax/src/ast/node_ext.rs index 27381ba807..b8ce71d270 100644 --- a/crates/syntax/src/ast/node_ext.rs +++ b/crates/syntax/src/ast/node_ext.rs @@ -156,14 +156,28 @@ impl ast::PathSegment { .expect("segments are always nested in paths") } + pub fn crate_token(&self) -> Option { + self.name_ref().and_then(|it| it.crate_token()) + } + + pub fn self_token(&self) -> Option { + self.name_ref().and_then(|it| it.self_token()) + } + + pub fn super_token(&self) -> Option { + self.name_ref().and_then(|it| it.super_token()) + } + pub fn kind(&self) -> Option { let res = if let Some(name_ref) = self.name_ref() { - PathSegmentKind::Name(name_ref) + match name_ref.syntax().first_token().map(|it| it.kind()) { + Some(T![self]) => PathSegmentKind::SelfKw, + Some(T![super]) => PathSegmentKind::SuperKw, + Some(T![crate]) => PathSegmentKind::CrateKw, + _ => PathSegmentKind::Name(name_ref), + } } else { match self.syntax().first_child_or_token()?.kind() { - T![self] => PathSegmentKind::SelfKw, - T![super] => PathSegmentKind::SuperKw, - T![crate] => PathSegmentKind::CrateKw, T![<] => { // or // T is any TypeRef, Trait has to be a PathType diff --git a/crates/syntax/src/validation.rs b/crates/syntax/src/validation.rs index bfa2dc4ba0..7901580ee9 100644 --- a/crates/syntax/src/validation.rs +++ b/crates/syntax/src/validation.rs @@ -256,7 +256,7 @@ fn validate_path_keywords(segment: ast::PathSegment, errors: &mut Vec