mirror of
https://github.com/rust-lang/rust-analyzer.git
synced 2025-08-19 01:50:32 +00:00
Merge #9569
9569: internal: Explicitly check for reference locals or fields in Name classification r=Veykril a=Veykril Closes #7524 Inlines all the calls to reference related name classification as well as emits both goto definition targets for field shorthands. Co-authored-by: Lukas Wirth <lukastw97@gmail.com>
This commit is contained in:
commit
fe00358888
12 changed files with 202 additions and 112 deletions
|
@ -112,8 +112,16 @@ pub(crate) fn external_docs(
|
||||||
let node = token.parent()?;
|
let node = token.parent()?;
|
||||||
let definition = match_ast! {
|
let definition = match_ast! {
|
||||||
match node {
|
match node {
|
||||||
ast::NameRef(name_ref) => NameRefClass::classify(&sema, &name_ref).map(|d| d.referenced())?,
|
ast::NameRef(name_ref) => match NameRefClass::classify(&sema, &name_ref)? {
|
||||||
ast::Name(name) => NameClass::classify(&sema, &name).map(|d| d.referenced_or_defined())?,
|
NameRefClass::Definition(def) => def,
|
||||||
|
NameRefClass::FieldShorthand { local_ref: _, field_ref } => {
|
||||||
|
Definition::Field(field_ref)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
ast::Name(name) => match NameClass::classify(&sema, &name)? {
|
||||||
|
NameClass::Definition(it) | NameClass::ConstReference(it) => it,
|
||||||
|
NameClass::PatFieldShorthand { local_def: _, field_ref } => Definition::Field(field_ref),
|
||||||
|
},
|
||||||
_ => return None,
|
_ => return None,
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -51,11 +51,3 @@ pub(crate) fn annotations(ra_fixture: &str) -> (Analysis, FilePosition, Vec<(Fil
|
||||||
.collect();
|
.collect();
|
||||||
(host.analysis(), FilePosition { file_id, offset }, annotations)
|
(host.analysis(), FilePosition { file_id, offset }, annotations)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn nav_target_annotation(ra_fixture: &str) -> (Analysis, FilePosition, FileRange) {
|
|
||||||
let (analysis, position, mut annotations) = annotations(ra_fixture);
|
|
||||||
let (expected, data) = annotations.pop().unwrap();
|
|
||||||
assert!(annotations.is_empty());
|
|
||||||
assert_eq!(data, "");
|
|
||||||
(analysis, position, expected)
|
|
||||||
}
|
|
||||||
|
|
|
@ -23,34 +23,36 @@ pub(crate) fn goto_declaration(
|
||||||
let parent = token.parent()?;
|
let parent = token.parent()?;
|
||||||
let def = match_ast! {
|
let def = match_ast! {
|
||||||
match parent {
|
match parent {
|
||||||
ast::NameRef(name_ref) => {
|
ast::NameRef(name_ref) => match NameRefClass::classify(&sema, &name_ref)? {
|
||||||
let name_kind = NameRefClass::classify(&sema, &name_ref)?;
|
NameRefClass::Definition(it) => Some(it),
|
||||||
name_kind.referenced()
|
_ => None
|
||||||
},
|
},
|
||||||
ast::Name(name) => {
|
ast::Name(name) => match NameClass::classify(&sema, &name)? {
|
||||||
NameClass::classify(&sema, &name)?.referenced_or_defined()
|
NameClass::Definition(it) => Some(it),
|
||||||
|
_ => None
|
||||||
},
|
},
|
||||||
_ => return None,
|
_ => None,
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
match def {
|
match def? {
|
||||||
Definition::ModuleDef(hir::ModuleDef::Module(module)) => Some(RangeInfo::new(
|
Definition::ModuleDef(hir::ModuleDef::Module(module)) => Some(RangeInfo::new(
|
||||||
original_token.text_range(),
|
original_token.text_range(),
|
||||||
vec![NavigationTarget::from_module_to_decl(db, module)],
|
vec![NavigationTarget::from_module_to_decl(db, module)],
|
||||||
)),
|
)),
|
||||||
_ => return None,
|
_ => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use ide_db::base_db::FileRange;
|
use ide_db::base_db::FileRange;
|
||||||
|
use itertools::Itertools;
|
||||||
|
|
||||||
use crate::fixture;
|
use crate::fixture;
|
||||||
|
|
||||||
fn check(ra_fixture: &str) {
|
fn check(ra_fixture: &str) {
|
||||||
let (analysis, position, expected) = fixture::nav_target_annotation(ra_fixture);
|
let (analysis, position, expected) = fixture::annotations(ra_fixture);
|
||||||
let mut navs = analysis
|
let navs = analysis
|
||||||
.goto_declaration(position)
|
.goto_declaration(position)
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.expect("no declaration or definition found")
|
.expect("no declaration or definition found")
|
||||||
|
@ -58,10 +60,19 @@ mod tests {
|
||||||
if navs.len() == 0 {
|
if navs.len() == 0 {
|
||||||
panic!("unresolved reference")
|
panic!("unresolved reference")
|
||||||
}
|
}
|
||||||
assert_eq!(navs.len(), 1);
|
|
||||||
|
|
||||||
let nav = navs.pop().unwrap();
|
let cmp = |&FileRange { file_id, range }: &_| (file_id, range.start());
|
||||||
assert_eq!(expected, FileRange { file_id: nav.file_id, range: nav.focus_or_full_range() });
|
let navs = navs
|
||||||
|
.into_iter()
|
||||||
|
.map(|nav| FileRange { file_id: nav.file_id, range: nav.focus_or_full_range() })
|
||||||
|
.sorted_by_key(cmp)
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
let expected = expected
|
||||||
|
.into_iter()
|
||||||
|
.map(|(FileRange { file_id, range }, _)| FileRange { file_id, range })
|
||||||
|
.sorted_by_key(cmp)
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
assert_eq!(expected, navs);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
use std::convert::TryInto;
|
use std::{convert::TryInto, iter};
|
||||||
|
|
||||||
use either::Either;
|
use either::Either;
|
||||||
use hir::{AsAssocItem, InFile, ModuleDef, Semantics};
|
use hir::{AsAssocItem, InFile, ModuleDef, Semantics};
|
||||||
|
@ -11,7 +11,7 @@ use ide_db::{
|
||||||
use syntax::{ast, match_ast, AstNode, AstToken, SyntaxKind::*, SyntaxToken, TextRange, T};
|
use syntax::{ast, match_ast, AstNode, AstToken, SyntaxKind::*, SyntaxToken, TextRange, T};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
display::TryToNav,
|
display::{ToNav, TryToNav},
|
||||||
doc_links::{doc_attributes, extract_definitions_from_markdown, resolve_doc_path_for_def},
|
doc_links::{doc_attributes, extract_definitions_from_markdown, resolve_doc_path_for_def},
|
||||||
FilePosition, NavigationTarget, RangeInfo,
|
FilePosition, NavigationTarget, RangeInfo,
|
||||||
};
|
};
|
||||||
|
@ -54,28 +54,36 @@ pub(crate) fn goto_definition(
|
||||||
let nav = resolve_doc_path_for_def(db, def, &link, ns)?.try_to_nav(db)?;
|
let nav = resolve_doc_path_for_def(db, def, &link, ns)?.try_to_nav(db)?;
|
||||||
return Some(RangeInfo::new(original_token.text_range(), vec![nav]));
|
return Some(RangeInfo::new(original_token.text_range(), vec![nav]));
|
||||||
}
|
}
|
||||||
let nav = match_ast! {
|
|
||||||
|
let navs = match_ast! {
|
||||||
match parent {
|
match parent {
|
||||||
ast::NameRef(name_ref) => {
|
ast::NameRef(name_ref) => {
|
||||||
reference_definition(&sema, Either::Right(&name_ref))
|
reference_definition(&sema, Either::Right(&name_ref))
|
||||||
},
|
},
|
||||||
ast::Name(name) => {
|
ast::Name(name) => {
|
||||||
let def = NameClass::classify(&sema, &name)?.referenced_or_defined();
|
match NameClass::classify(&sema, &name)? {
|
||||||
try_find_trait_item_definition(sema.db, &def)
|
NameClass::Definition(def) | NameClass::ConstReference(def) => {
|
||||||
.or_else(|| def.try_to_nav(sema.db))
|
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) => if let Some(name_class) = NameClass::classify_lifetime(&sema, <) {
|
ast::Lifetime(lt) => if let Some(name_class) = NameClass::classify_lifetime(&sema, <) {
|
||||||
let def = name_class.referenced_or_defined();
|
match name_class {
|
||||||
def.try_to_nav(sema.db)
|
NameClass::Definition(def) => def_to_nav(sema.db, def),
|
||||||
|
_ => return None,
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
reference_definition(&sema, Either::Left(<))
|
reference_definition(&sema, Either::Left(<))
|
||||||
},
|
},
|
||||||
ast::TokenTree(tt) => try_lookup_include_path(sema.db, tt, token, position.file_id),
|
ast::TokenTree(tt) => try_lookup_include_path(sema.db, tt, token, position.file_id)?,
|
||||||
_ => return None,
|
_ => return None,
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
Some(RangeInfo::new(original_token.text_range(), nav.into_iter().collect()))
|
Some(RangeInfo::new(original_token.text_range(), navs))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn try_lookup_include_path(
|
fn try_lookup_include_path(
|
||||||
|
@ -83,7 +91,7 @@ fn try_lookup_include_path(
|
||||||
tt: ast::TokenTree,
|
tt: ast::TokenTree,
|
||||||
token: SyntaxToken,
|
token: SyntaxToken,
|
||||||
file_id: FileId,
|
file_id: FileId,
|
||||||
) -> Option<NavigationTarget> {
|
) -> Option<Vec<NavigationTarget>> {
|
||||||
let path = ast::String::cast(token)?.value()?.into_owned();
|
let path = ast::String::cast(token)?.value()?.into_owned();
|
||||||
let macro_call = tt.syntax().parent().and_then(ast::MacroCall::cast)?;
|
let macro_call = tt.syntax().parent().and_then(ast::MacroCall::cast)?;
|
||||||
let name = macro_call.path()?.segment()?.name_ref()?;
|
let name = macro_call.path()?.segment()?.name_ref()?;
|
||||||
|
@ -92,7 +100,7 @@ fn try_lookup_include_path(
|
||||||
}
|
}
|
||||||
let file_id = db.resolve_path(AnchoredPath { anchor: file_id, path: &path })?;
|
let file_id = db.resolve_path(AnchoredPath { anchor: file_id, path: &path })?;
|
||||||
let size = db.file_text(file_id).len().try_into().ok()?;
|
let size = db.file_text(file_id).len().try_into().ok()?;
|
||||||
Some(NavigationTarget {
|
Some(vec![NavigationTarget {
|
||||||
file_id,
|
file_id,
|
||||||
full_range: TextRange::new(0.into(), size),
|
full_range: TextRange::new(0.into(), size),
|
||||||
name: path.into(),
|
name: path.into(),
|
||||||
|
@ -101,7 +109,7 @@ fn try_lookup_include_path(
|
||||||
container_name: None,
|
container_name: None,
|
||||||
description: None,
|
description: None,
|
||||||
docs: None,
|
docs: None,
|
||||||
})
|
}])
|
||||||
}
|
}
|
||||||
|
|
||||||
/// finds the trait definition of an impl'd item
|
/// finds the trait definition of an impl'd item
|
||||||
|
@ -111,7 +119,10 @@ fn try_lookup_include_path(
|
||||||
/// struct S;
|
/// struct S;
|
||||||
/// impl A for S { fn a(); } // <-- on this function, will get the location of a() in the trait
|
/// impl A for S { fn a(); } // <-- on this function, will get the location of a() in the trait
|
||||||
/// ```
|
/// ```
|
||||||
fn try_find_trait_item_definition(db: &RootDatabase, def: &Definition) -> Option<NavigationTarget> {
|
fn try_find_trait_item_definition(
|
||||||
|
db: &RootDatabase,
|
||||||
|
def: &Definition,
|
||||||
|
) -> Option<Vec<NavigationTarget>> {
|
||||||
let name = def.name(db)?;
|
let name = def.name(db)?;
|
||||||
let assoc = match def {
|
let assoc = match def {
|
||||||
Definition::ModuleDef(ModuleDef::Function(f)) => f.as_assoc_item(db),
|
Definition::ModuleDef(ModuleDef::Function(f)) => f.as_assoc_item(db),
|
||||||
|
@ -130,37 +141,66 @@ fn try_find_trait_item_definition(db: &RootDatabase, def: &Definition) -> Option
|
||||||
.items(db)
|
.items(db)
|
||||||
.iter()
|
.iter()
|
||||||
.find_map(|itm| (itm.name(db)? == name).then(|| itm.try_to_nav(db)).flatten())
|
.find_map(|itm| (itm.name(db)? == name).then(|| itm.try_to_nav(db)).flatten())
|
||||||
|
.map(|it| vec![it])
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn reference_definition(
|
pub(crate) fn reference_definition(
|
||||||
sema: &Semantics<RootDatabase>,
|
sema: &Semantics<RootDatabase>,
|
||||||
name_ref: Either<&ast::Lifetime, &ast::NameRef>,
|
name_ref: Either<&ast::Lifetime, &ast::NameRef>,
|
||||||
) -> Option<NavigationTarget> {
|
) -> Vec<NavigationTarget> {
|
||||||
let name_kind = name_ref.either(
|
let name_kind = match name_ref.either(
|
||||||
|lifetime| NameRefClass::classify_lifetime(sema, lifetime),
|
|lifetime| NameRefClass::classify_lifetime(sema, lifetime),
|
||||||
|name_ref| NameRefClass::classify(sema, name_ref),
|
|name_ref| NameRefClass::classify(sema, name_ref),
|
||||||
)?;
|
) {
|
||||||
let def = name_kind.referenced();
|
Some(class) => class,
|
||||||
def.try_to_nav(sema.db)
|
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<NavigationTarget> {
|
||||||
|
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<NavigationTarget> {
|
||||||
|
iter::once(local.to_nav(db)).chain(field.try_to_nav(db)).collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use ide_db::base_db::FileRange;
|
use ide_db::base_db::FileRange;
|
||||||
|
use itertools::Itertools;
|
||||||
|
|
||||||
use crate::fixture;
|
use crate::fixture;
|
||||||
|
|
||||||
fn check(ra_fixture: &str) {
|
fn check(ra_fixture: &str) {
|
||||||
let (analysis, position, expected) = fixture::nav_target_annotation(ra_fixture);
|
let (analysis, position, expected) = fixture::annotations(ra_fixture);
|
||||||
let mut navs =
|
let navs = analysis.goto_definition(position).unwrap().expect("no definition found").info;
|
||||||
analysis.goto_definition(position).unwrap().expect("no definition found").info;
|
|
||||||
if navs.len() == 0 {
|
if navs.len() == 0 {
|
||||||
panic!("unresolved reference")
|
panic!("unresolved reference")
|
||||||
}
|
}
|
||||||
assert_eq!(navs.len(), 1);
|
|
||||||
|
|
||||||
let nav = navs.pop().unwrap();
|
let cmp = |&FileRange { file_id, range }: &_| (file_id, range.start());
|
||||||
assert_eq!(expected, FileRange { file_id: nav.file_id, range: nav.focus_or_full_range() });
|
let navs = navs
|
||||||
|
.into_iter()
|
||||||
|
.map(|nav| FileRange { file_id: nav.file_id, range: nav.focus_or_full_range() })
|
||||||
|
.sorted_by_key(cmp)
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
let expected = expected
|
||||||
|
.into_iter()
|
||||||
|
.map(|(FileRange { file_id, range }, _)| FileRange { file_id, range })
|
||||||
|
.sorted_by_key(cmp)
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
assert_eq!(expected, navs);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn check_unresolved(ra_fixture: &str) {
|
fn check_unresolved(ra_fixture: &str) {
|
||||||
|
@ -863,6 +903,7 @@ fn bar() {
|
||||||
check(
|
check(
|
||||||
r#"
|
r#"
|
||||||
struct Foo { x: i32 }
|
struct Foo { x: i32 }
|
||||||
|
//^
|
||||||
fn main() {
|
fn main() {
|
||||||
let x = 92;
|
let x = 92;
|
||||||
//^
|
//^
|
||||||
|
@ -878,10 +919,12 @@ fn main() {
|
||||||
r#"
|
r#"
|
||||||
enum Foo {
|
enum Foo {
|
||||||
Bar { x: i32 }
|
Bar { x: i32 }
|
||||||
} //^
|
//^
|
||||||
|
}
|
||||||
fn baz(foo: Foo) {
|
fn baz(foo: Foo) {
|
||||||
match foo {
|
match foo {
|
||||||
Foo::Bar { x$0 } => x
|
Foo::Bar { x$0 } => x
|
||||||
|
//^
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
"#,
|
"#,
|
||||||
|
@ -1126,13 +1169,15 @@ fn foo<'foobar>(_: &'foobar ()) {
|
||||||
fn goto_lifetime_hrtb() {
|
fn goto_lifetime_hrtb() {
|
||||||
// FIXME: requires the HIR to somehow track these hrtb lifetimes
|
// FIXME: requires the HIR to somehow track these hrtb lifetimes
|
||||||
check_unresolved(
|
check_unresolved(
|
||||||
r#"trait Foo<T> {}
|
r#"
|
||||||
|
trait Foo<T> {}
|
||||||
fn foo<T>() where for<'a> T: Foo<&'a$0 (u8, u16)>, {}
|
fn foo<T>() where for<'a> T: Foo<&'a$0 (u8, u16)>, {}
|
||||||
//^^
|
//^^
|
||||||
"#,
|
"#,
|
||||||
);
|
);
|
||||||
check_unresolved(
|
check_unresolved(
|
||||||
r#"trait Foo<T> {}
|
r#"
|
||||||
|
trait Foo<T> {}
|
||||||
fn foo<T>() where for<'a$0> T: Foo<&'a (u8, u16)>, {}
|
fn foo<T>() where for<'a$0> T: Foo<&'a (u8, u16)>, {}
|
||||||
//^^
|
//^^
|
||||||
"#,
|
"#,
|
||||||
|
|
|
@ -28,11 +28,19 @@ pub(crate) fn goto_implementation(
|
||||||
|
|
||||||
let node = sema.find_node_at_offset_with_descend(&syntax, position.offset)?;
|
let node = sema.find_node_at_offset_with_descend(&syntax, position.offset)?;
|
||||||
let def = match &node {
|
let def = match &node {
|
||||||
ast::NameLike::Name(name) => {
|
ast::NameLike::Name(name) => NameClass::classify(&sema, name).map(|class| match class {
|
||||||
NameClass::classify(&sema, name).map(|class| class.referenced_or_defined())
|
NameClass::Definition(it) | NameClass::ConstReference(it) => it,
|
||||||
}
|
NameClass::PatFieldShorthand { local_def, field_ref: _ } => {
|
||||||
|
Definition::Local(local_def)
|
||||||
|
}
|
||||||
|
}),
|
||||||
ast::NameLike::NameRef(name_ref) => {
|
ast::NameLike::NameRef(name_ref) => {
|
||||||
NameRefClass::classify(&sema, name_ref).map(|class| class.referenced())
|
NameRefClass::classify(&sema, name_ref).map(|class| match class {
|
||||||
|
NameRefClass::Definition(def) => def,
|
||||||
|
NameRefClass::FieldShorthand { local_ref, field_ref: _ } => {
|
||||||
|
Definition::Local(local_ref)
|
||||||
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
ast::NameLike::Lifetime(_) => None,
|
ast::NameLike::Lifetime(_) => None,
|
||||||
}?;
|
}?;
|
||||||
|
|
|
@ -96,18 +96,23 @@ pub(crate) fn hover(
|
||||||
match node {
|
match node {
|
||||||
// we don't use NameClass::referenced_or_defined here as we do not want to resolve
|
// we don't use NameClass::referenced_or_defined here as we do not want to resolve
|
||||||
// field pattern shorthands to their definition
|
// field pattern shorthands to their definition
|
||||||
ast::Name(name) => NameClass::classify(&sema, &name).and_then(|class| match class {
|
ast::Name(name) => NameClass::classify(&sema, &name).map(|class| match class {
|
||||||
NameClass::ConstReference(def) => Some(def),
|
NameClass::Definition(it) | NameClass::ConstReference(it) => it,
|
||||||
def => def.defined(),
|
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::NameRef(name_ref) => {
|
|
||||||
NameRefClass::classify(&sema, &name_ref).map(|d| d.referenced())
|
|
||||||
},
|
|
||||||
ast::Lifetime(lifetime) => NameClass::classify_lifetime(&sema, &lifetime).map_or_else(
|
ast::Lifetime(lifetime) => NameClass::classify_lifetime(&sema, &lifetime).map_or_else(
|
||||||
|| NameRefClass::classify_lifetime(&sema, &lifetime).map(|d| d.referenced()),
|
|| NameRefClass::classify_lifetime(&sema, &lifetime).and_then(|class| match class {
|
||||||
|
NameRefClass::Definition(it) => Some(it),
|
||||||
|
_ => None,
|
||||||
|
}),
|
||||||
|d| d.defined(),
|
|d| d.defined(),
|
||||||
),
|
),
|
||||||
|
|
||||||
_ => {
|
_ => {
|
||||||
if ast::Comment::cast(token.clone()).is_some() {
|
if ast::Comment::cast(token.clone()).is_some() {
|
||||||
cov_mark::hit!(no_highlight_on_comment_hover);
|
cov_mark::hit!(no_highlight_on_comment_hover);
|
||||||
|
|
|
@ -58,7 +58,15 @@ pub(crate) fn find_all_refs(
|
||||||
|
|
||||||
let (def, is_literal_search) =
|
let (def, is_literal_search) =
|
||||||
if let Some(name) = get_name_of_item_declaration(&syntax, position) {
|
if let Some(name) = get_name_of_item_declaration(&syntax, position) {
|
||||||
(NameClass::classify(sema, &name)?.referenced_or_defined(), true)
|
(
|
||||||
|
match NameClass::classify(sema, &name)? {
|
||||||
|
NameClass::Definition(it) | NameClass::ConstReference(it) => it,
|
||||||
|
NameClass::PatFieldShorthand { local_def: _, field_ref } => {
|
||||||
|
Definition::Field(field_ref)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
true,
|
||||||
|
)
|
||||||
} else {
|
} else {
|
||||||
(find_def(sema, &syntax, position.offset)?, false)
|
(find_def(sema, &syntax, position.offset)?, false)
|
||||||
};
|
};
|
||||||
|
@ -116,13 +124,28 @@ pub(crate) fn find_def(
|
||||||
offset: TextSize,
|
offset: TextSize,
|
||||||
) -> Option<Definition> {
|
) -> Option<Definition> {
|
||||||
let def = match sema.find_node_at_offset_with_descend(syntax, offset)? {
|
let def = match sema.find_node_at_offset_with_descend(syntax, offset)? {
|
||||||
ast::NameLike::NameRef(name_ref) => NameRefClass::classify(sema, &name_ref)?.referenced(),
|
ast::NameLike::NameRef(name_ref) => match NameRefClass::classify(sema, &name_ref)? {
|
||||||
ast::NameLike::Name(name) => NameClass::classify(sema, &name)?.referenced_or_defined(),
|
NameRefClass::Definition(def) => def,
|
||||||
|
NameRefClass::FieldShorthand { local_ref, field_ref: _ } => {
|
||||||
|
Definition::Local(local_ref)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
ast::NameLike::Name(name) => match NameClass::classify(sema, &name)? {
|
||||||
|
NameClass::Definition(it) | NameClass::ConstReference(it) => it,
|
||||||
|
NameClass::PatFieldShorthand { local_def, field_ref: _ } => {
|
||||||
|
Definition::Local(local_def)
|
||||||
|
}
|
||||||
|
},
|
||||||
ast::NameLike::Lifetime(lifetime) => NameRefClass::classify_lifetime(sema, &lifetime)
|
ast::NameLike::Lifetime(lifetime) => NameRefClass::classify_lifetime(sema, &lifetime)
|
||||||
.map(|class| class.referenced())
|
.and_then(|class| match class {
|
||||||
|
NameRefClass::Definition(it) => Some(it),
|
||||||
|
_ => None,
|
||||||
|
})
|
||||||
.or_else(|| {
|
.or_else(|| {
|
||||||
NameClass::classify_lifetime(sema, &lifetime)
|
NameClass::classify_lifetime(sema, &lifetime).and_then(|class| match class {
|
||||||
.map(|class| class.referenced_or_defined())
|
NameClass::Definition(it) => Some(it),
|
||||||
|
_ => None,
|
||||||
|
})
|
||||||
})?,
|
})?,
|
||||||
};
|
};
|
||||||
Some(def)
|
Some(def)
|
||||||
|
|
|
@ -107,13 +107,19 @@ fn find_definition(
|
||||||
{
|
{
|
||||||
bail!("Renaming aliases is currently unsupported")
|
bail!("Renaming aliases is currently unsupported")
|
||||||
}
|
}
|
||||||
ast::NameLike::Name(name) => {
|
ast::NameLike::Name(name) => NameClass::classify(sema, &name).map(|class| match class {
|
||||||
NameClass::classify(sema, &name).map(|class| class.referenced_or_defined())
|
NameClass::Definition(it) | NameClass::ConstReference(it) => it,
|
||||||
}
|
NameClass::PatFieldShorthand { local_def, field_ref: _ } => {
|
||||||
|
Definition::Local(local_def)
|
||||||
|
}
|
||||||
|
}),
|
||||||
ast::NameLike::NameRef(name_ref) => {
|
ast::NameLike::NameRef(name_ref) => {
|
||||||
if let Some(def) =
|
if let Some(def) = NameRefClass::classify(sema, &name_ref).map(|class| match class {
|
||||||
NameRefClass::classify(sema, &name_ref).map(|class| class.referenced())
|
NameRefClass::Definition(def) => def,
|
||||||
{
|
NameRefClass::FieldShorthand { local_ref, field_ref: _ } => {
|
||||||
|
Definition::Local(local_ref)
|
||||||
|
}
|
||||||
|
}) {
|
||||||
// if the name differs from the definitions name it has to be an alias
|
// if the name differs from the definitions name it has to be an alias
|
||||||
if def.name(sema.db).map_or(false, |it| it.to_string() != name_ref.text()) {
|
if def.name(sema.db).map_or(false, |it| it.to_string() != name_ref.text()) {
|
||||||
bail!("Renaming aliases is currently unsupported");
|
bail!("Renaming aliases is currently unsupported");
|
||||||
|
@ -124,9 +130,15 @@ fn find_definition(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ast::NameLike::Lifetime(lifetime) => NameRefClass::classify_lifetime(sema, &lifetime)
|
ast::NameLike::Lifetime(lifetime) => NameRefClass::classify_lifetime(sema, &lifetime)
|
||||||
.map(|class| class.referenced())
|
.and_then(|class| match class {
|
||||||
|
NameRefClass::Definition(def) => Some(def),
|
||||||
|
_ => None,
|
||||||
|
})
|
||||||
.or_else(|| {
|
.or_else(|| {
|
||||||
NameClass::classify_lifetime(sema, &lifetime).map(|it| it.referenced_or_defined())
|
NameClass::classify_lifetime(sema, &lifetime).and_then(|it| match it {
|
||||||
|
NameClass::Definition(it) => Some(it),
|
||||||
|
_ => None,
|
||||||
|
})
|
||||||
}),
|
}),
|
||||||
}
|
}
|
||||||
.ok_or_else(|| format_err!("No references found at position"))?;
|
.ok_or_else(|| format_err!("No references found at position"))?;
|
||||||
|
|
|
@ -58,10 +58,8 @@ pub(super) fn element(
|
||||||
Some(NameClass::ConstReference(def)) => highlight_def(db, krate, def),
|
Some(NameClass::ConstReference(def)) => highlight_def(db, krate, def),
|
||||||
Some(NameClass::PatFieldShorthand { field_ref, .. }) => {
|
Some(NameClass::PatFieldShorthand { field_ref, .. }) => {
|
||||||
let mut h = HlTag::Symbol(SymbolKind::Field).into();
|
let mut h = HlTag::Symbol(SymbolKind::Field).into();
|
||||||
if let Definition::Field(field) = field_ref {
|
if let hir::VariantDef::Union(_) = field_ref.parent_def(db) {
|
||||||
if let hir::VariantDef::Union(_) = field.parent_def(db) {
|
h |= HlMod::Unsafe;
|
||||||
h |= HlMod::Unsafe;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
h
|
h
|
||||||
}
|
}
|
||||||
|
|
|
@ -638,7 +638,12 @@ fn vars_used_in_body(ctx: &AssistContext, body: &FunctionBody) -> Vec<Local> {
|
||||||
body.descendants()
|
body.descendants()
|
||||||
.filter_map(ast::NameRef::cast)
|
.filter_map(ast::NameRef::cast)
|
||||||
.filter_map(|name_ref| NameRefClass::classify(&ctx.sema, &name_ref))
|
.filter_map(|name_ref| NameRefClass::classify(&ctx.sema, &name_ref))
|
||||||
.map(|name_kind| name_kind.referenced())
|
.map(|name_kind| match name_kind {
|
||||||
|
NameRefClass::Definition(def) => def,
|
||||||
|
NameRefClass::FieldShorthand { local_ref, field_ref: _ } => {
|
||||||
|
Definition::Local(local_ref)
|
||||||
|
}
|
||||||
|
})
|
||||||
.filter_map(|definition| match definition {
|
.filter_map(|definition| match definition {
|
||||||
Definition::Local(local) => Some(local),
|
Definition::Local(local) => Some(local),
|
||||||
_ => None,
|
_ => None,
|
||||||
|
|
|
@ -112,12 +112,11 @@ pub enum NameClass {
|
||||||
/// `None` in `if let None = Some(82) {}`.
|
/// `None` in `if let None = Some(82) {}`.
|
||||||
/// Syntactically, it is a name, but semantically it is a reference.
|
/// Syntactically, it is a name, but semantically it is a reference.
|
||||||
ConstReference(Definition),
|
ConstReference(Definition),
|
||||||
/// `field` in `if let Foo { field } = foo`. Here, `ast::Name` both Here the
|
/// `field` in `if let Foo { field } = foo`. Here, `ast::Name` both introduces
|
||||||
/// name both introduces a definition into a local scope, and refers to an
|
/// a definition into a local scope, and refers to an existing definition.
|
||||||
/// existing definition.
|
|
||||||
PatFieldShorthand {
|
PatFieldShorthand {
|
||||||
local_def: Local,
|
local_def: Local,
|
||||||
field_ref: Definition,
|
field_ref: Field,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -134,14 +133,6 @@ impl NameClass {
|
||||||
Some(res)
|
Some(res)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// `Definition` referenced or defined by this name.
|
|
||||||
pub fn referenced_or_defined(self) -> Definition {
|
|
||||||
match self {
|
|
||||||
NameClass::Definition(it) | NameClass::ConstReference(it) => it,
|
|
||||||
NameClass::PatFieldShorthand { local_def: _, field_ref } => field_ref,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn classify(sema: &Semantics<RootDatabase>, name: &ast::Name) -> Option<NameClass> {
|
pub fn classify(sema: &Semantics<RootDatabase>, name: &ast::Name) -> Option<NameClass> {
|
||||||
let _p = profile::span("classify_name");
|
let _p = profile::span("classify_name");
|
||||||
|
|
||||||
|
@ -183,7 +174,12 @@ impl NameClass {
|
||||||
})
|
})
|
||||||
.and_then(|name_ref| NameRefClass::classify(sema, &name_ref))?;
|
.and_then(|name_ref| NameRefClass::classify(sema, &name_ref))?;
|
||||||
|
|
||||||
Some(NameClass::Definition(name_ref_class.referenced()))
|
Some(NameClass::Definition(match name_ref_class {
|
||||||
|
NameRefClass::Definition(def) => def,
|
||||||
|
NameRefClass::FieldShorthand { local_ref: _, field_ref } => {
|
||||||
|
Definition::Field(field_ref)
|
||||||
|
}
|
||||||
|
}))
|
||||||
} else {
|
} else {
|
||||||
let extern_crate = it.syntax().parent().and_then(ast::ExternCrate::cast)?;
|
let extern_crate = it.syntax().parent().and_then(ast::ExternCrate::cast)?;
|
||||||
let krate = sema.resolve_extern_crate(&extern_crate)?;
|
let krate = sema.resolve_extern_crate(&extern_crate)?;
|
||||||
|
@ -197,7 +193,6 @@ impl NameClass {
|
||||||
if let Some(record_pat_field) = it.syntax().parent().and_then(ast::RecordPatField::cast) {
|
if let Some(record_pat_field) = it.syntax().parent().and_then(ast::RecordPatField::cast) {
|
||||||
if record_pat_field.name_ref().is_none() {
|
if record_pat_field.name_ref().is_none() {
|
||||||
if let Some(field) = sema.resolve_record_pat_field(&record_pat_field) {
|
if let Some(field) = sema.resolve_record_pat_field(&record_pat_field) {
|
||||||
let field = Definition::Field(field);
|
|
||||||
return Some(NameClass::PatFieldShorthand { local_def: local, field_ref: field });
|
return Some(NameClass::PatFieldShorthand { local_def: local, field_ref: field });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -302,22 +297,10 @@ impl NameClass {
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum NameRefClass {
|
pub enum NameRefClass {
|
||||||
Definition(Definition),
|
Definition(Definition),
|
||||||
FieldShorthand { local_ref: Local, field_ref: Definition },
|
FieldShorthand { local_ref: Local, field_ref: Field },
|
||||||
}
|
}
|
||||||
|
|
||||||
impl NameRefClass {
|
impl NameRefClass {
|
||||||
/// `Definition`, which this name refers to.
|
|
||||||
pub fn referenced(self) -> Definition {
|
|
||||||
match self {
|
|
||||||
NameRefClass::Definition(def) => def,
|
|
||||||
NameRefClass::FieldShorthand { local_ref, field_ref: _ } => {
|
|
||||||
// FIXME: this is inherently ambiguous -- this name refers to
|
|
||||||
// two different defs....
|
|
||||||
Definition::Local(local_ref)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Note: we don't have unit-tests for this rather important function.
|
// Note: we don't have unit-tests for this rather important function.
|
||||||
// It is primarily exercised via goto definition tests in `ide`.
|
// It is primarily exercised via goto definition tests in `ide`.
|
||||||
pub fn classify(
|
pub fn classify(
|
||||||
|
@ -342,9 +325,8 @@ impl NameRefClass {
|
||||||
|
|
||||||
if let Some(record_field) = ast::RecordExprField::for_field_name(name_ref) {
|
if let Some(record_field) = ast::RecordExprField::for_field_name(name_ref) {
|
||||||
if let Some((field, local, _)) = sema.resolve_record_field(&record_field) {
|
if let Some((field, local, _)) = sema.resolve_record_field(&record_field) {
|
||||||
let field = Definition::Field(field);
|
|
||||||
let res = match local {
|
let res = match local {
|
||||||
None => NameRefClass::Definition(field),
|
None => NameRefClass::Definition(Definition::Field(field)),
|
||||||
Some(local) => {
|
Some(local) => {
|
||||||
NameRefClass::FieldShorthand { field_ref: field, local_ref: local }
|
NameRefClass::FieldShorthand { field_ref: field, local_ref: local }
|
||||||
}
|
}
|
||||||
|
|
|
@ -550,6 +550,7 @@ impl<'a> FindUsages<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Some(NameRefClass::FieldShorthand { local_ref: local, field_ref: field }) => {
|
Some(NameRefClass::FieldShorthand { local_ref: local, field_ref: field }) => {
|
||||||
|
let field = Definition::Field(field);
|
||||||
let FileRange { file_id, range } = self.sema.original_range(name_ref.syntax());
|
let FileRange { file_id, range } = self.sema.original_range(name_ref.syntax());
|
||||||
let access = match self.def {
|
let access = match self.def {
|
||||||
Definition::Field(_) if field == self.def => reference_access(&field, name_ref),
|
Definition::Field(_) if field == self.def => reference_access(&field, name_ref),
|
||||||
|
@ -574,7 +575,7 @@ impl<'a> FindUsages<'a> {
|
||||||
match NameClass::classify(self.sema, name) {
|
match NameClass::classify(self.sema, name) {
|
||||||
Some(NameClass::PatFieldShorthand { local_def: _, field_ref })
|
Some(NameClass::PatFieldShorthand { local_def: _, field_ref })
|
||||||
if matches!(
|
if matches!(
|
||||||
self.def, Definition::Field(_) if field_ref == self.def
|
self.def, Definition::Field(_) if Definition::Field(field_ref) == self.def
|
||||||
) =>
|
) =>
|
||||||
{
|
{
|
||||||
let FileRange { file_id, range } = self.sema.original_range(name.syntax());
|
let FileRange { file_id, range } = self.sema.original_range(name.syntax());
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue