From 0777698f29e0ee7d73de17bf8f35385410f1963f Mon Sep 17 00:00:00 2001 From: hamidreza kalbasi Date: Sun, 19 Sep 2021 16:15:44 +0430 Subject: [PATCH 1/5] internal: definition based hover functions --- crates/ide/src/hover.rs | 215 +++++++++++++++++++++++----------------- 1 file changed, 123 insertions(+), 92 deletions(-) diff --git a/crates/ide/src/hover.rs b/crates/ide/src/hover.rs index 506d3ba3c0..49bd6e8b34 100644 --- a/crates/ide/src/hover.rs +++ b/crates/ide/src/hover.rs @@ -117,6 +117,26 @@ pub(crate) fn hover( let mut seen = HashSet::default(); let mut fallback = None; + // attributes, require special machinery as they are mere ident tokens + if token.kind() != COMMENT { + if let Some(attr) = token.ancestors().find_map(ast::Attr::cast) { + // lints + if let Some(res) = try_hover_for_lint(&attr, &token) { + return Some(res); + // derives + } else { + let def = try_resolve_derive_input_at(&sema, &attr, &token).map(Definition::Macro); + if let Some(def) = def { + if let Some(hover) = + hover_for_definition(&sema, file_id, def, &token.parent().unwrap(), config) + { + return Some(RangeInfo::new(token.text_range(), hover)); + } + } + } + } + } + sema.descend_into_macros_many(token.clone()) .iter() .filter_map(|token| match token.parent() { @@ -169,71 +189,39 @@ fn find_hover_result( // so don't add them to the `seen` duplicate check let mut add_to_seen_definitions = true; - let definition = match_ast! { - match node { - ast::Name(name) => NameClass::classify(sema, &name).map(|class| match class { - NameClass::Definition(it) | NameClass::ConstReference(it) => it, - NameClass::PatFieldShorthand { local_def, field_ref: _ } => Definition::Local(local_def), - }), - ast::NameRef(name_ref) => NameRefClass::classify(sema, &name_ref).map(|class| match class { - NameRefClass::Definition(def) => def, - NameRefClass::FieldShorthand { local_ref: _, field_ref } => { - Definition::Field(field_ref) - } - }), - ast::Lifetime(lifetime) => NameClass::classify_lifetime(&sema, &lifetime).map_or_else( - || { - NameRefClass::classify_lifetime(&sema, &lifetime).and_then(|class| match class { - NameRefClass::Definition(it) => Some(it), - _ => None, - }) + let definition = find_definition(sema, node).or_else(|| { + // intra-doc links + // FIXME: move comment + attribute special cases somewhere else to simplify control flow, + // hopefully simplifying the return type of this function in the process + // (the `Break`/`Continue` distinction is needed to decide whether to use fallback hovers) + // + // FIXME: hovering the intra doc link to `Foo` not working: + // + // #[identity] + // trait Foo { + // /// [`Foo`] + // fn foo() {} + if token.kind() == COMMENT { + add_to_seen_definitions = false; + cov_mark::hit!(no_highlight_on_comment_hover); + let (attributes, def) = doc_attributes(sema, node)?; + let (docs, doc_mapping) = attributes.docs_with_rangemap(sema.db)?; + let (idl_range, link, ns) = extract_definitions_from_docs(&docs).into_iter().find_map( + |(range, link, ns)| { + let mapped = doc_mapping.map(range)?; + (mapped.file_id == file_id.into() && mapped.value.contains(offset)) + .then(|| (mapped.value, link, ns)) }, - NameClass::defined, - ), - _ => { - // intra-doc links - // FIXME: move comment + attribute special cases somewhere else to simplify control flow, - // hopefully simplifying the return type of this function in the process - // (the `Break`/`Continue` distinction is needed to decide whether to use fallback hovers) - // - // FIXME: hovering the intra doc link to `Foo` not working: - // - // #[identity] - // trait Foo { - // /// [`Foo`] - // fn foo() {} - if token.kind() == COMMENT { - add_to_seen_definitions = false; - cov_mark::hit!(no_highlight_on_comment_hover); - let (attributes, def) = doc_attributes(sema, node)?; - let (docs, doc_mapping) = attributes.docs_with_rangemap(sema.db)?; - let (idl_range, link, ns) = - extract_definitions_from_docs(&docs).into_iter().find_map(|(range, link, ns)| { - let mapped = doc_mapping.map(range)?; - (mapped.file_id == file_id.into() && mapped.value.contains(offset)).then(||(mapped.value, link, ns)) - })?; - range_override = Some(idl_range); - Some(match resolve_doc_path_for_def(sema.db,def, &link,ns)? { - Either::Left(it) => Definition::ModuleDef(it), - Either::Right(it) => Definition::Macro(it), - }) - // attributes, require special machinery as they are mere ident tokens - } else if let Some(attr) = token.ancestors().find_map(ast::Attr::cast) { - add_to_seen_definitions = false; - // lints - if let Some(res) = try_hover_for_lint(&attr, &token) { - return Some(ControlFlow::Break(res)); - // derives - } else { - range_override = Some(token.text_range()); - try_resolve_derive_input_at(&sema, &attr, &token).map(Definition::Macro) - } - } else { - None - } - }, + )?; + range_override = Some(idl_range); + Some(match resolve_doc_path_for_def(sema.db, def, &link, ns)? { + Either::Left(it) => Definition::ModuleDef(it), + Either::Right(it) => Definition::Macro(it), + }) + } else { + None } - }; + }); if let Some(definition) = definition { // skip duplicates @@ -243,33 +231,7 @@ fn find_hover_result( if add_to_seen_definitions { seen.insert(definition); } - let famous_defs = match &definition { - Definition::ModuleDef(hir::ModuleDef::BuiltinType(_)) => { - Some(FamousDefs(&sema, sema.scope(&node).krate())) - } - _ => None, - }; - if let Some(markup) = - hover_for_definition(sema.db, definition, famous_defs.as_ref(), config) - { - let mut res = HoverResult::default(); - res.markup = process_markup(sema.db, definition, &markup, config); - if let Some(action) = show_implementations_action(sema.db, definition) { - res.actions.push(action); - } - - if let Some(action) = show_fn_references_action(sema.db, definition) { - res.actions.push(action); - } - - if let Some(action) = runnable_action(&sema, definition, file_id) { - res.actions.push(action); - } - - if let Some(action) = goto_type_action_for_def(sema.db, definition) { - res.actions.push(action); - } - + if let Some(res) = hover_for_definition(sema, file_id, definition, &node, config) { let range = range_override.unwrap_or_else(|| sema.original_range(&node).range); return Some(ControlFlow::Break(RangeInfo::new(range, res))); } @@ -283,6 +245,9 @@ fn type_hover( config: &HoverConfig, token: &SyntaxToken, ) -> Option> { + if token.kind() == COMMENT { + return None; + } let node = token .ancestors() .take_while(|it| !ast::Item::can_cast(it.kind())) @@ -749,7 +714,73 @@ fn definition_mod_path(db: &RootDatabase, def: &Definition) -> Option { def.module(db).map(|module| render_path(db, module, definition_owner_name(db, def))) } -fn hover_for_definition( +pub(crate) fn find_definition( + sema: &Semantics, + node: &SyntaxNode, +) -> Option { + match_ast! { + match node { + ast::Name(name) => NameClass::classify(sema, &name).map(|class| match class { + NameClass::Definition(it) | NameClass::ConstReference(it) => it, + NameClass::PatFieldShorthand { local_def, field_ref: _ } => Definition::Local(local_def), + }), + ast::NameRef(name_ref) => NameRefClass::classify(sema, &name_ref).map(|class| match class { + NameRefClass::Definition(def) => def, + NameRefClass::FieldShorthand { local_ref: _, field_ref } => { + Definition::Field(field_ref) + } + }), + ast::Lifetime(lifetime) => NameClass::classify_lifetime(&sema, &lifetime).map_or_else( + || { + NameRefClass::classify_lifetime(&sema, &lifetime).and_then(|class| match class { + NameRefClass::Definition(it) => Some(it), + _ => None, + }) + }, + NameClass::defined, + ), + _ => None, + } + } +} + +pub(crate) fn hover_for_definition( + sema: &Semantics, + file_id: FileId, + definition: Definition, + node: &SyntaxNode, + config: &HoverConfig, +) -> Option { + let famous_defs = match &definition { + Definition::ModuleDef(hir::ModuleDef::BuiltinType(_)) => { + Some(FamousDefs(&sema, sema.scope(&node).krate())) + } + _ => None, + }; + if let Some(markup) = markup_for_definition(sema.db, definition, famous_defs.as_ref(), config) { + let mut res = HoverResult::default(); + res.markup = process_markup(sema.db, definition, &markup, config); + if let Some(action) = show_implementations_action(sema.db, definition) { + res.actions.push(action); + } + + if let Some(action) = show_fn_references_action(sema.db, definition) { + res.actions.push(action); + } + + if let Some(action) = runnable_action(&sema, definition, file_id) { + res.actions.push(action); + } + + if let Some(action) = goto_type_action_for_def(sema.db, definition) { + res.actions.push(action); + } + return Some(res); + } + None +} + +fn markup_for_definition( db: &RootDatabase, def: Definition, famous_defs: Option<&FamousDefs>, @@ -885,7 +916,7 @@ mod tests { FileRange { file_id: position.file_id, range: TextRange::empty(position.offset) }, ) .unwrap(); - assert!(hover.is_none()); + assert!(hover.is_none(), "hover not expected but found: {:?}", hover.unwrap()); } fn check(ra_fixture: &str, expect: Expect) { From 887b7ddc376a94f228cade75d90c38719fe4262b Mon Sep 17 00:00:00 2001 From: hamidreza kalbasi Date: Tue, 21 Sep 2021 19:55:57 +0430 Subject: [PATCH 2/5] fix derive hover in macro --- crates/ide/src/hover.rs | 69 ++++++++++++++++++++++++++++++++--------- 1 file changed, 55 insertions(+), 14 deletions(-) diff --git a/crates/ide/src/hover.rs b/crates/ide/src/hover.rs index 49bd6e8b34..0d916acade 100644 --- a/crates/ide/src/hover.rs +++ b/crates/ide/src/hover.rs @@ -118,26 +118,36 @@ pub(crate) fn hover( let mut fallback = None; // attributes, require special machinery as they are mere ident tokens - if token.kind() != COMMENT { - if let Some(attr) = token.ancestors().find_map(ast::Attr::cast) { - // lints - if let Some(res) = try_hover_for_lint(&attr, &token) { - return Some(res); - // derives - } else { - let def = try_resolve_derive_input_at(&sema, &attr, &token).map(Definition::Macro); - if let Some(def) = def { - if let Some(hover) = - hover_for_definition(&sema, file_id, def, &token.parent().unwrap(), config) - { - return Some(RangeInfo::new(token.text_range(), hover)); + + let descend_macros = sema.descend_into_macros_many(token.clone()); + + for token in &descend_macros { + if token.kind() != COMMENT { + if let Some(attr) = token.ancestors().find_map(ast::Attr::cast) { + // lints + if let Some(res) = try_hover_for_lint(&attr, &token) { + return Some(res); + // derives + } else { + let def = + try_resolve_derive_input_at(&sema, &attr, &token).map(Definition::Macro); + if let Some(def) = def { + if let Some(hover) = hover_for_definition( + &sema, + file_id, + def, + &token.parent().unwrap(), + config, + ) { + return Some(RangeInfo::new(token.text_range(), hover)); + } } } } } } - sema.descend_into_macros_many(token.clone()) + descend_macros .iter() .filter_map(|token| match token.parent() { Some(node) => { @@ -4560,6 +4570,37 @@ use crate as foo$0; ); } + // FIXME: wrong range in macros. `es! ` should be `Copy` + #[test] + fn hover_attribute_in_macro() { + check( + r#" +macro_rules! identity { + ($struct:item) => { + $struct + }; +} +#[rustc_builtin_macro] +pub macro Copy {} +identity!{ + #[derive(Copy$0)] + struct Foo; +} +"#, + expect![[r#" + *es! * + + ```rust + test + ``` + + ```rust + pub macro Copy + ``` + "#]], + ); + } + #[test] fn hover_derive_input() { check( From 569ac5bee186e907cc14300ab0d46976ed063580 Mon Sep 17 00:00:00 2001 From: hamidreza kalbasi Date: Wed, 22 Sep 2021 11:37:26 +0330 Subject: [PATCH 3/5] use find_definition in go to --- crates/ide/src/goto_definition.rs | 55 +++++++++--------------- crates/ide/src/hover.rs | 71 +++++++++++++++++++------------ crates/ide/src/lib.rs | 2 +- 3 files changed, 64 insertions(+), 64 deletions(-) diff --git a/crates/ide/src/goto_definition.rs b/crates/ide/src/goto_definition.rs index b2d0fead87..c2fdc8dc7c 100644 --- a/crates/ide/src/goto_definition.rs +++ b/crates/ide/src/goto_definition.rs @@ -1,5 +1,11 @@ use std::{convert::TryInto, iter}; +use crate::hover::find_definition; +use crate::{ + display::{ToNav, TryToNav}, + doc_links::{doc_attributes, extract_definitions_from_docs, resolve_doc_path_for_def}, + FilePosition, NavigationTarget, RangeInfo, +}; use either::Either; use hir::{AsAssocItem, InFile, ModuleDef, Semantics}; use ide_db::{ @@ -11,12 +17,6 @@ use ide_db::{ use itertools::Itertools; use syntax::{ast, match_ast, AstNode, AstToken, SyntaxKind::*, SyntaxToken, TextRange, T}; -use crate::{ - display::{ToNav, TryToNav}, - doc_links::{doc_attributes, extract_definitions_from_docs, resolve_doc_path_for_def}, - FilePosition, NavigationTarget, RangeInfo, -}; - // Feature: Go to Definition // // Navigates to the definition of an identifier. @@ -58,39 +58,22 @@ pub(crate) fn goto_definition( .into_iter() .filter_map(|token| { let parent = token.parent()?; - let navs = match_ast! { + let result = find_definition(&sema, &parent) + .flat_map(|def| { + try_find_trait_item_definition(sema.db, &def) + .unwrap_or_else(|| def_to_nav(sema.db, def)) + }) + .collect::>(); + if !result.is_empty() { + return Some(result); + } + match_ast! { match parent { - ast::NameRef(name_ref) => { - reference_definition(&sema, Either::Right(&name_ref)) - }, - ast::Name(name) => { - match NameClass::classify(&sema, &name)? { - NameClass::Definition(def) | NameClass::ConstReference(def) => { - try_find_trait_item_definition(sema.db, &def) - .unwrap_or_else(|| def_to_nav(sema.db, def)) - } - NameClass::PatFieldShorthand { local_def, field_ref } => { - local_and_field_to_nav(sema.db, local_def, field_ref) - }, - } - }, - ast::Lifetime(lt) => { - match NameClass::classify_lifetime(&sema, <) { - Some(name_class) => { - match name_class { - NameClass::Definition(def) => def_to_nav(sema.db, def), - _ => return None, - } - } - None => reference_definition(&sema, Either::Left(<)), - } - }, ast::TokenTree(tt) => - try_lookup_include_path_or_derive(&sema, tt, token, position.file_id)?, - _ => return None, + try_lookup_include_path_or_derive(&sema, tt, token, position.file_id), + _ => None } - }; - Some(navs) + } }) .flatten() .unique() diff --git a/crates/ide/src/hover.rs b/crates/ide/src/hover.rs index 0d916acade..a01e272e53 100644 --- a/crates/ide/src/hover.rs +++ b/crates/ide/src/hover.rs @@ -1,4 +1,4 @@ -use std::{collections::HashSet, ops::ControlFlow}; +use std::{collections::HashSet, iter, ops::ControlFlow}; use either::Either; use hir::{AsAssocItem, HasAttrs, HasSource, HirDisplay, Semantics, TypeInfo}; @@ -199,7 +199,7 @@ fn find_hover_result( // so don't add them to the `seen` duplicate check let mut add_to_seen_definitions = true; - let definition = find_definition(sema, node).or_else(|| { + let definition = find_definition(sema, node).next().or_else(|| { // intra-doc links // FIXME: move comment + attribute special cases somewhere else to simplify control flow, // hopefully simplifying the return type of this function in the process @@ -724,34 +724,51 @@ fn definition_mod_path(db: &RootDatabase, def: &Definition) -> Option { def.module(db).map(|module| render_path(db, module, definition_owner_name(db, def))) } -pub(crate) fn find_definition( - sema: &Semantics, +pub(crate) fn find_definition<'a>( + sema: &'a Semantics, node: &SyntaxNode, -) -> Option { - match_ast! { - match node { - ast::Name(name) => NameClass::classify(sema, &name).map(|class| match class { - NameClass::Definition(it) | NameClass::ConstReference(it) => it, - NameClass::PatFieldShorthand { local_def, field_ref: _ } => Definition::Local(local_def), - }), - ast::NameRef(name_ref) => NameRefClass::classify(sema, &name_ref).map(|class| match class { - NameRefClass::Definition(def) => def, - NameRefClass::FieldShorthand { local_ref: _, field_ref } => { - Definition::Field(field_ref) - } - }), - ast::Lifetime(lifetime) => NameClass::classify_lifetime(&sema, &lifetime).map_or_else( - || { - NameRefClass::classify_lifetime(&sema, &lifetime).and_then(|class| match class { - NameRefClass::Definition(it) => Some(it), - _ => None, - }) +) -> impl Iterator + 'a { + iter::once(node.clone()).flat_map(move |node| { + match_ast! { + match node { + ast::Name(name) => { + let class = if let Some(x) = NameClass::classify(&sema, &name) { + x + } else { + return vec![]; + }; + match class { + NameClass::Definition(it) | NameClass::ConstReference(it) => vec![it], + NameClass::PatFieldShorthand { local_def, field_ref } => vec![Definition::Local(local_def), Definition::Field(field_ref)], + } }, - NameClass::defined, - ), - _ => None, + ast::NameRef(name_ref) => { + let class = if let Some(x) = NameRefClass::classify(sema, &name_ref) { + x + } else { + return vec![]; + }; + match class { + NameRefClass::Definition(def) => vec![def], + NameRefClass::FieldShorthand { local_ref, field_ref } => { + vec![Definition::Field(field_ref), Definition::Local(local_ref)] + } + } + }, + ast::Lifetime(lifetime) => { + (if let Some(x) = NameClass::classify_lifetime(&sema, &lifetime) { + NameClass::defined(x) + } else { + NameRefClass::classify_lifetime(&sema, &lifetime).and_then(|class| match class { + NameRefClass::Definition(it) => Some(it), + _ => None, + }) + }).into_iter().collect() + }, + _ => vec![], + } } - } + }) } pub(crate) fn hover_for_definition( diff --git a/crates/ide/src/lib.rs b/crates/ide/src/lib.rs index 21872c81d1..9ea0fe171c 100644 --- a/crates/ide/src/lib.rs +++ b/crates/ide/src/lib.rs @@ -35,7 +35,7 @@ mod goto_declaration; mod goto_definition; mod goto_implementation; mod goto_type_definition; -mod hover; +pub mod hover; mod inlay_hints; mod join_lines; mod markdown_remove; From 18e6b508dd48d7559f8033e10a3741135e6425ce Mon Sep 17 00:00:00 2001 From: hamidreza kalbasi Date: Wed, 22 Sep 2021 11:44:23 +0330 Subject: [PATCH 4/5] remove dead code --- crates/ide/src/goto_definition.rs | 34 +++---------------------------- 1 file changed, 3 insertions(+), 31 deletions(-) diff --git a/crates/ide/src/goto_definition.rs b/crates/ide/src/goto_definition.rs index c2fdc8dc7c..ec5d462da2 100644 --- a/crates/ide/src/goto_definition.rs +++ b/crates/ide/src/goto_definition.rs @@ -1,16 +1,15 @@ -use std::{convert::TryInto, iter}; +use std::convert::TryInto; use crate::hover::find_definition; use crate::{ - display::{ToNav, TryToNav}, + display::TryToNav, doc_links::{doc_attributes, extract_definitions_from_docs, resolve_doc_path_for_def}, FilePosition, NavigationTarget, RangeInfo, }; -use either::Either; use hir::{AsAssocItem, InFile, ModuleDef, Semantics}; use ide_db::{ base_db::{AnchoredPath, FileId, FileLoader}, - defs::{Definition, NameClass, NameRefClass}, + defs::Definition, helpers::{pick_best_token, try_resolve_derive_input_at}, RootDatabase, }; @@ -151,37 +150,10 @@ fn try_find_trait_item_definition( .map(|it| vec![it]) } -pub(crate) fn reference_definition( - sema: &Semantics, - name_ref: Either<&ast::Lifetime, &ast::NameRef>, -) -> Vec { - let name_kind = match name_ref.either( - |lifetime| NameRefClass::classify_lifetime(sema, lifetime), - |name_ref| NameRefClass::classify(sema, name_ref), - ) { - Some(class) => class, - None => return Vec::new(), - }; - match name_kind { - NameRefClass::Definition(def) => def_to_nav(sema.db, def), - NameRefClass::FieldShorthand { local_ref, field_ref } => { - local_and_field_to_nav(sema.db, local_ref, field_ref) - } - } -} - fn def_to_nav(db: &RootDatabase, def: Definition) -> Vec { def.try_to_nav(db).map(|it| vec![it]).unwrap_or_default() } -fn local_and_field_to_nav( - db: &RootDatabase, - local: hir::Local, - field: hir::Field, -) -> Vec { - iter::once(local.to_nav(db)).chain(field.try_to_nav(db)).collect() -} - #[cfg(test)] mod tests { use ide_db::base_db::FileRange; From 589c1dfa04bbb4429223c810c2fefc2f12beb6cd Mon Sep 17 00:00:00 2001 From: hamidreza kalbasi Date: Wed, 22 Sep 2021 18:35:54 +0330 Subject: [PATCH 5/5] move function to defs.rs --- crates/ide/src/goto_definition.rs | 81 ++++++++++++--------------- crates/ide/src/hover.rs | 91 +++++++------------------------ crates/ide/src/lib.rs | 2 +- crates/ide_db/src/defs.rs | 60 +++++++++++++++++++- 4 files changed, 113 insertions(+), 121 deletions(-) diff --git a/crates/ide/src/goto_definition.rs b/crates/ide/src/goto_definition.rs index ec5d462da2..4c01231f4a 100644 --- a/crates/ide/src/goto_definition.rs +++ b/crates/ide/src/goto_definition.rs @@ -1,6 +1,5 @@ use std::convert::TryInto; -use crate::hover::find_definition; use crate::{ display::TryToNav, doc_links::{doc_attributes, extract_definitions_from_docs, resolve_doc_path_for_def}, @@ -10,11 +9,11 @@ use hir::{AsAssocItem, InFile, ModuleDef, Semantics}; use ide_db::{ base_db::{AnchoredPath, FileId, FileLoader}, defs::Definition, - helpers::{pick_best_token, try_resolve_derive_input_at}, + helpers::pick_best_token, RootDatabase, }; use itertools::Itertools; -use syntax::{ast, match_ast, AstNode, AstToken, SyntaxKind::*, SyntaxToken, TextRange, T}; +use syntax::{ast, AstNode, AstToken, SyntaxKind::*, SyntaxToken, TextRange, T}; // Feature: Go to Definition // @@ -57,22 +56,22 @@ pub(crate) fn goto_definition( .into_iter() .filter_map(|token| { let parent = token.parent()?; - let result = find_definition(&sema, &parent) - .flat_map(|def| { - try_find_trait_item_definition(sema.db, &def) - .unwrap_or_else(|| def_to_nav(sema.db, def)) - }) - .collect::>(); - if !result.is_empty() { - return Some(result); - } - match_ast! { - match parent { - ast::TokenTree(tt) => - try_lookup_include_path_or_derive(&sema, tt, token, position.file_id), - _ => None + if let Some(tt) = ast::TokenTree::cast(parent.clone()) { + if let x @ Some(_) = + try_lookup_include_path(&sema, tt, token.clone(), position.file_id) + { + return x; } } + Some( + Definition::from_node(&sema, &token) + .into_iter() + .flat_map(|def| { + try_find_trait_item_definition(sema.db, &def) + .unwrap_or_else(|| def_to_nav(sema.db, def)) + }) + .collect::>(), + ) }) .flatten() .unique() @@ -81,41 +80,31 @@ pub(crate) fn goto_definition( Some(RangeInfo::new(original_token.text_range(), navs)) } -fn try_lookup_include_path_or_derive( +fn try_lookup_include_path( sema: &Semantics, tt: ast::TokenTree, token: SyntaxToken, file_id: FileId, ) -> Option> { - match ast::String::cast(token.clone()) { - Some(token) => { - let path = token.value()?.into_owned(); - let macro_call = tt.syntax().parent().and_then(ast::MacroCall::cast)?; - let name = macro_call.path()?.segment()?.name_ref()?; - if !matches!(&*name.text(), "include" | "include_str" | "include_bytes") { - return None; - } - let file_id = sema.db.resolve_path(AnchoredPath { anchor: file_id, path: &path })?; - let size = sema.db.file_text(file_id).len().try_into().ok()?; - Some(vec![NavigationTarget { - file_id, - full_range: TextRange::new(0.into(), size), - name: path.into(), - focus_range: None, - kind: None, - container_name: None, - description: None, - docs: None, - }]) - } - None => try_resolve_derive_input_at( - sema, - &tt.syntax().ancestors().nth(2).and_then(ast::Attr::cast)?, - &token, - ) - .and_then(|it| it.try_to_nav(sema.db)) - .map(|it| vec![it]), + let token = ast::String::cast(token.clone())?; + let path = token.value()?.into_owned(); + let macro_call = tt.syntax().parent().and_then(ast::MacroCall::cast)?; + let name = macro_call.path()?.segment()?.name_ref()?; + if !matches!(&*name.text(), "include" | "include_str" | "include_bytes") { + return None; } + let file_id = sema.db.resolve_path(AnchoredPath { anchor: file_id, path: &path })?; + let size = sema.db.file_text(file_id).len().try_into().ok()?; + Some(vec![NavigationTarget { + file_id, + full_range: TextRange::new(0.into(), size), + name: path.into(), + focus_range: None, + kind: None, + container_name: None, + description: None, + docs: None, + }]) } /// finds the trait definition of an impl'd item diff --git a/crates/ide/src/hover.rs b/crates/ide/src/hover.rs index a01e272e53..80e91f303c 100644 --- a/crates/ide/src/hover.rs +++ b/crates/ide/src/hover.rs @@ -1,13 +1,13 @@ -use std::{collections::HashSet, iter, ops::ControlFlow}; +use std::{collections::HashSet, ops::ControlFlow}; use either::Either; use hir::{AsAssocItem, HasAttrs, HasSource, HirDisplay, Semantics, TypeInfo}; use ide_db::{ base_db::{FileRange, SourceDatabase}, - defs::{Definition, NameClass, NameRefClass}, + defs::Definition, helpers::{ generated_lints::{CLIPPY_LINTS, DEFAULT_LINTS, FEATURES}, - pick_best_token, try_resolve_derive_input_at, FamousDefs, + pick_best_token, FamousDefs, }, RootDatabase, }; @@ -107,7 +107,7 @@ pub(crate) fn hover( } let offset = range.start(); - let token = pick_best_token(file.token_at_offset(offset), |kind| match kind { + let original_token = pick_best_token(file.token_at_offset(offset), |kind| match kind { IDENT | INT_NUMBER | LIFETIME_IDENT | T![self] | T![super] | T![crate] => 3, T!['('] | T![')'] => 2, kind if kind.is_trivia() => 0, @@ -119,7 +119,7 @@ pub(crate) fn hover( let mut fallback = None; // attributes, require special machinery as they are mere ident tokens - let descend_macros = sema.descend_into_macros_many(token.clone()); + let descend_macros = sema.descend_into_macros_many(original_token.clone()); for token in &descend_macros { if token.kind() != COMMENT { @@ -127,21 +127,6 @@ pub(crate) fn hover( // lints if let Some(res) = try_hover_for_lint(&attr, &token) { return Some(res); - // derives - } else { - let def = - try_resolve_derive_input_at(&sema, &attr, &token).map(Definition::Macro); - if let Some(def) = def { - if let Some(hover) = hover_for_definition( - &sema, - file_id, - def, - &token.parent().unwrap(), - config, - ) { - return Some(RangeInfo::new(token.text_range(), hover)); - } - } } } } @@ -151,7 +136,16 @@ pub(crate) fn hover( .iter() .filter_map(|token| match token.parent() { Some(node) => { - match find_hover_result(&sema, file_id, offset, config, token, &node, &mut seen) { + match find_hover_result( + &sema, + file_id, + offset, + config, + &original_token, + token, + &node, + &mut seen, + ) { Some(res) => match res { ControlFlow::Break(inner) => Some(inner), ControlFlow::Continue(_) => { @@ -189,6 +183,7 @@ fn find_hover_result( file_id: FileId, offset: TextSize, config: &HoverConfig, + original_token: &SyntaxToken, token: &SyntaxToken, node: &SyntaxNode, seen: &mut HashSet, @@ -199,7 +194,7 @@ fn find_hover_result( // so don't add them to the `seen` duplicate check let mut add_to_seen_definitions = true; - let definition = find_definition(sema, node).next().or_else(|| { + let definition = Definition::from_node(sema, token).into_iter().next().or_else(|| { // intra-doc links // FIXME: move comment + attribute special cases somewhere else to simplify control flow, // hopefully simplifying the return type of this function in the process @@ -242,7 +237,7 @@ fn find_hover_result( seen.insert(definition); } if let Some(res) = hover_for_definition(sema, file_id, definition, &node, config) { - let range = range_override.unwrap_or_else(|| sema.original_range(&node).range); + let range = range_override.unwrap_or_else(|| original_token.text_range()); return Some(ControlFlow::Break(RangeInfo::new(range, res))); } } @@ -724,53 +719,6 @@ fn definition_mod_path(db: &RootDatabase, def: &Definition) -> Option { def.module(db).map(|module| render_path(db, module, definition_owner_name(db, def))) } -pub(crate) fn find_definition<'a>( - sema: &'a Semantics, - node: &SyntaxNode, -) -> impl Iterator + 'a { - iter::once(node.clone()).flat_map(move |node| { - match_ast! { - match node { - ast::Name(name) => { - let class = if let Some(x) = NameClass::classify(&sema, &name) { - x - } else { - return vec![]; - }; - match class { - NameClass::Definition(it) | NameClass::ConstReference(it) => vec![it], - NameClass::PatFieldShorthand { local_def, field_ref } => vec![Definition::Local(local_def), Definition::Field(field_ref)], - } - }, - ast::NameRef(name_ref) => { - let class = if let Some(x) = NameRefClass::classify(sema, &name_ref) { - x - } else { - return vec![]; - }; - match class { - NameRefClass::Definition(def) => vec![def], - NameRefClass::FieldShorthand { local_ref, field_ref } => { - vec![Definition::Field(field_ref), Definition::Local(local_ref)] - } - } - }, - ast::Lifetime(lifetime) => { - (if let Some(x) = NameClass::classify_lifetime(&sema, &lifetime) { - NameClass::defined(x) - } else { - NameRefClass::classify_lifetime(&sema, &lifetime).and_then(|class| match class { - NameRefClass::Definition(it) => Some(it), - _ => None, - }) - }).into_iter().collect() - }, - _ => vec![], - } - } - }) -} - pub(crate) fn hover_for_definition( sema: &Semantics, file_id: FileId, @@ -4587,7 +4535,6 @@ use crate as foo$0; ); } - // FIXME: wrong range in macros. `es! ` should be `Copy` #[test] fn hover_attribute_in_macro() { check( @@ -4605,7 +4552,7 @@ identity!{ } "#, expect![[r#" - *es! * + *Copy* ```rust test diff --git a/crates/ide/src/lib.rs b/crates/ide/src/lib.rs index 9ea0fe171c..21872c81d1 100644 --- a/crates/ide/src/lib.rs +++ b/crates/ide/src/lib.rs @@ -35,7 +35,7 @@ mod goto_declaration; mod goto_definition; mod goto_implementation; mod goto_type_definition; -pub mod hover; +mod hover; mod inlay_hints; mod join_lines; mod markdown_remove; diff --git a/crates/ide_db/src/defs.rs b/crates/ide_db/src/defs.rs index 719f424fd2..d42af9c38f 100644 --- a/crates/ide_db/src/defs.rs +++ b/crates/ide_db/src/defs.rs @@ -11,10 +11,10 @@ use hir::{ }; use syntax::{ ast::{self, AstNode}, - match_ast, SyntaxKind, + match_ast, SyntaxKind, SyntaxToken, }; -use crate::RootDatabase; +use crate::{helpers::try_resolve_derive_input_at, RootDatabase}; // FIXME: a more precise name would probably be `Symbol`? #[derive(Debug, PartialEq, Eq, Copy, Clone, Hash)] @@ -29,6 +29,62 @@ pub enum Definition { } impl Definition { + pub fn from_node(sema: &Semantics, token: &SyntaxToken) -> Vec { + let node = if let Some(x) = token.parent() { + x + } else { + return vec![]; + }; + if token.kind() != SyntaxKind::COMMENT { + if let Some(attr) = token.ancestors().find_map(ast::Attr::cast) { + // derives + let def = try_resolve_derive_input_at(&sema, &attr, &token).map(Definition::Macro); + if let Some(def) = def { + return vec![def]; + } + } + } + match_ast! { + match node { + ast::Name(name) => { + let class = if let Some(x) = NameClass::classify(&sema, &name) { + x + } else { + return vec![]; + }; + match class { + NameClass::Definition(it) | NameClass::ConstReference(it) => vec![it], + NameClass::PatFieldShorthand { local_def, field_ref } => vec![Definition::Local(local_def), Definition::Field(field_ref)], + } + }, + ast::NameRef(name_ref) => { + let class = if let Some(x) = NameRefClass::classify(sema, &name_ref) { + x + } else { + return vec![]; + }; + match class { + NameRefClass::Definition(def) => vec![def], + NameRefClass::FieldShorthand { local_ref, field_ref } => { + vec![Definition::Field(field_ref), Definition::Local(local_ref)] + } + } + }, + ast::Lifetime(lifetime) => { + (if let Some(x) = NameClass::classify_lifetime(&sema, &lifetime) { + NameClass::defined(x) + } else { + NameRefClass::classify_lifetime(&sema, &lifetime).and_then(|class| match class { + NameRefClass::Definition(it) => Some(it), + _ => None, + }) + }).into_iter().collect() + }, + _ => vec![], + } + } + } + pub fn module(&self, db: &RootDatabase) -> Option { match self { Definition::Macro(it) => it.module(db),