Treat ast::Name in field patterns as use

This commit is contained in:
Jonas Schievink 2020-10-09 19:55:30 +02:00
parent 95c498d913
commit 210456aeaa
2 changed files with 119 additions and 43 deletions

View file

@ -686,6 +686,52 @@ fn g() { f(); }
);
}
#[test]
fn test_find_all_refs_struct_pat() {
check(
r#"
struct S {
field<|>: u8,
}
fn f(s: S) {
match s {
S { field } => {}
}
}
"#,
expect![[r#"
field RECORD_FIELD FileId(0) 15..24 15..20 Other
FileId(0) 68..73 FieldShorthandForField Read
"#]],
);
}
#[test]
fn test_find_all_refs_enum_var_pat() {
check(
r#"
enum En {
Variant {
field<|>: u8,
}
}
fn f(e: En) {
match e {
En::Variant { field } => {}
}
}
"#,
expect![[r#"
field RECORD_FIELD FileId(0) 32..41 32..37 Other
FileId(0) 102..107 FieldShorthandForField Read
"#]],
);
}
fn check(ra_fixture: &str, expect: Expect) {
check_with_scope(ra_fixture, None, expect)
}

View file

@ -12,8 +12,9 @@ use once_cell::unsync::Lazy;
use rustc_hash::FxHashMap;
use syntax::{ast, match_ast, AstNode, TextRange, TextSize};
use crate::defs::NameClass;
use crate::{
defs::{classify_name_ref, Definition, NameRefClass},
defs::{classify_name, classify_name_ref, Definition, NameRefClass},
RootDatabase,
};
@ -226,9 +227,9 @@ impl<'a> FindUsages<'a> {
let search_scope = {
let base = self.def.search_scope(sema.db);
match self.scope {
match &self.scope {
None => base,
Some(scope) => base.intersection(&scope),
Some(scope) => base.intersection(scope),
}
};
@ -251,54 +252,83 @@ impl<'a> FindUsages<'a> {
continue;
}
let name_ref: ast::NameRef =
match sema.find_node_at_offset_with_descend(&tree, offset) {
Some(it) => it,
None => continue,
};
match classify_name_ref(&sema, &name_ref) {
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)
{
ReferenceKind::StructLiteral
} else {
ReferenceKind::Other
};
let reference = Reference {
file_range: sema.original_range(name_ref.syntax()),
kind,
access: reference_access(&def, &name_ref),
};
if sink(reference) {
match sema.find_node_at_offset_with_descend(&tree, offset) {
Some(name_ref) => {
if self.found_name_ref(&name_ref, sink) {
return;
}
}
Some(NameRefClass::FieldShorthand { local, field }) => {
let reference = match self.def {
Definition::Field(_) if &field == self.def => Reference {
file_range: self.sema.original_range(name_ref.syntax()),
kind: ReferenceKind::FieldShorthandForField,
access: reference_access(&field, &name_ref),
},
Definition::Local(l) if &local == l => Reference {
file_range: self.sema.original_range(name_ref.syntax()),
kind: ReferenceKind::FieldShorthandForLocal,
access: reference_access(&Definition::Local(local), &name_ref),
},
_ => continue, // not a usage
};
if sink(reference) {
return;
None => match sema.find_node_at_offset_with_descend(&tree, offset) {
Some(name) => {
if self.found_name(&name, sink) {
return;
}
}
}
_ => {} // not a usage
None => {}
},
}
}
}
}
fn found_name_ref(
&self,
name_ref: &ast::NameRef,
sink: &mut dyn FnMut(Reference) -> bool,
) -> bool {
match classify_name_ref(self.sema, &name_ref) {
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)
{
ReferenceKind::StructLiteral
} else {
ReferenceKind::Other
};
let reference = Reference {
file_range: self.sema.original_range(name_ref.syntax()),
kind,
access: reference_access(&def, &name_ref),
};
sink(reference)
}
Some(NameRefClass::FieldShorthand { local, field }) => {
let reference = match self.def {
Definition::Field(_) if &field == self.def => Reference {
file_range: self.sema.original_range(name_ref.syntax()),
kind: ReferenceKind::FieldShorthandForField,
access: reference_access(&field, &name_ref),
},
Definition::Local(l) if &local == l => Reference {
file_range: self.sema.original_range(name_ref.syntax()),
kind: ReferenceKind::FieldShorthandForLocal,
access: reference_access(&Definition::Local(local), &name_ref),
},
_ => return false, // not a usage
};
sink(reference)
}
_ => false, // not a usage
}
}
fn found_name(&self, name: &ast::Name, sink: &mut dyn FnMut(Reference) -> bool) -> bool {
match classify_name(self.sema, name) {
Some(NameClass::FieldShorthand { local: _, field }) => {
let reference = match self.def {
Definition::Field(_) if &field == self.def => Reference {
file_range: self.sema.original_range(name.syntax()),
kind: ReferenceKind::FieldShorthandForField,
// FIXME: mutable patterns should have `Write` access
access: Some(ReferenceAccess::Read),
},
_ => return false, // not a usage
};
sink(reference)
}
_ => false, // not a usage
}
}
}
fn reference_access(def: &Definition, name_ref: &ast::NameRef) -> Option<ReferenceAccess> {