aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--crates/assists/src/handlers/fix_visibility.rs10
-rw-r--r--crates/hir_def/src/adt.rs28
-rw-r--r--crates/ide/src/references.rs70
-rw-r--r--crates/ide_db/src/search.rs116
4 files changed, 166 insertions, 58 deletions
diff --git a/crates/assists/src/handlers/fix_visibility.rs b/crates/assists/src/handlers/fix_visibility.rs
index 7cd76ea06..d505e9444 100644
--- a/crates/assists/src/handlers/fix_visibility.rs
+++ b/crates/assists/src/handlers/fix_visibility.rs
@@ -324,14 +324,14 @@ pub struct Foo { pub bar: () }
324 324
325 #[test] 325 #[test]
326 fn fix_visibility_of_enum_variant_field() { 326 fn fix_visibility_of_enum_variant_field() {
327 check_assist( 327 // Enum variants, as well as their fields, always get the enum's visibility. In fact, rustc
328 // rejects any visibility specifiers on them, so this assist should never fire on them.
329 check_assist_not_applicable(
328 fix_visibility, 330 fix_visibility,
329 r"mod foo { pub enum Foo { Bar { bar: () } } } 331 r"mod foo { pub enum Foo { Bar { bar: () } } }
330 fn main() { foo::Foo::Bar { <|>bar: () }; } ", 332 fn main() { foo::Foo::Bar { <|>bar: () }; } ",
331 r"mod foo { pub enum Foo { Bar { $0pub(crate) bar: () } } }
332 fn main() { foo::Foo::Bar { bar: () }; } ",
333 ); 333 );
334 check_assist( 334 check_assist_not_applicable(
335 fix_visibility, 335 fix_visibility,
336 r" 336 r"
337//- /lib.rs 337//- /lib.rs
@@ -340,8 +340,6 @@ fn main() { foo::Foo::Bar { <|>bar: () }; }
340//- /foo.rs 340//- /foo.rs
341pub enum Foo { Bar { bar: () } } 341pub enum Foo { Bar { bar: () } }
342", 342",
343 r"pub enum Foo { Bar { $0pub(crate) bar: () } }
344",
345 ); 343 );
346 check_assist_not_applicable( 344 check_assist_not_applicable(
347 fix_visibility, 345 fix_visibility,
diff --git a/crates/hir_def/src/adt.rs b/crates/hir_def/src/adt.rs
index d69ff2fc7..6539959c3 100644
--- a/crates/hir_def/src/adt.rs
+++ b/crates/hir_def/src/adt.rs
@@ -14,7 +14,7 @@ use tt::{Delimiter, DelimiterKind, Leaf, Subtree, TokenTree};
14use crate::{ 14use crate::{
15 body::{CfgExpander, LowerCtx}, 15 body::{CfgExpander, LowerCtx},
16 db::DefDatabase, 16 db::DefDatabase,
17 item_tree::{AttrOwner, Field, Fields, ItemTree, ModItem}, 17 item_tree::{AttrOwner, Field, Fields, ItemTree, ModItem, RawVisibilityId},
18 src::HasChildSource, 18 src::HasChildSource,
19 src::HasSource, 19 src::HasSource,
20 trace::Trace, 20 trace::Trace,
@@ -91,7 +91,7 @@ impl StructData {
91 let cfg_options = db.crate_graph()[loc.container.module(db).krate].cfg_options.clone(); 91 let cfg_options = db.crate_graph()[loc.container.module(db).krate].cfg_options.clone();
92 92
93 let strukt = &item_tree[loc.id.value]; 93 let strukt = &item_tree[loc.id.value];
94 let variant_data = lower_fields(&item_tree, &cfg_options, &strukt.fields); 94 let variant_data = lower_fields(&item_tree, &cfg_options, &strukt.fields, None);
95 Arc::new(StructData { 95 Arc::new(StructData {
96 name: strukt.name.clone(), 96 name: strukt.name.clone(),
97 variant_data: Arc::new(variant_data), 97 variant_data: Arc::new(variant_data),
@@ -105,7 +105,7 @@ impl StructData {
105 let cfg_options = db.crate_graph()[loc.container.module(db).krate].cfg_options.clone(); 105 let cfg_options = db.crate_graph()[loc.container.module(db).krate].cfg_options.clone();
106 106
107 let union = &item_tree[loc.id.value]; 107 let union = &item_tree[loc.id.value];
108 let variant_data = lower_fields(&item_tree, &cfg_options, &union.fields); 108 let variant_data = lower_fields(&item_tree, &cfg_options, &union.fields, None);
109 109
110 Arc::new(StructData { 110 Arc::new(StructData {
111 name: union.name.clone(), 111 name: union.name.clone(),
@@ -126,7 +126,8 @@ impl EnumData {
126 for var_id in enum_.variants.clone() { 126 for var_id in enum_.variants.clone() {
127 if item_tree.attrs(var_id.into()).is_cfg_enabled(&cfg_options) { 127 if item_tree.attrs(var_id.into()).is_cfg_enabled(&cfg_options) {
128 let var = &item_tree[var_id]; 128 let var = &item_tree[var_id];
129 let var_data = lower_fields(&item_tree, &cfg_options, &var.fields); 129 let var_data =
130 lower_fields(&item_tree, &cfg_options, &var.fields, Some(enum_.visibility));
130 131
131 variants.alloc(EnumVariantData { 132 variants.alloc(EnumVariantData {
132 name: var.name.clone(), 133 name: var.name.clone(),
@@ -296,13 +297,18 @@ fn lower_struct(
296 } 297 }
297} 298}
298 299
299fn lower_fields(item_tree: &ItemTree, cfg_options: &CfgOptions, fields: &Fields) -> VariantData { 300fn lower_fields(
301 item_tree: &ItemTree,
302 cfg_options: &CfgOptions,
303 fields: &Fields,
304 override_visibility: Option<RawVisibilityId>,
305) -> VariantData {
300 match fields { 306 match fields {
301 Fields::Record(flds) => { 307 Fields::Record(flds) => {
302 let mut arena = Arena::new(); 308 let mut arena = Arena::new();
303 for field_id in flds.clone() { 309 for field_id in flds.clone() {
304 if item_tree.attrs(field_id.into()).is_cfg_enabled(cfg_options) { 310 if item_tree.attrs(field_id.into()).is_cfg_enabled(cfg_options) {
305 arena.alloc(lower_field(item_tree, &item_tree[field_id])); 311 arena.alloc(lower_field(item_tree, &item_tree[field_id], override_visibility));
306 } 312 }
307 } 313 }
308 VariantData::Record(arena) 314 VariantData::Record(arena)
@@ -311,7 +317,7 @@ fn lower_fields(item_tree: &ItemTree, cfg_options: &CfgOptions, fields: &Fields)
311 let mut arena = Arena::new(); 317 let mut arena = Arena::new();
312 for field_id in flds.clone() { 318 for field_id in flds.clone() {
313 if item_tree.attrs(field_id.into()).is_cfg_enabled(cfg_options) { 319 if item_tree.attrs(field_id.into()).is_cfg_enabled(cfg_options) {
314 arena.alloc(lower_field(item_tree, &item_tree[field_id])); 320 arena.alloc(lower_field(item_tree, &item_tree[field_id], override_visibility));
315 } 321 }
316 } 322 }
317 VariantData::Tuple(arena) 323 VariantData::Tuple(arena)
@@ -320,10 +326,14 @@ fn lower_fields(item_tree: &ItemTree, cfg_options: &CfgOptions, fields: &Fields)
320 } 326 }
321} 327}
322 328
323fn lower_field(item_tree: &ItemTree, field: &Field) -> FieldData { 329fn lower_field(
330 item_tree: &ItemTree,
331 field: &Field,
332 override_visibility: Option<RawVisibilityId>,
333) -> FieldData {
324 FieldData { 334 FieldData {
325 name: field.name.clone(), 335 name: field.name.clone(),
326 type_ref: field.type_ref.clone(), 336 type_ref: field.type_ref.clone(),
327 visibility: item_tree[field.visibility].clone(), 337 visibility: item_tree[override_visibility.unwrap_or(field.visibility)].clone(),
328 } 338 }
329} 339}
diff --git a/crates/ide/src/references.rs b/crates/ide/src/references.rs
index ae68b4392..9315f7354 100644
--- a/crates/ide/src/references.rs
+++ b/crates/ide/src/references.rs
@@ -686,6 +686,76 @@ fn g() { f(); }
686 ); 686 );
687 } 687 }
688 688
689 #[test]
690 fn test_find_all_refs_struct_pat() {
691 check(
692 r#"
693struct S {
694 field<|>: u8,
695}
696
697fn f(s: S) {
698 match s {
699 S { field } => {}
700 }
701}
702"#,
703 expect![[r#"
704 field RECORD_FIELD FileId(0) 15..24 15..20 Other
705
706 FileId(0) 68..73 FieldShorthandForField Read
707 "#]],
708 );
709 }
710
711 #[test]
712 fn test_find_all_refs_enum_var_pat() {
713 check(
714 r#"
715enum En {
716 Variant {
717 field<|>: u8,
718 }
719}
720
721fn f(e: En) {
722 match e {
723 En::Variant { field } => {}
724 }
725}
726"#,
727 expect![[r#"
728 field RECORD_FIELD FileId(0) 32..41 32..37 Other
729
730 FileId(0) 102..107 FieldShorthandForField Read
731 "#]],
732 );
733 }
734
735 #[test]
736 fn test_find_all_refs_enum_var_privacy() {
737 check(
738 r#"
739mod m {
740 pub enum En {
741 Variant {
742 field<|>: u8,
743 }
744 }
745}
746
747fn f() -> m::En {
748 m::En::Variant { field: 0 }
749}
750"#,
751 expect![[r#"
752 field RECORD_FIELD FileId(0) 56..65 56..61 Other
753
754 FileId(0) 125..130 Other Read
755 "#]],
756 );
757 }
758
689 fn check(ra_fixture: &str, expect: Expect) { 759 fn check(ra_fixture: &str, expect: Expect) {
690 check_with_scope(ra_fixture, None, expect) 760 check_with_scope(ra_fixture, None, expect)
691 } 761 }
diff --git a/crates/ide_db/src/search.rs b/crates/ide_db/src/search.rs
index edab1d644..8e3dcd99c 100644
--- a/crates/ide_db/src/search.rs
+++ b/crates/ide_db/src/search.rs
@@ -12,8 +12,9 @@ use once_cell::unsync::Lazy;
12use rustc_hash::FxHashMap; 12use rustc_hash::FxHashMap;
13use syntax::{ast, match_ast, AstNode, TextRange, TextSize}; 13use syntax::{ast, match_ast, AstNode, TextRange, TextSize};
14 14
15use crate::defs::NameClass;
15use crate::{ 16use crate::{
16 defs::{classify_name_ref, Definition, NameRefClass}, 17 defs::{classify_name, classify_name_ref, Definition, NameRefClass},
17 RootDatabase, 18 RootDatabase,
18}; 19};
19 20
@@ -226,9 +227,9 @@ impl<'a> FindUsages<'a> {
226 227
227 let search_scope = { 228 let search_scope = {
228 let base = self.def.search_scope(sema.db); 229 let base = self.def.search_scope(sema.db);
229 match self.scope { 230 match &self.scope {
230 None => base, 231 None => base,
231 Some(scope) => base.intersection(&scope), 232 Some(scope) => base.intersection(scope),
232 } 233 }
233 }; 234 };
234 235
@@ -251,54 +252,83 @@ impl<'a> FindUsages<'a> {
251 continue; 252 continue;
252 } 253 }
253 254
254 let name_ref: ast::NameRef = 255 match sema.find_node_at_offset_with_descend(&tree, offset) {
255 match sema.find_node_at_offset_with_descend(&tree, offset) { 256 Some(name_ref) => {
256 Some(it) => it, 257 if self.found_name_ref(&name_ref, sink) {
257 None => continue,
258 };
259
260 match classify_name_ref(&sema, &name_ref) {
261 Some(NameRefClass::Definition(def)) if &def == self.def => {
262 let kind = if is_record_lit_name_ref(&name_ref)
263 || is_call_expr_name_ref(&name_ref)
264 {
265 ReferenceKind::StructLiteral
266 } else {
267 ReferenceKind::Other
268 };
269
270 let reference = Reference {
271 file_range: sema.original_range(name_ref.syntax()),
272 kind,
273 access: reference_access(&def, &name_ref),
274 };
275 if sink(reference) {
276 return; 258 return;
277 } 259 }
278 } 260 }
279 Some(NameRefClass::FieldShorthand { local, field }) => { 261 None => match sema.find_node_at_offset_with_descend(&tree, offset) {
280 let reference = match self.def { 262 Some(name) => {
281 Definition::Field(_) if &field == self.def => Reference { 263 if self.found_name(&name, sink) {
282 file_range: self.sema.original_range(name_ref.syntax()), 264 return;
283 kind: ReferenceKind::FieldShorthandForField, 265 }
284 access: reference_access(&field, &name_ref),
285 },
286 Definition::Local(l) if &local == l => Reference {
287 file_range: self.sema.original_range(name_ref.syntax()),
288 kind: ReferenceKind::FieldShorthandForLocal,
289 access: reference_access(&Definition::Local(local), &name_ref),
290 },
291 _ => continue, // not a usage
292 };
293 if sink(reference) {
294 return;
295 } 266 }
296 } 267 None => {}
297 _ => {} // not a usage 268 },
298 } 269 }
299 } 270 }
300 } 271 }
301 } 272 }
273
274 fn found_name_ref(
275 &self,
276 name_ref: &ast::NameRef,
277 sink: &mut dyn FnMut(Reference) -> bool,
278 ) -> bool {
279 match classify_name_ref(self.sema, &name_ref) {
280 Some(NameRefClass::Definition(def)) if &def == self.def => {
281 let kind = if is_record_lit_name_ref(&name_ref) || is_call_expr_name_ref(&name_ref)
282 {
283 ReferenceKind::StructLiteral
284 } else {
285 ReferenceKind::Other
286 };
287
288 let reference = Reference {
289 file_range: self.sema.original_range(name_ref.syntax()),
290 kind,
291 access: reference_access(&def, &name_ref),
292 };
293 sink(reference)
294 }
295 Some(NameRefClass::FieldShorthand { local, field }) => {
296 let reference = match self.def {
297 Definition::Field(_) if &field == self.def => Reference {
298 file_range: self.sema.original_range(name_ref.syntax()),
299 kind: ReferenceKind::FieldShorthandForField,
300 access: reference_access(&field, &name_ref),
301 },
302 Definition::Local(l) if &local == l => Reference {
303 file_range: self.sema.original_range(name_ref.syntax()),
304 kind: ReferenceKind::FieldShorthandForLocal,
305 access: reference_access(&Definition::Local(local), &name_ref),
306 },
307 _ => return false, // not a usage
308 };
309 sink(reference)
310 }
311 _ => false, // not a usage
312 }
313 }
314
315 fn found_name(&self, name: &ast::Name, sink: &mut dyn FnMut(Reference) -> bool) -> bool {
316 match classify_name(self.sema, name) {
317 Some(NameClass::FieldShorthand { local: _, field }) => {
318 let reference = match self.def {
319 Definition::Field(_) if &field == self.def => Reference {
320 file_range: self.sema.original_range(name.syntax()),
321 kind: ReferenceKind::FieldShorthandForField,
322 // FIXME: mutable patterns should have `Write` access
323 access: Some(ReferenceAccess::Read),
324 },
325 _ => return false, // not a usage
326 };
327 sink(reference)
328 }
329 _ => false, // not a usage
330 }
331 }
302} 332}
303 333
304fn reference_access(def: &Definition, name_ref: &ast::NameRef) -> Option<ReferenceAccess> { 334fn reference_access(def: &Definition, name_ref: &ast::NameRef) -> Option<ReferenceAccess> {