From cb863390f23bc2eac6561d55def9bd3ba54605fc Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Fri, 15 Jan 2021 18:57:32 +0100 Subject: Handle self/super/crate in PathSegment as NameRef --- crates/ide/src/diagnostics.rs | 4 +- crates/ide/src/display/navigation_target.rs | 23 ++-- crates/ide/src/goto_definition.rs | 35 ++---- crates/ide/src/hover.rs | 47 ++++++-- crates/ide/src/references.rs | 119 ++++----------------- crates/ide/src/references/rename.rs | 2 +- crates/ide/src/syntax_highlighting/highlight.rs | 39 ++----- .../test_data/highlight_assoc_functions.html | 6 +- .../test_data/highlight_doctest.html | 2 +- .../test_data/highlight_unsafe.html | 6 +- .../test_data/highlighting.html | 18 ++-- 11 files changed, 115 insertions(+), 186 deletions(-) (limited to 'crates/ide/src') diff --git a/crates/ide/src/diagnostics.rs b/crates/ide/src/diagnostics.rs index 2e5395b51..b35bc2bae 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 4eecae697..685052e7f 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 cd4afc804..988a5668f 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 317b6f011..6022bd275 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 7d4757e02..51a2f4327 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 039efb26f..9ac4af026 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 34bae49a8..87578e70a 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 - } - - if let Some(hir::PathResolution::Local(local)) = self_path { - if is_consumed_lvalue(element, &local, db) { - h |= HlMod::Consuming; - } + highlight_def(db, def) | HlMod::Definition + } else if element.ancestors().any(|it| it.kind() == USE_TREE) { + HlTag::Symbol(SymbolKind::SelfParam).into() + } else { + return None; } - - 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 e36e6fc3f..d421a7803 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 6dadda1c1..5e877df88 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 9d4d6d4a0..036cb6c11 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 6b7447c46..237149566 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, -- cgit v1.2.3