diff options
Diffstat (limited to 'crates')
-rw-r--r-- | crates/assists/src/handlers/fix_visibility.rs | 10 | ||||
-rw-r--r-- | crates/hir_def/src/adt.rs | 28 | ||||
-rw-r--r-- | crates/ide/src/references.rs | 70 | ||||
-rw-r--r-- | crates/ide_db/src/search.rs | 116 |
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 |
341 | pub enum Foo { Bar { bar: () } } | 341 | pub 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}; | |||
14 | use crate::{ | 14 | use 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 | ||
299 | fn lower_fields(item_tree: &ItemTree, cfg_options: &CfgOptions, fields: &Fields) -> VariantData { | 300 | fn 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 | ||
323 | fn lower_field(item_tree: &ItemTree, field: &Field) -> FieldData { | 329 | fn 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#" | ||
693 | struct S { | ||
694 | field<|>: u8, | ||
695 | } | ||
696 | |||
697 | fn 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#" | ||
715 | enum En { | ||
716 | Variant { | ||
717 | field<|>: u8, | ||
718 | } | ||
719 | } | ||
720 | |||
721 | fn 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#" | ||
739 | mod m { | ||
740 | pub enum En { | ||
741 | Variant { | ||
742 | field<|>: u8, | ||
743 | } | ||
744 | } | ||
745 | } | ||
746 | |||
747 | fn 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; | |||
12 | use rustc_hash::FxHashMap; | 12 | use rustc_hash::FxHashMap; |
13 | use syntax::{ast, match_ast, AstNode, TextRange, TextSize}; | 13 | use syntax::{ast, match_ast, AstNode, TextRange, TextSize}; |
14 | 14 | ||
15 | use crate::defs::NameClass; | ||
15 | use crate::{ | 16 | use 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 | ||
304 | fn reference_access(def: &Definition, name_ref: &ast::NameRef) -> Option<ReferenceAccess> { | 334 | fn reference_access(def: &Definition, name_ref: &ast::NameRef) -> Option<ReferenceAccess> { |