Skip to content

Commit c63f634

Browse files
committed
Give better diagnostic when using a private tuple struct constructor
1 parent 130359c commit c63f634

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
@@ -89,7 +89,7 @@ fn import_candidate_to_enum_paths(suggestion: &ImportSuggestion) -> (String, Str
8989
(variant_path_string, enum_path_string)
9090
}
9191

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

compiler/rustc_resolve/src/lib.rs

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

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

10041005
/// Features enabled for this crate.
10051006
active_features: FxHashSet<Symbol>,
@@ -1036,6 +1037,7 @@ pub struct ResolverArenas<'a> {
10361037
name_resolutions: TypedArena<RefCell<NameResolution<'a>>>,
10371038
macro_rules_bindings: TypedArena<MacroRulesBinding<'a>>,
10381039
ast_paths: TypedArena<ast::Path>,
1040+
pattern_spans: TypedArena<Span>,
10391041
}
10401042

10411043
impl<'a> ResolverArenas<'a> {
@@ -1067,6 +1069,9 @@ impl<'a> ResolverArenas<'a> {
10671069
fn alloc_ast_paths(&'a self, paths: &[ast::Path]) -> &'a [ast::Path] {
10681070
self.ast_paths.alloc_from_iter(paths.iter().cloned())
10691071
}
1072+
fn alloc_pattern_spans(&'a self, spans: impl Iterator<Item = Span>) -> &'a [Span] {
1073+
self.pattern_spans.alloc_from_iter(spans)
1074+
}
10701075
}
10711076

10721077
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)