Skip to content

Commit bc57bd8

Browse files
committed
Auto merge of #76499 - guswynn:priv_des, r=petrochenkov
Give better diagnostic when using a private tuple struct constructor Fixes #75907 Some notes: 1. This required some deep changes, including removing a Copy impl for PatKind. If some tests fail, I would still appreciate review on the overall approach 2. this only works with basic patterns (no wildcards for example), and fails if there is any problems getting the visibility of the fields (i am not sure what the failure that can happen in resolve_visibility_speculative, but we check the length of our fields in both cases against each other, so if anything goes wrong, we fall back to the worse error. This could be extended to more patterns 3. this does not yet deal with #75906, but I believe it will be similar 4. let me know if you want more tests 5. doesn't yet at the suggestion that `@yoshuawuyts` suggested at the end of their issue, but that could be added relatively easily (i believe)
2 parents 141bb23 + c63f634 commit bc57bd8

File tree

9 files changed

+152
-29
lines changed

9 files changed

+152
-29
lines changed

compiler/rustc_resolve/src/build_reduced_graph.rs

+10-7
Original file line numberDiff line numberDiff line change
@@ -796,23 +796,26 @@ impl<'a, 'b> BuildReducedGraphVisitor<'a, 'b> {
796796
vis
797797
};
798798

799+
let mut ret_fields = Vec::with_capacity(vdata.fields().len());
800+
799801
for field in vdata.fields() {
800802
// NOTE: The field may be an expansion placeholder, but expansion sets
801803
// correct visibilities for unnamed field placeholders specifically, so the
802804
// constructor visibility should still be determined correctly.
803-
if let Ok(field_vis) = self.resolve_visibility_speculative(&field.vis, true)
804-
{
805-
if ctor_vis.is_at_least(field_vis, &*self.r) {
806-
ctor_vis = field_vis;
807-
}
805+
let field_vis = self
806+
.resolve_visibility_speculative(&field.vis, true)
807+
.unwrap_or(ty::Visibility::Public);
808+
if ctor_vis.is_at_least(field_vis, &*self.r) {
809+
ctor_vis = field_vis;
808810
}
811+
ret_fields.push(field_vis);
809812
}
810813
let ctor_res = Res::Def(
811814
DefKind::Ctor(CtorOf::Struct, CtorKind::from_ast(vdata)),
812815
self.r.local_def_id(ctor_node_id).to_def_id(),
813816
);
814817
self.r.define(parent, ident, ValueNS, (ctor_res, ctor_vis, sp, expansion));
815-
self.r.struct_constructors.insert(def_id, (ctor_res, ctor_vis));
818+
self.r.struct_constructors.insert(def_id, (ctor_res, ctor_vis, ret_fields));
816819
}
817820
}
818821

@@ -964,7 +967,7 @@ impl<'a, 'b> BuildReducedGraphVisitor<'a, 'b> {
964967
Res::Def(DefKind::Ctor(CtorOf::Struct, ..), def_id) => {
965968
let parent = cstore.def_key(def_id).parent;
966969
if let Some(struct_def_id) = parent.map(|index| DefId { index, ..def_id }) {
967-
self.r.struct_constructors.insert(struct_def_id, (res, vis));
970+
self.r.struct_constructors.insert(struct_def_id, (res, vis, vec![]));
968971
}
969972
}
970973
_ => {}

compiler/rustc_resolve/src/late.rs

+19-11
Original file line numberDiff line numberDiff line change
@@ -188,7 +188,7 @@ crate enum PathSource<'a> {
188188
// Paths in struct expressions and patterns `Path { .. }`.
189189
Struct,
190190
// Paths in tuple struct patterns `Path(..)`.
191-
TupleStruct(Span),
191+
TupleStruct(Span, &'a [Span]),
192192
// `m::A::B` in `<T as m::A>::B::C`.
193193
TraitItem(Namespace),
194194
}
@@ -197,7 +197,7 @@ impl<'a> PathSource<'a> {
197197
fn namespace(self) -> Namespace {
198198
match self {
199199
PathSource::Type | PathSource::Trait(_) | PathSource::Struct => TypeNS,
200-
PathSource::Expr(..) | PathSource::Pat | PathSource::TupleStruct(_) => ValueNS,
200+
PathSource::Expr(..) | PathSource::Pat | PathSource::TupleStruct(..) => ValueNS,
201201
PathSource::TraitItem(ns) => ns,
202202
}
203203
}
@@ -208,7 +208,7 @@ impl<'a> PathSource<'a> {
208208
| PathSource::Expr(..)
209209
| PathSource::Pat
210210
| PathSource::Struct
211-
| PathSource::TupleStruct(_) => true,
211+
| PathSource::TupleStruct(..) => true,
212212
PathSource::Trait(_) | PathSource::TraitItem(..) => false,
213213
}
214214
}
@@ -219,7 +219,7 @@ impl<'a> PathSource<'a> {
219219
PathSource::Trait(_) => "trait",
220220
PathSource::Pat => "unit struct, unit variant or constant",
221221
PathSource::Struct => "struct, variant or union type",
222-
PathSource::TupleStruct(_) => "tuple struct or tuple variant",
222+
PathSource::TupleStruct(..) => "tuple struct or tuple variant",
223223
PathSource::TraitItem(ns) => match ns {
224224
TypeNS => "associated type",
225225
ValueNS => "method or associated constant",
@@ -305,7 +305,7 @@ impl<'a> PathSource<'a> {
305305
| Res::SelfCtor(..) => true,
306306
_ => false,
307307
},
308-
PathSource::TupleStruct(_) => match res {
308+
PathSource::TupleStruct(..) => match res {
309309
Res::Def(DefKind::Ctor(_, CtorKind::Fn), _) | Res::SelfCtor(..) => true,
310310
_ => false,
311311
},
@@ -340,8 +340,8 @@ impl<'a> PathSource<'a> {
340340
(PathSource::Struct, false) => error_code!(E0422),
341341
(PathSource::Expr(..), true) => error_code!(E0423),
342342
(PathSource::Expr(..), false) => error_code!(E0425),
343-
(PathSource::Pat | PathSource::TupleStruct(_), true) => error_code!(E0532),
344-
(PathSource::Pat | PathSource::TupleStruct(_), false) => error_code!(E0531),
343+
(PathSource::Pat | PathSource::TupleStruct(..), true) => error_code!(E0532),
344+
(PathSource::Pat | PathSource::TupleStruct(..), false) => error_code!(E0531),
345345
(PathSource::TraitItem(..), true) => error_code!(E0575),
346346
(PathSource::TraitItem(..), false) => error_code!(E0576),
347347
}
@@ -411,7 +411,7 @@ struct LateResolutionVisitor<'a, 'b, 'ast> {
411411
}
412412

413413
/// Walks the whole crate in DFS order, visiting each item, resolving names as it goes.
414-
impl<'a, 'ast> Visitor<'ast> for LateResolutionVisitor<'a, '_, 'ast> {
414+
impl<'a: 'ast, 'ast> Visitor<'ast> for LateResolutionVisitor<'a, '_, 'ast> {
415415
fn visit_item(&mut self, item: &'ast Item) {
416416
let prev = replace(&mut self.diagnostic_metadata.current_item, Some(item));
417417
// Always report errors in items we just entered.
@@ -659,7 +659,7 @@ impl<'a, 'ast> Visitor<'ast> for LateResolutionVisitor<'a, '_, 'ast> {
659659
}
660660
}
661661

662-
impl<'a, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
662+
impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
663663
fn new(resolver: &'b mut Resolver<'a>) -> LateResolutionVisitor<'a, 'b, 'ast> {
664664
// During late resolution we only track the module component of the parent scope,
665665
// although it may be useful to track other components as well for diagnostics.
@@ -1539,8 +1539,16 @@ impl<'a, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
15391539
.unwrap_or_else(|| self.fresh_binding(ident, pat.id, pat_src, bindings));
15401540
self.r.record_partial_res(pat.id, PartialRes::new(res));
15411541
}
1542-
PatKind::TupleStruct(ref path, ..) => {
1543-
self.smart_resolve_path(pat.id, None, path, PathSource::TupleStruct(pat.span));
1542+
PatKind::TupleStruct(ref path, ref sub_patterns) => {
1543+
self.smart_resolve_path(
1544+
pat.id,
1545+
None,
1546+
path,
1547+
PathSource::TupleStruct(
1548+
pat.span,
1549+
self.r.arenas.alloc_pattern_spans(sub_patterns.iter().map(|p| p.span)),
1550+
),
1551+
);
15441552
}
15451553
PatKind::Path(ref qself, ref path) => {
15461554
self.smart_resolve_path(pat.id, qself.as_ref(), path, PathSource::Pat);

compiler/rustc_resolve/src/late/diagnostics.rs

+45-10
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,7 @@ fn import_candidate_to_enum_paths(suggestion: &ImportSuggestion) -> (String, Str
9090
(variant_path_string, enum_path_string)
9191
}
9292

93-
impl<'a> LateResolutionVisitor<'a, '_, '_> {
93+
impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> {
9494
fn def_span(&self, def_id: DefId) -> Option<Span> {
9595
match def_id.krate {
9696
LOCAL_CRATE => self.r.opt_span(def_id),
@@ -623,12 +623,12 @@ impl<'a> LateResolutionVisitor<'a, '_, '_> {
623623
);
624624
}
625625
}
626-
PathSource::Expr(_) | PathSource::TupleStruct(_) | PathSource::Pat => {
626+
PathSource::Expr(_) | PathSource::TupleStruct(..) | PathSource::Pat => {
627627
let span = match &source {
628628
PathSource::Expr(Some(Expr {
629629
span, kind: ExprKind::Call(_, _), ..
630630
}))
631-
| PathSource::TupleStruct(span) => {
631+
| PathSource::TupleStruct(span, _) => {
632632
// We want the main underline to cover the suggested code as well for
633633
// cleaner output.
634634
err.set_span(*span);
@@ -640,7 +640,7 @@ impl<'a> LateResolutionVisitor<'a, '_, '_> {
640640
err.span_label(span, &format!("`{}` defined here", path_str));
641641
}
642642
let (tail, descr, applicability) = match source {
643-
PathSource::Pat | PathSource::TupleStruct(_) => {
643+
PathSource::Pat | PathSource::TupleStruct(..) => {
644644
("", "pattern", Applicability::MachineApplicable)
645645
}
646646
_ => (": val", "literal", Applicability::HasPlaceholders),
@@ -705,7 +705,7 @@ impl<'a> LateResolutionVisitor<'a, '_, '_> {
705705
}
706706
(
707707
Res::Def(DefKind::Enum, def_id),
708-
PathSource::TupleStruct(_) | PathSource::Expr(..),
708+
PathSource::TupleStruct(..) | PathSource::Expr(..),
709709
) => {
710710
if self
711711
.diagnostic_metadata
@@ -745,15 +745,50 @@ impl<'a> LateResolutionVisitor<'a, '_, '_> {
745745
}
746746
}
747747
(Res::Def(DefKind::Struct, def_id), _) if ns == ValueNS => {
748-
if let Some((ctor_def, ctor_vis)) = self.r.struct_constructors.get(&def_id).cloned()
748+
if let Some((ctor_def, ctor_vis, fields)) =
749+
self.r.struct_constructors.get(&def_id).cloned()
749750
{
750751
let accessible_ctor =
751752
self.r.is_accessible_from(ctor_vis, self.parent_scope.module);
752753
if is_expected(ctor_def) && !accessible_ctor {
753-
err.span_label(
754-
span,
755-
"constructor is not visible here due to private fields".to_string(),
756-
);
754+
let mut better_diag = false;
755+
if let PathSource::TupleStruct(_, pattern_spans) = source {
756+
if pattern_spans.len() > 0 && fields.len() == pattern_spans.len() {
757+
let non_visible_spans: Vec<Span> = fields
758+
.iter()
759+
.zip(pattern_spans.iter())
760+
.filter_map(|(vis, span)| {
761+
match self
762+
.r
763+
.is_accessible_from(*vis, self.parent_scope.module)
764+
{
765+
true => None,
766+
false => Some(*span),
767+
}
768+
})
769+
.collect();
770+
// Extra check to be sure
771+
if non_visible_spans.len() > 0 {
772+
let mut m: rustc_span::MultiSpan =
773+
non_visible_spans.clone().into();
774+
non_visible_spans.into_iter().for_each(|s| {
775+
m.push_span_label(s, "private field".to_string())
776+
});
777+
err.span_note(
778+
m,
779+
"constructor is not visible here due to private fields",
780+
);
781+
better_diag = true;
782+
}
783+
}
784+
}
785+
786+
if !better_diag {
787+
err.span_label(
788+
span,
789+
"constructor is not visible here due to private fields".to_string(),
790+
);
791+
}
757792
}
758793
} else {
759794
bad_struct_syntax_suggestion(def_id);

compiler/rustc_resolve/src/lib.rs

+6-1
Original file line numberDiff line numberDiff line change
@@ -1005,7 +1005,8 @@ pub struct Resolver<'a> {
10051005

10061006
/// Table for mapping struct IDs into struct constructor IDs,
10071007
/// it's not used during normal resolution, only for better error reporting.
1008-
struct_constructors: DefIdMap<(Res, ty::Visibility)>,
1008+
/// Also includes of list of each fields visibility
1009+
struct_constructors: DefIdMap<(Res, ty::Visibility, Vec<ty::Visibility>)>,
10091010

10101011
/// Features enabled for this crate.
10111012
active_features: FxHashSet<Symbol>,
@@ -1042,6 +1043,7 @@ pub struct ResolverArenas<'a> {
10421043
name_resolutions: TypedArena<RefCell<NameResolution<'a>>>,
10431044
macro_rules_bindings: TypedArena<MacroRulesBinding<'a>>,
10441045
ast_paths: TypedArena<ast::Path>,
1046+
pattern_spans: TypedArena<Span>,
10451047
}
10461048

10471049
impl<'a> ResolverArenas<'a> {
@@ -1073,6 +1075,9 @@ impl<'a> ResolverArenas<'a> {
10731075
fn alloc_ast_paths(&'a self, paths: &[ast::Path]) -> &'a [ast::Path] {
10741076
self.ast_paths.alloc_from_iter(paths.iter().cloned())
10751077
}
1078+
fn alloc_pattern_spans(&'a self, spans: impl Iterator<Item = Span>) -> &'a [Span] {
1079+
self.pattern_spans.alloc_from_iter(spans)
1080+
}
10761081
}
10771082

10781083
impl<'a> AsMut<Resolver<'a>> for Resolver<'a> {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
pub struct Bar(pub u8, u8, u8);
2+
3+
pub fn make_bar() -> Bar {
4+
Bar(1, 12, 10)
5+
}

src/test/ui/issues/issue-75907.rs

+18
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
// Test for for diagnostic improvement issue #75907
2+
3+
mod foo {
4+
pub(crate) struct Foo(u8);
5+
pub(crate) struct Bar(pub u8, u8, Foo);
6+
7+
pub(crate) fn make_bar() -> Bar {
8+
Bar(1, 12, Foo(10))
9+
}
10+
}
11+
12+
use foo::{make_bar, Bar, Foo};
13+
14+
fn main() {
15+
let Bar(x, y, Foo(z)) = make_bar();
16+
//~^ ERROR expected tuple struct
17+
//~| ERROR expected tuple struct
18+
}

src/test/ui/issues/issue-75907.stderr

+29
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
error[E0532]: expected tuple struct or tuple variant, found struct `Bar`
2+
--> $DIR/issue-75907.rs:15:9
3+
|
4+
LL | let Bar(x, y, Foo(z)) = make_bar();
5+
| ^^^
6+
|
7+
note: constructor is not visible here due to private fields
8+
--> $DIR/issue-75907.rs:15:16
9+
|
10+
LL | let Bar(x, y, Foo(z)) = make_bar();
11+
| ^ ^^^^^^ private field
12+
| |
13+
| private field
14+
15+
error[E0532]: expected tuple struct or tuple variant, found struct `Foo`
16+
--> $DIR/issue-75907.rs:15:19
17+
|
18+
LL | let Bar(x, y, Foo(z)) = make_bar();
19+
| ^^^
20+
|
21+
note: constructor is not visible here due to private fields
22+
--> $DIR/issue-75907.rs:15:23
23+
|
24+
LL | let Bar(x, y, Foo(z)) = make_bar();
25+
| ^ private field
26+
27+
error: aborting due to 2 previous errors
28+
29+
For more information about this error, try `rustc --explain E0532`.

src/test/ui/issues/issue-75907_b.rs

+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
// Test for for diagnostic improvement issue #75907, extern crate
2+
// aux-build:issue-75907.rs
3+
4+
extern crate issue_75907 as a;
5+
6+
use a::{make_bar, Bar};
7+
8+
fn main() {
9+
let Bar(x, y, z) = make_bar();
10+
//~^ ERROR expected tuple struct
11+
}
+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
error[E0532]: expected tuple struct or tuple variant, found struct `Bar`
2+
--> $DIR/issue-75907_b.rs:9:9
3+
|
4+
LL | let Bar(x, y, z) = make_bar();
5+
| ^^^ constructor is not visible here due to private fields
6+
7+
error: aborting due to previous error
8+
9+
For more information about this error, try `rustc --explain E0532`.

0 commit comments

Comments
 (0)