6243: Clarify classification API r=matklad a=matklad

bors r+
🤖

Co-authored-by: Aleksey Kladov <aleksey.kladov@gmail.com>
This commit is contained in:
bors[bot] 2020-10-15 15:39:35 +00:00 committed by GitHub
commit d8c6e192f7
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
11 changed files with 246 additions and 233 deletions

View file

@ -1,4 +1,4 @@
use ide_db::defs::{classify_name_ref, Definition, NameRefClass}; use ide_db::defs::{Definition, NameRefClass};
use syntax::{ast, AstNode, SyntaxKind, T}; use syntax::{ast, AstNode, SyntaxKind, T};
use test_utils::mark; use test_utils::mark;
@ -39,7 +39,7 @@ pub(crate) fn add_turbo_fish(acc: &mut Assists, ctx: &AssistContext) -> Option<(
return None; return None;
} }
let name_ref = ast::NameRef::cast(ident.parent())?; let name_ref = ast::NameRef::cast(ident.parent())?;
let def = match classify_name_ref(&ctx.sema, &name_ref)? { let def = match NameRefClass::classify(&ctx.sema, &name_ref)? {
NameRefClass::Definition(def) => def, NameRefClass::Definition(def) => def,
NameRefClass::ExternCrate(_) | NameRefClass::FieldShorthand { .. } => return None, NameRefClass::ExternCrate(_) | NameRefClass::FieldShorthand { .. } => return None,
}; };

View file

@ -1,7 +1,7 @@
use either::Either; use either::Either;
use hir::{AssocItem, MacroDef, Module, ModuleDef, Name, PathResolution, ScopeDef}; use hir::{AssocItem, MacroDef, Module, ModuleDef, Name, PathResolution, ScopeDef};
use ide_db::{ use ide_db::{
defs::{classify_name_ref, Definition, NameRefClass}, defs::{Definition, NameRefClass},
search::SearchScope, search::SearchScope,
}; };
use syntax::{ use syntax::{
@ -217,7 +217,7 @@ fn find_imported_defs(ctx: &AssistContext, star: SyntaxToken) -> Option<Vec<Def>
.flatten() .flatten()
.filter_map(|n| Some(n.descendants().filter_map(ast::NameRef::cast))) .filter_map(|n| Some(n.descendants().filter_map(ast::NameRef::cast)))
.flatten() .flatten()
.filter_map(|r| match classify_name_ref(&ctx.sema, &r)? { .filter_map(|r| match NameRefClass::classify(&ctx.sema, &r)? {
NameRefClass::Definition(Definition::ModuleDef(def)) => Some(Def::ModuleDef(def)), NameRefClass::Definition(Definition::ModuleDef(def)) => Some(Def::ModuleDef(def)),
NameRefClass::Definition(Definition::Macro(def)) => Some(Def::MacroDef(def)), NameRefClass::Definition(Definition::Macro(def)) => Some(Def::MacroDef(def)),
_ => None, _ => None,

View file

@ -14,7 +14,7 @@ use hir::{
ModuleDef, ModuleDef,
}; };
use ide_db::{ use ide_db::{
defs::{classify_name, classify_name_ref, Definition}, defs::{Definition, NameClass, NameRefClass},
RootDatabase, RootDatabase,
}; };
use syntax::{ast, match_ast, AstNode, SyntaxKind::*, SyntaxToken, TokenAtOffset, T}; use syntax::{ast, match_ast, AstNode, SyntaxKind::*, SyntaxToken, TokenAtOffset, T};
@ -232,8 +232,8 @@ 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) => classify_name_ref(&sema, &name_ref).map(|d| d.definition(sema.db)), ast::NameRef(name_ref) => NameRefClass::classify(&sema, &name_ref).map(|d| d.referenced(sema.db)),
ast::Name(name) => classify_name(&sema, &name).map(|d| d.definition(sema.db)), ast::Name(name) => NameClass::classify(&sema, &name).map(|d| d.referenced_or_defined(sema.db)),
_ => None, _ => None,
} }
}; };

View file

@ -1,6 +1,6 @@
use hir::Semantics; use hir::Semantics;
use ide_db::{ use ide_db::{
defs::{classify_name, classify_name_ref}, defs::{NameClass, NameRefClass},
symbol_index, RootDatabase, symbol_index, RootDatabase,
}; };
use syntax::{ use syntax::{
@ -40,7 +40,7 @@ pub(crate) fn goto_definition(
reference_definition(&sema, &name_ref).to_vec() reference_definition(&sema, &name_ref).to_vec()
}, },
ast::Name(name) => { ast::Name(name) => {
let def = classify_name(&sema, &name)?.definition(sema.db); let def = NameClass::classify(&sema, &name)?.referenced_or_defined(sema.db);
let nav = def.try_to_nav(sema.db)?; let nav = def.try_to_nav(sema.db)?;
vec![nav] vec![nav]
}, },
@ -81,9 +81,9 @@ pub(crate) fn reference_definition(
sema: &Semantics<RootDatabase>, sema: &Semantics<RootDatabase>,
name_ref: &ast::NameRef, name_ref: &ast::NameRef,
) -> ReferenceResult { ) -> ReferenceResult {
let name_kind = classify_name_ref(sema, name_ref); let name_kind = NameRefClass::classify(sema, name_ref);
if let Some(def) = name_kind { if let Some(def) = name_kind {
let def = def.definition(sema.db); let def = def.referenced(sema.db);
return match def.try_to_nav(sema.db) { return match def.try_to_nav(sema.db) {
Some(nav) => ReferenceResult::Exact(nav), Some(nav) => ReferenceResult::Exact(nav),
None => ReferenceResult::Approximate(Vec::new()), None => ReferenceResult::Approximate(Vec::new()),

View file

@ -4,7 +4,7 @@ use hir::{
Module, ModuleDef, ModuleSource, Semantics, Module, ModuleDef, ModuleSource, Semantics,
}; };
use ide_db::{ use ide_db::{
defs::{classify_name, classify_name_ref, Definition}, defs::{Definition, NameClass, NameRefClass},
RootDatabase, RootDatabase,
}; };
use itertools::Itertools; use itertools::Itertools;
@ -107,8 +107,8 @@ pub(crate) fn hover(
let node = token.parent(); let node = token.parent();
let definition = match_ast! { let definition = match_ast! {
match node { match node {
ast::NameRef(name_ref) => classify_name_ref(&sema, &name_ref).map(|d| d.definition(sema.db)), ast::Name(name) => NameClass::classify(&sema, &name).and_then(|d| d.defined(sema.db)),
ast::Name(name) => classify_name(&sema, &name).and_then(|d| d.into_definition(sema.db)), ast::NameRef(name_ref) => NameRefClass::classify(&sema, &name_ref).map(|d| d.referenced(sema.db)),
_ => None, _ => None,
} }
}; };

View file

@ -13,7 +13,7 @@ pub(crate) mod rename;
use hir::Semantics; use hir::Semantics;
use ide_db::{ use ide_db::{
defs::{classify_name, classify_name_ref, Definition}, defs::{Definition, NameClass, NameRefClass},
search::SearchScope, search::SearchScope,
RootDatabase, RootDatabase,
}; };
@ -132,13 +132,13 @@ fn find_name(
opt_name: Option<ast::Name>, opt_name: Option<ast::Name>,
) -> Option<RangeInfo<Definition>> { ) -> Option<RangeInfo<Definition>> {
if let Some(name) = opt_name { if let Some(name) = opt_name {
let def = classify_name(sema, &name)?.definition(sema.db); let def = NameClass::classify(sema, &name)?.referenced_or_defined(sema.db);
let range = name.syntax().text_range(); let range = name.syntax().text_range();
return Some(RangeInfo::new(range, def)); return Some(RangeInfo::new(range, def));
} }
let name_ref = let name_ref =
sema.find_node_at_offset_with_descend::<ast::NameRef>(&syntax, position.offset)?; sema.find_node_at_offset_with_descend::<ast::NameRef>(&syntax, position.offset)?;
let def = classify_name_ref(sema, &name_ref)?.definition(sema.db); let def = NameRefClass::classify(sema, &name_ref)?.referenced(sema.db);
let range = name_ref.syntax().text_range(); let range = name_ref.syntax().text_range();
Some(RangeInfo::new(range, def)) Some(RangeInfo::new(range, def))
} }

View file

@ -3,7 +3,7 @@
use base_db::SourceDatabaseExt; use base_db::SourceDatabaseExt;
use hir::{Module, ModuleDef, ModuleSource, Semantics}; use hir::{Module, ModuleDef, ModuleSource, Semantics};
use ide_db::{ use ide_db::{
defs::{classify_name, classify_name_ref, Definition, NameClass, NameRefClass}, defs::{Definition, NameClass, NameRefClass},
RootDatabase, RootDatabase,
}; };
@ -88,13 +88,13 @@ fn find_module_at_offset(
let module = match_ast! { let module = match_ast! {
match (ident.parent()) { match (ident.parent()) {
ast::NameRef(name_ref) => { ast::NameRef(name_ref) => {
match classify_name_ref(sema, &name_ref)? { match NameRefClass::classify(sema, &name_ref)? {
NameRefClass::Definition(Definition::ModuleDef(ModuleDef::Module(module))) => module, NameRefClass::Definition(Definition::ModuleDef(ModuleDef::Module(module))) => module,
_ => return None, _ => return None,
} }
}, },
ast::Name(name) => { ast::Name(name) => {
match classify_name(&sema, &name)? { match NameClass::classify(&sema, &name)? {
NameClass::Definition(Definition::ModuleDef(ModuleDef::Module(module))) => module, NameClass::Definition(Definition::ModuleDef(ModuleDef::Module(module))) => module,
_ => return None, _ => return None,
} }

View file

@ -8,7 +8,7 @@ mod tests;
use hir::{Local, Name, Semantics, VariantDef}; use hir::{Local, Name, Semantics, VariantDef};
use ide_db::{ use ide_db::{
defs::{classify_name, classify_name_ref, Definition, NameClass, NameRefClass}, defs::{Definition, NameClass, NameRefClass},
RootDatabase, RootDatabase,
}; };
use rustc_hash::FxHashMap; use rustc_hash::FxHashMap;
@ -443,7 +443,7 @@ fn highlight_element(
// Highlight definitions depending on the "type" of the definition. // Highlight definitions depending on the "type" of the definition.
NAME => { NAME => {
let name = element.into_node().and_then(ast::Name::cast).unwrap(); let name = element.into_node().and_then(ast::Name::cast).unwrap();
let name_kind = classify_name(sema, &name); let name_kind = NameClass::classify(sema, &name);
if let Some(NameClass::Definition(Definition::Local(local))) = &name_kind { if let Some(NameClass::Definition(Definition::Local(local))) = &name_kind {
if let Some(name) = local.name(db) { if let Some(name) = local.name(db) {
@ -459,9 +459,9 @@ fn highlight_element(
highlight_def(db, def) | HighlightModifier::Definition highlight_def(db, def) | HighlightModifier::Definition
} }
Some(NameClass::ConstReference(def)) => highlight_def(db, def), Some(NameClass::ConstReference(def)) => highlight_def(db, def),
Some(NameClass::FieldShorthand { field, .. }) => { Some(NameClass::PatFieldShorthand { field_ref, .. }) => {
let mut h = HighlightTag::Field.into(); let mut h = HighlightTag::Field.into();
if let Definition::Field(field) = field { if let Definition::Field(field) = field_ref {
if let VariantDef::Union(_) = field.parent_def(db) { if let VariantDef::Union(_) = field.parent_def(db) {
h |= HighlightModifier::Unsafe; h |= HighlightModifier::Unsafe;
} }
@ -480,7 +480,7 @@ fn highlight_element(
NAME_REF => { NAME_REF => {
let name_ref = element.into_node().and_then(ast::NameRef::cast).unwrap(); let name_ref = element.into_node().and_then(ast::NameRef::cast).unwrap();
highlight_func_by_name_ref(sema, &name_ref).unwrap_or_else(|| { highlight_func_by_name_ref(sema, &name_ref).unwrap_or_else(|| {
match classify_name_ref(sema, &name_ref) { match NameRefClass::classify(sema, &name_ref) {
Some(name_kind) => match name_kind { Some(name_kind) => match name_kind {
NameRefClass::ExternCrate(_) => HighlightTag::Module.into(), NameRefClass::ExternCrate(_) => HighlightTag::Module.into(),
NameRefClass::Definition(def) => { NameRefClass::Definition(def) => {

View file

@ -81,34 +81,39 @@ impl Definition {
pub enum NameClass { pub enum NameClass {
ExternCrate(Crate), ExternCrate(Crate),
Definition(Definition), Definition(Definition),
/// `None` in `if let None = Some(82) {}` /// `None` in `if let None = Some(82) {}`.
ConstReference(Definition), ConstReference(Definition),
FieldShorthand { /// `field` in `if let Foo { field } = foo`.
local: Local, PatFieldShorthand {
field: Definition, local_def: Local,
field_ref: Definition,
}, },
} }
impl NameClass { impl NameClass {
pub fn into_definition(self, db: &dyn HirDatabase) -> Option<Definition> { /// `Definition` defined by this name.
Some(match self { pub fn defined(self, db: &dyn HirDatabase) -> Option<Definition> {
let res = match self {
NameClass::ExternCrate(krate) => Definition::ModuleDef(krate.root_module(db).into()), NameClass::ExternCrate(krate) => Definition::ModuleDef(krate.root_module(db).into()),
NameClass::Definition(it) => it, NameClass::Definition(it) => it,
NameClass::ConstReference(_) => return None, NameClass::ConstReference(_) => return None,
NameClass::FieldShorthand { local, field: _ } => Definition::Local(local), NameClass::PatFieldShorthand { local_def, field_ref: _ } => {
}) Definition::Local(local_def)
}
};
Some(res)
} }
pub fn definition(self, db: &dyn HirDatabase) -> Definition { /// `Definition` referenced or defined by this name.
pub fn referenced_or_defined(self, db: &dyn HirDatabase) -> Definition {
match self { match self {
NameClass::ExternCrate(krate) => Definition::ModuleDef(krate.root_module(db).into()), NameClass::ExternCrate(krate) => Definition::ModuleDef(krate.root_module(db).into()),
NameClass::Definition(it) | NameClass::ConstReference(it) => it, NameClass::Definition(it) | NameClass::ConstReference(it) => it,
NameClass::FieldShorthand { local: _, field } => field, NameClass::PatFieldShorthand { local_def: _, field_ref } => field_ref,
} }
} }
}
pub fn classify_name(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");
let parent = name.syntax().parent()?; let parent = name.syntax().parent()?;
@ -145,9 +150,9 @@ pub fn classify_name(sema: &Semantics<RootDatabase>, name: &ast::Name) -> Option
let path_segment = path.segment()?; let path_segment = path.segment()?;
path_segment.name_ref() path_segment.name_ref()
}) })
.and_then(|name_ref| classify_name_ref(sema, &name_ref))?; .and_then(|name_ref| NameRefClass::classify(sema, &name_ref))?;
Some(NameClass::Definition(name_ref_class.definition(sema.db))) Some(NameClass::Definition(name_ref_class.referenced(sema.db)))
} 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 resolved = sema.resolve_extern_crate(&extern_crate)?; let resolved = sema.resolve_extern_crate(&extern_crate)?;
@ -161,7 +166,7 @@ pub fn classify_name(sema: &Semantics<RootDatabase>, name: &ast::Name) -> Option
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); let field = Definition::Field(field);
return Some(NameClass::FieldShorthand { local, field }); return Some(NameClass::PatFieldShorthand { local_def: local, field_ref: field });
} }
} }
} }
@ -223,31 +228,36 @@ pub fn classify_name(sema: &Semantics<RootDatabase>, name: &ast::Name) -> Option
_ => None, _ => None,
} }
} }
}
} }
#[derive(Debug)] #[derive(Debug)]
pub enum NameRefClass { pub enum NameRefClass {
ExternCrate(Crate), ExternCrate(Crate),
Definition(Definition), Definition(Definition),
FieldShorthand { local: Local, field: Definition }, FieldShorthand { local_ref: Local, field_ref: Definition },
} }
impl NameRefClass { impl NameRefClass {
pub fn definition(self, db: &dyn HirDatabase) -> Definition { /// `Definition`, which this name refers to.
pub fn referenced(self, db: &dyn HirDatabase) -> Definition {
match self { match self {
NameRefClass::ExternCrate(krate) => Definition::ModuleDef(krate.root_module(db).into()), NameRefClass::ExternCrate(krate) => Definition::ModuleDef(krate.root_module(db).into()),
NameRefClass::Definition(def) => def, NameRefClass::Definition(def) => def,
NameRefClass::FieldShorthand { local, field: _ } => Definition::Local(local), 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_name_ref( pub fn classify(
sema: &Semantics<RootDatabase>, sema: &Semantics<RootDatabase>,
name_ref: &ast::NameRef, name_ref: &ast::NameRef,
) -> Option<NameRefClass> { ) -> Option<NameRefClass> {
let _p = profile::span("classify_name_ref"); let _p = profile::span("classify_name_ref");
let parent = name_ref.syntax().parent()?; let parent = name_ref.syntax().parent()?;
@ -269,7 +279,9 @@ pub fn classify_name_ref(
let field = Definition::Field(field); let field = Definition::Field(field);
let res = match local { let res = match local {
None => NameRefClass::Definition(field), None => NameRefClass::Definition(field),
Some(local) => NameRefClass::FieldShorthand { field, local }, Some(local) => {
NameRefClass::FieldShorthand { field_ref: field, local_ref: local }
}
}; };
return Some(res); return Some(res);
} }
@ -325,6 +337,7 @@ pub fn classify_name_ref(
let extern_crate = ast::ExternCrate::cast(parent)?; let extern_crate = ast::ExternCrate::cast(parent)?;
let resolved = sema.resolve_extern_crate(&extern_crate)?; let resolved = sema.resolve_extern_crate(&extern_crate)?;
Some(NameRefClass::ExternCrate(resolved)) Some(NameRefClass::ExternCrate(resolved))
}
} }
impl From<PathResolution> for Definition { impl From<PathResolution> for Definition {

View file

@ -5,7 +5,7 @@ use hir::{Crate, MacroDef, ModuleDef, Semantics};
use syntax::{ast, AstNode, SyntaxKind::NAME}; use syntax::{ast, AstNode, SyntaxKind::NAME};
use crate::{ use crate::{
defs::{classify_name, Definition}, defs::{Definition, NameClass},
symbol_index::{self, FileSymbol, Query}, symbol_index::{self, FileSymbol, Query},
RootDatabase, RootDatabase,
}; };
@ -60,5 +60,5 @@ fn get_name_definition<'a>(
candidate_node candidate_node
}; };
let name = ast::Name::cast(candidate_name_node)?; let name = ast::Name::cast(candidate_name_node)?;
classify_name(sema, &name)?.into_definition(sema.db) NameClass::classify(sema, &name)?.defined(sema.db)
} }

View file

@ -14,7 +14,7 @@ use syntax::{ast, match_ast, AstNode, TextRange, TextSize};
use crate::defs::NameClass; use crate::defs::NameClass;
use crate::{ use crate::{
defs::{classify_name, classify_name_ref, Definition, NameRefClass}, defs::{Definition, NameRefClass},
RootDatabase, RootDatabase,
}; };
@ -276,7 +276,7 @@ impl<'a> FindUsages<'a> {
name_ref: &ast::NameRef, name_ref: &ast::NameRef,
sink: &mut dyn FnMut(Reference) -> bool, sink: &mut dyn FnMut(Reference) -> bool,
) -> bool { ) -> bool {
match classify_name_ref(self.sema, &name_ref) { match NameRefClass::classify(self.sema, &name_ref) {
Some(NameRefClass::Definition(def)) if &def == self.def => { Some(NameRefClass::Definition(def)) if &def == self.def => {
let kind = if is_record_lit_name_ref(&name_ref) || is_call_expr_name_ref(&name_ref) let kind = if is_record_lit_name_ref(&name_ref) || is_call_expr_name_ref(&name_ref)
{ {
@ -292,7 +292,7 @@ impl<'a> FindUsages<'a> {
}; };
sink(reference) sink(reference)
} }
Some(NameRefClass::FieldShorthand { local, field }) => { Some(NameRefClass::FieldShorthand { local_ref: local, field_ref: field }) => {
let reference = match self.def { let reference = match self.def {
Definition::Field(_) if &field == self.def => Reference { Definition::Field(_) if &field == self.def => Reference {
file_range: self.sema.original_range(name_ref.syntax()), file_range: self.sema.original_range(name_ref.syntax()),
@ -313,10 +313,10 @@ impl<'a> FindUsages<'a> {
} }
fn found_name(&self, name: &ast::Name, sink: &mut dyn FnMut(Reference) -> bool) -> bool { fn found_name(&self, name: &ast::Name, sink: &mut dyn FnMut(Reference) -> bool) -> bool {
match classify_name(self.sema, name) { match NameClass::classify(self.sema, name) {
Some(NameClass::FieldShorthand { local: _, field }) => { Some(NameClass::PatFieldShorthand { local_def: _, field_ref }) => {
let reference = match self.def { let reference = match self.def {
Definition::Field(_) if &field == self.def => Reference { Definition::Field(_) if &field_ref == self.def => Reference {
file_range: self.sema.original_range(name.syntax()), file_range: self.sema.original_range(name.syntax()),
kind: ReferenceKind::FieldShorthandForField, kind: ReferenceKind::FieldShorthandForField,
// FIXME: mutable patterns should have `Write` access // FIXME: mutable patterns should have `Write` access