Skip to content

Commit e7760fa

Browse files
authored
Rollup merge of #133477 - estebank:issue-133343, r=davidtwco
Detect tuple structs that are unconstructable due to re-export When a tuple-struct is re-exported that has inaccessible fields at the `use` scope, the type's constructor cannot be accessed through that re-export. We now account for this case and extend the resulting resolution error. We also check if the constructor would be accessible directly, not through the re-export, and if so, we suggest using the full path instead. ``` error[E0423]: cannot initialize a tuple struct which contains private fields --> $DIR/ctor-not-accessible-due-to-inaccessible-field-in-reexport.rs:12:33 | LL | let crate::Foo(x) = crate::Foo(42); | ^^^^^^^^^^ | note: the type is accessed through this re-export, but the type's constructor is not visible in this import's scope due to private fields --> $DIR/ctor-not-accessible-due-to-inaccessible-field-in-reexport.rs:3:9 | LL | pub use my_mod::Foo; | ^^^^^^^^^^^ help: the type can be constructed directly, because its fields are available from the current scope | LL | let crate::Foo(x) = crate::my_mod::Foo(42); | ~~~~~~~~~~~~~~~~~~ ``` Fix #133343.
2 parents f957826 + 8f7d61b commit e7760fa

6 files changed

+181
-33
lines changed

compiler/rustc_resolve/src/ident.rs

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -901,6 +901,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
901901
binding,
902902
if resolution.non_glob_binding.is_some() { resolution.glob_binding } else { None },
903903
parent_scope,
904+
module,
904905
finalize,
905906
shadowing,
906907
);
@@ -1025,6 +1026,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
10251026
binding: Option<NameBinding<'ra>>,
10261027
shadowed_glob: Option<NameBinding<'ra>>,
10271028
parent_scope: &ParentScope<'ra>,
1029+
module: Module<'ra>,
10281030
finalize: Finalize,
10291031
shadowing: Shadowing,
10301032
) -> Result<NameBinding<'ra>, (Determinacy, Weak)> {
@@ -1076,6 +1078,37 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
10761078
self.macro_expanded_macro_export_errors.insert((path_span, binding.span));
10771079
}
10781080

1081+
// If we encounter a re-export for a type with private fields, it will not be able to
1082+
// be constructed through this re-export. We track that case here to expand later
1083+
// privacy errors with appropriate information.
1084+
if let Res::Def(_, def_id) = binding.res() {
1085+
let struct_ctor = match def_id.as_local() {
1086+
Some(def_id) => self.struct_constructors.get(&def_id).cloned(),
1087+
None => {
1088+
let ctor = self.cstore().ctor_untracked(def_id);
1089+
ctor.map(|(ctor_kind, ctor_def_id)| {
1090+
let ctor_res = Res::Def(
1091+
DefKind::Ctor(rustc_hir::def::CtorOf::Struct, ctor_kind),
1092+
ctor_def_id,
1093+
);
1094+
let ctor_vis = self.tcx.visibility(ctor_def_id);
1095+
let field_visibilities = self
1096+
.tcx
1097+
.associated_item_def_ids(def_id)
1098+
.iter()
1099+
.map(|field_id| self.tcx.visibility(field_id))
1100+
.collect();
1101+
(ctor_res, ctor_vis, field_visibilities)
1102+
})
1103+
}
1104+
};
1105+
if let Some((_, _, fields)) = struct_ctor
1106+
&& fields.iter().any(|vis| !self.is_accessible_from(*vis, module))
1107+
{
1108+
self.inaccessible_ctor_reexport.insert(path_span, binding.span);
1109+
}
1110+
}
1111+
10791112
self.record_use(ident, binding, used);
10801113
return Ok(binding);
10811114
}

compiler/rustc_resolve/src/late/diagnostics.rs

Lines changed: 66 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1942,44 +1942,77 @@ impl<'ast, 'ra, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> {
19421942
return true;
19431943
};
19441944

1945+
let update_message =
1946+
|this: &mut Self, err: &mut Diag<'_>, source: &PathSource<'_, '_, '_>| {
1947+
match source {
1948+
// e.g. `if let Enum::TupleVariant(field1, field2) = _`
1949+
PathSource::TupleStruct(_, pattern_spans) => {
1950+
err.primary_message(
1951+
"cannot match against a tuple struct which contains private fields",
1952+
);
1953+
1954+
// Use spans of the tuple struct pattern.
1955+
Some(Vec::from(*pattern_spans))
1956+
}
1957+
// e.g. `let _ = Enum::TupleVariant(field1, field2);`
1958+
PathSource::Expr(Some(Expr {
1959+
kind: ExprKind::Call(path, args),
1960+
span: call_span,
1961+
..
1962+
})) => {
1963+
err.primary_message(
1964+
"cannot initialize a tuple struct which contains private fields",
1965+
);
1966+
this.suggest_alternative_construction_methods(
1967+
def_id,
1968+
err,
1969+
path.span,
1970+
*call_span,
1971+
&args[..],
1972+
);
1973+
// Use spans of the tuple struct definition.
1974+
this.r
1975+
.field_idents(def_id)
1976+
.map(|fields| fields.iter().map(|f| f.span).collect::<Vec<_>>())
1977+
}
1978+
_ => None,
1979+
}
1980+
};
19451981
let is_accessible = self.r.is_accessible_from(ctor_vis, self.parent_scope.module);
1982+
if let Some(use_span) = self.r.inaccessible_ctor_reexport.get(&span)
1983+
&& is_accessible
1984+
{
1985+
err.span_note(
1986+
*use_span,
1987+
"the type is accessed through this re-export, but the type's constructor \
1988+
is not visible in this import's scope due to private fields",
1989+
);
1990+
if is_accessible
1991+
&& fields
1992+
.iter()
1993+
.all(|vis| self.r.is_accessible_from(*vis, self.parent_scope.module))
1994+
{
1995+
err.span_suggestion_verbose(
1996+
span,
1997+
"the type can be constructed directly, because its fields are \
1998+
available from the current scope",
1999+
// Using `tcx.def_path_str` causes the compiler to hang.
2000+
// We don't need to handle foreign crate types because in that case you
2001+
// can't access the ctor either way.
2002+
format!(
2003+
"crate{}", // The method already has leading `::`.
2004+
self.r.tcx.def_path(def_id).to_string_no_crate_verbose(),
2005+
),
2006+
Applicability::MachineApplicable,
2007+
);
2008+
}
2009+
update_message(self, err, &source);
2010+
}
19462011
if !is_expected(ctor_def) || is_accessible {
19472012
return true;
19482013
}
19492014

1950-
let field_spans = match source {
1951-
// e.g. `if let Enum::TupleVariant(field1, field2) = _`
1952-
PathSource::TupleStruct(_, pattern_spans) => {
1953-
err.primary_message(
1954-
"cannot match against a tuple struct which contains private fields",
1955-
);
1956-
1957-
// Use spans of the tuple struct pattern.
1958-
Some(Vec::from(pattern_spans))
1959-
}
1960-
// e.g. `let _ = Enum::TupleVariant(field1, field2);`
1961-
PathSource::Expr(Some(Expr {
1962-
kind: ExprKind::Call(path, args),
1963-
span: call_span,
1964-
..
1965-
})) => {
1966-
err.primary_message(
1967-
"cannot initialize a tuple struct which contains private fields",
1968-
);
1969-
self.suggest_alternative_construction_methods(
1970-
def_id,
1971-
err,
1972-
path.span,
1973-
*call_span,
1974-
&args[..],
1975-
);
1976-
// Use spans of the tuple struct definition.
1977-
self.r
1978-
.field_idents(def_id)
1979-
.map(|fields| fields.iter().map(|f| f.span).collect::<Vec<_>>())
1980-
}
1981-
_ => None,
1982-
};
2015+
let field_spans = update_message(self, err, &source);
19832016

19842017
if let Some(spans) =
19852018
field_spans.filter(|spans| spans.len() > 0 && fields.len() == spans.len())

compiler/rustc_resolve/src/lib.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1167,6 +1167,11 @@ pub struct Resolver<'ra, 'tcx> {
11671167
/// Crate-local macro expanded `macro_export` referred to by a module-relative path.
11681168
macro_expanded_macro_export_errors: BTreeSet<(Span, Span)> = BTreeSet::new(),
11691169

1170+
/// When a type is re-exported that has an inaccessible constructor because it has fields that
1171+
/// are inaccessible from the import's scope, we mark that as the type won't be able to be built
1172+
/// through the re-export. We use this information to extend the existing diagnostic.
1173+
inaccessible_ctor_reexport: FxHashMap<Span, Span>,
1174+
11701175
arenas: &'ra ResolverArenas<'ra>,
11711176
dummy_binding: NameBinding<'ra>,
11721177
builtin_types_bindings: FxHashMap<Symbol, NameBinding<'ra>>,
@@ -1595,6 +1600,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
15951600
glob_map: Default::default(),
15961601
used_imports: FxHashSet::default(),
15971602
maybe_unused_trait_imports: Default::default(),
1603+
inaccessible_ctor_reexport: Default::default(),
15981604

15991605
arenas,
16001606
dummy_binding: arenas.new_pub_res_binding(Res::Err, DUMMY_SP, LocalExpnId::ROOT),
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
#![allow(dead_code, unused_variables)]
2+
//@ run-rustfix
3+
pub use my_mod::Foo;
4+
//~^ NOTE the type is accessed through this re-export, but the type's constructor is not visible in this import's scope due to private fields
5+
//~| NOTE the type is accessed through this re-export, but the type's constructor is not visible in this import's scope due to private fields
6+
7+
mod my_mod {
8+
pub struct Foo(u32);
9+
10+
mod my_sub_mod {
11+
fn my_func() {
12+
let crate::my_mod::Foo(x) = crate::my_mod::Foo(42);
13+
//~^ ERROR cannot initialize a tuple struct which contains private fields
14+
//~| HELP the type can be constructed directly, because its fields are available from the current scope
15+
//~| ERROR cannot match against a tuple struct which contains private fields
16+
//~| HELP the type can be constructed directly, because its fields are available from the current scope
17+
}
18+
}
19+
}
20+
fn main() {}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
#![allow(dead_code, unused_variables)]
2+
//@ run-rustfix
3+
pub use my_mod::Foo;
4+
//~^ NOTE the type is accessed through this re-export, but the type's constructor is not visible in this import's scope due to private fields
5+
//~| NOTE the type is accessed through this re-export, but the type's constructor is not visible in this import's scope due to private fields
6+
7+
mod my_mod {
8+
pub struct Foo(u32);
9+
10+
mod my_sub_mod {
11+
fn my_func() {
12+
let crate::Foo(x) = crate::Foo(42);
13+
//~^ ERROR cannot initialize a tuple struct which contains private fields
14+
//~| HELP the type can be constructed directly, because its fields are available from the current scope
15+
//~| ERROR cannot match against a tuple struct which contains private fields
16+
//~| HELP the type can be constructed directly, because its fields are available from the current scope
17+
}
18+
}
19+
}
20+
fn main() {}
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
error[E0423]: cannot initialize a tuple struct which contains private fields
2+
--> $DIR/ctor-not-accessible-due-to-inaccessible-field-in-reexport.rs:12:33
3+
|
4+
LL | let crate::Foo(x) = crate::Foo(42);
5+
| ^^^^^^^^^^
6+
|
7+
note: the type is accessed through this re-export, but the type's constructor is not visible in this import's scope due to private fields
8+
--> $DIR/ctor-not-accessible-due-to-inaccessible-field-in-reexport.rs:3:9
9+
|
10+
LL | pub use my_mod::Foo;
11+
| ^^^^^^^^^^^
12+
help: the type can be constructed directly, because its fields are available from the current scope
13+
|
14+
LL | let crate::Foo(x) = crate::my_mod::Foo(42);
15+
| ++++++++
16+
17+
error[E0532]: cannot match against a tuple struct which contains private fields
18+
--> $DIR/ctor-not-accessible-due-to-inaccessible-field-in-reexport.rs:12:17
19+
|
20+
LL | let crate::Foo(x) = crate::Foo(42);
21+
| ^^^^^^^^^^
22+
|
23+
note: the type is accessed through this re-export, but the type's constructor is not visible in this import's scope due to private fields
24+
--> $DIR/ctor-not-accessible-due-to-inaccessible-field-in-reexport.rs:3:9
25+
|
26+
LL | pub use my_mod::Foo;
27+
| ^^^^^^^^^^^
28+
help: the type can be constructed directly, because its fields are available from the current scope
29+
|
30+
LL | let crate::my_mod::Foo(x) = crate::Foo(42);
31+
| ++++++++
32+
33+
error: aborting due to 2 previous errors
34+
35+
Some errors have detailed explanations: E0423, E0532.
36+
For more information about an error, try `rustc --explain E0423`.

0 commit comments

Comments
 (0)