3405: More principled approach for gotodef for field shorhand r=matklad a=matklad

Callers can now decide for themselves if they should prefer field or
local definition. By default, it's the local.



bors r+
🤖

Co-authored-by: Aleksey Kladov <aleksey.kladov@gmail.com>
This commit is contained in:
bors[bot] 2020-03-02 18:01:50 +00:00 committed by GitHub
commit 7928ac4bbd
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 79 additions and 36 deletions

View file

@ -107,8 +107,11 @@ impl<'db, DB: HirDatabase> Semantics<'db, DB> {
self.analyze(field.syntax()).resolve_field(field)
}
pub fn resolve_record_field(&self, field: &ast::RecordField) -> Option<StructField> {
self.analyze(field.syntax()).resolve_record_field(field)
pub fn resolve_record_field(
&self,
field: &ast::RecordField,
) -> Option<(StructField, Option<Local>)> {
self.analyze(field.syntax()).resolve_record_field(self.db, field)
}
pub fn resolve_record_literal(&self, record_lit: &ast::RecordLit) -> Option<VariantDef> {

View file

@ -5,7 +5,7 @@
//!
//! So, this modules should not be used during hir construction, it exists
//! purely for "IDE needs".
use std::sync::Arc;
use std::{iter::once, sync::Arc};
use either::Either;
use hir_def::{
@ -25,8 +25,8 @@ use ra_syntax::{
};
use crate::{
db::HirDatabase, Adt, Const, EnumVariant, Function, Local, MacroDef, ModuleDef, Path, Static,
Struct, Trait, Type, TypeAlias, TypeParam,
db::HirDatabase, Adt, Const, EnumVariant, Function, Local, MacroDef, ModPath, ModuleDef, Path,
PathKind, Static, Struct, Trait, Type, TypeAlias, TypeParam,
};
/// `SourceAnalyzer` is a convenience wrapper which exposes HIR API in terms of
@ -162,16 +162,27 @@ impl SourceAnalyzer {
pub(crate) fn resolve_record_field(
&self,
db: &impl HirDatabase,
field: &ast::RecordField,
) -> Option<crate::StructField> {
let expr_id = match field.expr() {
Some(it) => self.expr_id(&it)?,
) -> Option<(crate::StructField, Option<Local>)> {
let (expr_id, local) = match field.expr() {
Some(it) => (self.expr_id(&it)?, None),
None => {
let src = InFile { file_id: self.file_id, value: field };
self.body_source_map.as_ref()?.field_init_shorthand_expr(src)?
let expr_id = self.body_source_map.as_ref()?.field_init_shorthand_expr(src)?;
let local_name = field.name_ref()?.as_name();
let path = ModPath::from_segments(PathKind::Plain, once(local_name));
let local = match self.resolver.resolve_path_in_value_ns_fully(db, &path) {
Some(ValueNs::LocalBinding(pat_id)) => {
Some(Local { pat_id, parent: self.resolver.body_owner()? })
}
_ => None,
};
(expr_id, local)
}
};
self.infer.as_ref()?.record_field_resolution(expr_id).map(|it| it.into())
let struct_field = self.infer.as_ref()?.record_field_resolution(expr_id)?;
Some((struct_field.into(), local))
}
pub(crate) fn resolve_record_literal(

View file

@ -76,6 +76,8 @@ pub(crate) fn reference_definition(
let name_kind = classify_name_ref(sema, name_ref);
if let Some(def) = name_kind {
let def = def.definition();
return match def.try_to_nav(sema.db) {
Some(nav) => ReferenceResult::Exact(nav),
None => ReferenceResult::Approximate(Vec::new()),
@ -795,8 +797,8 @@ mod tests {
Foo { x<|> };
}
",
"x RECORD_FIELD_DEF FileId(1) [13; 19) [13; 14)",
"x: i32|x",
"x BIND_PAT FileId(1) [42; 43)",
"x",
)
}
}

View file

@ -153,7 +153,7 @@ pub(crate) fn hover(db: &RootDatabase, position: FilePosition) -> Option<RangeIn
if let Some((node, name_kind)) = match_ast! {
match (token.parent()) {
ast::NameRef(name_ref) => {
classify_name_ref(&sema, &name_ref).map(|d| (name_ref.syntax().clone(), d))
classify_name_ref(&sema, &name_ref).map(|d| (name_ref.syntax().clone(), d.definition()))
},
ast::Name(name) => {
classify_name(&sema, &name).map(|d| (name.syntax().clone(), d.definition()))

View file

@ -27,7 +27,10 @@ use test_utils::tested_by;
use crate::{display::TryToNav, FilePosition, FileRange, NavigationTarget, RangeInfo};
pub(crate) use self::{classify::classify_name_ref, rename::rename};
pub(crate) use self::{
classify::{classify_name_ref, NameRefClass},
rename::rename,
};
pub(crate) use ra_ide_db::defs::{classify_name, NameDefinition};
pub use self::search_scope::SearchScope;
@ -160,7 +163,7 @@ fn find_name(
return Some(RangeInfo::new(range, (name.text().to_string(), def)));
}
let name_ref = find_node_at_offset::<ast::NameRef>(&syntax, position.offset)?;
let def = classify_name_ref(sema, &name_ref)?;
let def = classify_name_ref(sema, &name_ref)?.definition();
let range = name_ref.syntax().text_range();
Some(RangeInfo::new(range, (name_ref.text().to_string(), def)))
}
@ -212,6 +215,7 @@ fn process_definition(
// See https://github.com/rust-lang/rust/pull/68198#issuecomment-574269098
if let Some(d) = classify_name_ref(&sema, &name_ref) {
let d = d.definition();
if d == def {
let kind =
if is_record_lit_name_ref(&name_ref) || is_call_expr_name_ref(&name_ref) {

View file

@ -1,6 +1,6 @@
//! Functions that are used to classify an element from its definition or reference.
use hir::{PathResolution, Semantics};
use hir::{Local, PathResolution, Semantics};
use ra_ide_db::defs::NameDefinition;
use ra_ide_db::RootDatabase;
use ra_prof::profile;
@ -9,10 +9,24 @@ use test_utils::tested_by;
pub use ra_ide_db::defs::{from_module_def, from_struct_field};
pub enum NameRefClass {
NameDefinition(NameDefinition),
FieldShorthand { local: Local, field: NameDefinition },
}
impl NameRefClass {
pub fn definition(self) -> NameDefinition {
match self {
NameRefClass::NameDefinition(def) => def,
NameRefClass::FieldShorthand { local, field: _ } => NameDefinition::Local(local),
}
}
}
pub(crate) fn classify_name_ref(
sema: &Semantics<RootDatabase>,
name_ref: &ast::NameRef,
) -> Option<NameDefinition> {
) -> Option<NameRefClass> {
let _p = profile("classify_name_ref");
let parent = name_ref.syntax().parent()?;
@ -20,29 +34,34 @@ pub(crate) fn classify_name_ref(
if let Some(method_call) = ast::MethodCallExpr::cast(parent.clone()) {
tested_by!(goto_def_for_methods);
if let Some(func) = sema.resolve_method_call(&method_call) {
return Some(from_module_def(func.into()));
return Some(NameRefClass::NameDefinition(NameDefinition::ModuleDef(func.into())));
}
}
if let Some(field_expr) = ast::FieldExpr::cast(parent.clone()) {
tested_by!(goto_def_for_fields);
if let Some(field) = sema.resolve_field(&field_expr) {
return Some(from_struct_field(field));
return Some(NameRefClass::NameDefinition(NameDefinition::StructField(field)));
}
}
if let Some(record_field) = ast::RecordField::cast(parent.clone()) {
tested_by!(goto_def_for_record_fields);
tested_by!(goto_def_for_field_init_shorthand);
if let Some(field_def) = sema.resolve_record_field(&record_field) {
return Some(from_struct_field(field_def));
if let Some((field, local)) = sema.resolve_record_field(&record_field) {
let field = NameDefinition::StructField(field);
let res = match local {
None => NameRefClass::NameDefinition(field),
Some(local) => NameRefClass::FieldShorthand { field, local },
};
return Some(res);
}
}
if let Some(macro_call) = parent.ancestors().find_map(ast::MacroCall::cast) {
tested_by!(goto_def_for_macros);
if let Some(macro_def) = sema.resolve_macro_call(&macro_call) {
return Some(NameDefinition::Macro(macro_def));
return Some(NameRefClass::NameDefinition(NameDefinition::Macro(macro_def)));
}
}
@ -63,5 +82,5 @@ pub(crate) fn classify_name_ref(
PathResolution::Macro(def) => NameDefinition::Macro(def),
PathResolution::SelfType(impl_def) => NameDefinition::SelfType(impl_def),
};
Some(res)
Some(NameRefClass::NameDefinition(res))
}

View file

@ -19,7 +19,11 @@ use ra_syntax::{
};
use rustc_hash::FxHashMap;
use crate::{call_info::call_info_for_token, references::classify_name_ref, Analysis, FileId};
use crate::{
call_info::call_info_for_token,
references::{classify_name_ref, NameRefClass},
Analysis, FileId,
};
pub(crate) use html::highlight_as_html;
pub use tags::{Highlight, HighlightModifier, HighlightModifiers, HighlightTag};
@ -186,24 +190,24 @@ fn highlight_element(
}
// Highlight references like the definitions they resolve to
// Special-case field init shorthand
NAME_REF if element.parent().and_then(ast::RecordField::cast).is_some() => {
HighlightTag::Field.into()
}
NAME_REF if element.ancestors().any(|it| it.kind() == ATTR) => return None,
NAME_REF => {
let name_ref = element.into_node().and_then(ast::NameRef::cast).unwrap();
let name_kind = classify_name_ref(sema, &name_ref)?;
if let NameDefinition::Local(local) = &name_kind {
if let Some(name) = local.name(db) {
let shadow_count = bindings_shadow_count.entry(name.clone()).or_default();
binding_hash = Some(calc_binding_hash(&name, *shadow_count))
match name_kind {
NameRefClass::NameDefinition(def) => {
if let NameDefinition::Local(local) = &def {
if let Some(name) = local.name(db) {
let shadow_count =
bindings_shadow_count.entry(name.clone()).or_default();
binding_hash = Some(calc_binding_hash(&name, *shadow_count))
}
};
highlight_name(db, def)
}
};
highlight_name(db, name_kind)
NameRefClass::FieldShorthand { .. } => HighlightTag::Field.into(),
}
}
// Simple token-based highlighting