Skip to content

Commit 4063732

Browse files
committed
Detect unconstructable re-exported tuple structs
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 rust-lang#133343.
1 parent bfe809d commit 4063732

6 files changed

+181
-33
lines changed

compiler/rustc_resolve/src/ident.rs

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -931,6 +931,38 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
931931
}
932932

933933
self.record_use(ident, binding, used);
934+
935+
// If we encounter a re-export for a type with private fields, it will not be able to
936+
// be constructed through this re-export. We track that case here to expand later
937+
// privacy errors with appropriate information.
938+
if let Res::Def(_, def_id) = binding.res() {
939+
let struct_ctor = match def_id.as_local() {
940+
Some(def_id) => self.struct_constructors.get(&def_id).cloned(),
941+
None => {
942+
let ctor = self.cstore().ctor_untracked(def_id);
943+
ctor.map(|(ctor_kind, ctor_def_id)| {
944+
let ctor_res = Res::Def(
945+
DefKind::Ctor(rustc_hir::def::CtorOf::Struct, ctor_kind),
946+
ctor_def_id,
947+
);
948+
let ctor_vis = self.tcx.visibility(ctor_def_id);
949+
let field_visibilities = self
950+
.tcx
951+
.associated_item_def_ids(def_id)
952+
.iter()
953+
.map(|field_id| self.tcx.visibility(field_id))
954+
.collect();
955+
(ctor_res, ctor_vis, field_visibilities)
956+
})
957+
}
958+
};
959+
if let Some((_, _, fields)) = struct_ctor
960+
&& fields.iter().any(|vis| !self.is_accessible_from(*vis, module))
961+
{
962+
self.inaccessible_ctor_reexport.insert(path_span, binding.span);
963+
}
964+
}
965+
934966
return Ok(binding);
935967
}
936968

compiler/rustc_resolve/src/late/diagnostics.rs

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

1741+
let update_message =
1742+
|this: &mut Self, err: &mut Diag<'_>, source: &PathSource<'_>| {
1743+
match source {
1744+
// e.g. `if let Enum::TupleVariant(field1, field2) = _`
1745+
PathSource::TupleStruct(_, pattern_spans) => {
1746+
err.primary_message(
1747+
"cannot match against a tuple struct which contains private fields",
1748+
);
1749+
1750+
// Use spans of the tuple struct pattern.
1751+
Some(Vec::from(*pattern_spans))
1752+
}
1753+
// e.g. `let _ = Enum::TupleVariant(field1, field2);`
1754+
PathSource::Expr(Some(Expr {
1755+
kind: ExprKind::Call(path, ref args),
1756+
span: call_span,
1757+
..
1758+
})) => {
1759+
err.primary_message(
1760+
"cannot initialize a tuple struct which contains private fields",
1761+
);
1762+
this.suggest_alternative_construction_methods(
1763+
def_id,
1764+
err,
1765+
path.span,
1766+
*call_span,
1767+
&args[..],
1768+
);
1769+
// Use spans of the tuple struct definition.
1770+
this.r
1771+
.field_idents(def_id)
1772+
.map(|fields| fields.iter().map(|f| f.span).collect::<Vec<_>>())
1773+
}
1774+
_ => None,
1775+
}
1776+
};
17411777
let is_accessible = self.r.is_accessible_from(ctor_vis, self.parent_scope.module);
1778+
if let Some(use_span) = self.r.inaccessible_ctor_reexport.get(&span)
1779+
&& is_accessible
1780+
{
1781+
err.span_note(
1782+
*use_span,
1783+
"the type is accessed through this re-export, but the type's constructor \
1784+
is not visible in this import's scope due to private fields",
1785+
);
1786+
if is_accessible
1787+
&& fields
1788+
.iter()
1789+
.all(|vis| self.r.is_accessible_from(*vis, self.parent_scope.module))
1790+
{
1791+
err.span_suggestion_verbose(
1792+
span,
1793+
"the type can be constructed directly, because its fields are \
1794+
available from the current scope",
1795+
// Using `tcx.def_path_str` causes the compiler to hang.
1796+
// We don't need to handle foreign crate types because in that case you
1797+
// can't access the ctor either way.
1798+
format!(
1799+
"crate{}", // The method already has leading `::`.
1800+
self.r.tcx.def_path(def_id).to_string_no_crate_verbose(),
1801+
),
1802+
Applicability::MachineApplicable,
1803+
);
1804+
}
1805+
update_message(self, err, &source);
1806+
}
17421807
if !is_expected(ctor_def) || is_accessible {
17431808
return true;
17441809
}
17451810

1746-
let field_spans = match source {
1747-
// e.g. `if let Enum::TupleVariant(field1, field2) = _`
1748-
PathSource::TupleStruct(_, pattern_spans) => {
1749-
err.primary_message(
1750-
"cannot match against a tuple struct which contains private fields",
1751-
);
1752-
1753-
// Use spans of the tuple struct pattern.
1754-
Some(Vec::from(pattern_spans))
1755-
}
1756-
// e.g. `let _ = Enum::TupleVariant(field1, field2);`
1757-
PathSource::Expr(Some(Expr {
1758-
kind: ExprKind::Call(path, ref args),
1759-
span: call_span,
1760-
..
1761-
})) => {
1762-
err.primary_message(
1763-
"cannot initialize a tuple struct which contains private fields",
1764-
);
1765-
self.suggest_alternative_construction_methods(
1766-
def_id,
1767-
err,
1768-
path.span,
1769-
*call_span,
1770-
&args[..],
1771-
);
1772-
// Use spans of the tuple struct definition.
1773-
self.r
1774-
.field_idents(def_id)
1775-
.map(|fields| fields.iter().map(|f| f.span).collect::<Vec<_>>())
1776-
}
1777-
_ => None,
1778-
};
1811+
let field_spans = update_message(self, err, &source);
17791812

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

compiler/rustc_resolve/src/lib.rs

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

1102+
/// When a type is re-exported that has an inaccessible constructor because it has fields that
1103+
/// are inaccessible from the import's scope, we mark that as the type won't be able to be built
1104+
/// through the re-export. We use this information to extend the existing diagnostic.
1105+
inaccessible_ctor_reexport: FxHashMap<Span, Span>,
1106+
11021107
arenas: &'ra ResolverArenas<'ra>,
11031108
dummy_binding: NameBinding<'ra>,
11041109
builtin_types_bindings: FxHashMap<Symbol, NameBinding<'ra>>,
@@ -1477,6 +1482,8 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
14771482
use_injections: Vec::new(),
14781483
macro_expanded_macro_export_errors: BTreeSet::new(),
14791484

1485+
inaccessible_ctor_reexport: Default::default(),
1486+
14801487
arenas,
14811488
dummy_binding: (Res::Err, pub_vis, DUMMY_SP, LocalExpnId::ROOT).to_name_binding(arenas),
14821489
builtin_types_bindings: PrimTy::ALL
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)