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 #133343.
This commit is contained in:
parent
8d72d3e1e9
commit
8f7d61b9ef
6 changed files with 181 additions and 33 deletions
|
|
@ -901,6 +901,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
|
|||
binding,
|
||||
if resolution.non_glob_binding.is_some() { resolution.glob_binding } else { None },
|
||||
parent_scope,
|
||||
module,
|
||||
finalize,
|
||||
shadowing,
|
||||
);
|
||||
|
|
@ -1025,6 +1026,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
|
|||
binding: Option<NameBinding<'ra>>,
|
||||
shadowed_glob: Option<NameBinding<'ra>>,
|
||||
parent_scope: &ParentScope<'ra>,
|
||||
module: Module<'ra>,
|
||||
finalize: Finalize,
|
||||
shadowing: Shadowing,
|
||||
) -> Result<NameBinding<'ra>, (Determinacy, Weak)> {
|
||||
|
|
@ -1076,6 +1078,37 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
|
|||
self.macro_expanded_macro_export_errors.insert((path_span, binding.span));
|
||||
}
|
||||
|
||||
// If we encounter a re-export for a type with private fields, it will not be able to
|
||||
// be constructed through this re-export. We track that case here to expand later
|
||||
// privacy errors with appropriate information.
|
||||
if let Res::Def(_, def_id) = binding.res() {
|
||||
let struct_ctor = match def_id.as_local() {
|
||||
Some(def_id) => self.struct_constructors.get(&def_id).cloned(),
|
||||
None => {
|
||||
let ctor = self.cstore().ctor_untracked(def_id);
|
||||
ctor.map(|(ctor_kind, ctor_def_id)| {
|
||||
let ctor_res = Res::Def(
|
||||
DefKind::Ctor(rustc_hir::def::CtorOf::Struct, ctor_kind),
|
||||
ctor_def_id,
|
||||
);
|
||||
let ctor_vis = self.tcx.visibility(ctor_def_id);
|
||||
let field_visibilities = self
|
||||
.tcx
|
||||
.associated_item_def_ids(def_id)
|
||||
.iter()
|
||||
.map(|field_id| self.tcx.visibility(field_id))
|
||||
.collect();
|
||||
(ctor_res, ctor_vis, field_visibilities)
|
||||
})
|
||||
}
|
||||
};
|
||||
if let Some((_, _, fields)) = struct_ctor
|
||||
&& fields.iter().any(|vis| !self.is_accessible_from(*vis, module))
|
||||
{
|
||||
self.inaccessible_ctor_reexport.insert(path_span, binding.span);
|
||||
}
|
||||
}
|
||||
|
||||
self.record_use(ident, binding, used);
|
||||
return Ok(binding);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1942,44 +1942,77 @@ impl<'ast, 'ra, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> {
|
|||
return true;
|
||||
};
|
||||
|
||||
let update_message =
|
||||
|this: &mut Self, err: &mut Diag<'_>, source: &PathSource<'_, '_, '_>| {
|
||||
match source {
|
||||
// e.g. `if let Enum::TupleVariant(field1, field2) = _`
|
||||
PathSource::TupleStruct(_, pattern_spans) => {
|
||||
err.primary_message(
|
||||
"cannot match against a tuple struct which contains private fields",
|
||||
);
|
||||
|
||||
// Use spans of the tuple struct pattern.
|
||||
Some(Vec::from(*pattern_spans))
|
||||
}
|
||||
// e.g. `let _ = Enum::TupleVariant(field1, field2);`
|
||||
PathSource::Expr(Some(Expr {
|
||||
kind: ExprKind::Call(path, args),
|
||||
span: call_span,
|
||||
..
|
||||
})) => {
|
||||
err.primary_message(
|
||||
"cannot initialize a tuple struct which contains private fields",
|
||||
);
|
||||
this.suggest_alternative_construction_methods(
|
||||
def_id,
|
||||
err,
|
||||
path.span,
|
||||
*call_span,
|
||||
&args[..],
|
||||
);
|
||||
// Use spans of the tuple struct definition.
|
||||
this.r
|
||||
.field_idents(def_id)
|
||||
.map(|fields| fields.iter().map(|f| f.span).collect::<Vec<_>>())
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
};
|
||||
let is_accessible = self.r.is_accessible_from(ctor_vis, self.parent_scope.module);
|
||||
if let Some(use_span) = self.r.inaccessible_ctor_reexport.get(&span)
|
||||
&& is_accessible
|
||||
{
|
||||
err.span_note(
|
||||
*use_span,
|
||||
"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",
|
||||
);
|
||||
if is_accessible
|
||||
&& fields
|
||||
.iter()
|
||||
.all(|vis| self.r.is_accessible_from(*vis, self.parent_scope.module))
|
||||
{
|
||||
err.span_suggestion_verbose(
|
||||
span,
|
||||
"the type can be constructed directly, because its fields are \
|
||||
available from the current scope",
|
||||
// Using `tcx.def_path_str` causes the compiler to hang.
|
||||
// We don't need to handle foreign crate types because in that case you
|
||||
// can't access the ctor either way.
|
||||
format!(
|
||||
"crate{}", // The method already has leading `::`.
|
||||
self.r.tcx.def_path(def_id).to_string_no_crate_verbose(),
|
||||
),
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
}
|
||||
update_message(self, err, &source);
|
||||
}
|
||||
if !is_expected(ctor_def) || is_accessible {
|
||||
return true;
|
||||
}
|
||||
|
||||
let field_spans = match source {
|
||||
// e.g. `if let Enum::TupleVariant(field1, field2) = _`
|
||||
PathSource::TupleStruct(_, pattern_spans) => {
|
||||
err.primary_message(
|
||||
"cannot match against a tuple struct which contains private fields",
|
||||
);
|
||||
|
||||
// Use spans of the tuple struct pattern.
|
||||
Some(Vec::from(pattern_spans))
|
||||
}
|
||||
// e.g. `let _ = Enum::TupleVariant(field1, field2);`
|
||||
PathSource::Expr(Some(Expr {
|
||||
kind: ExprKind::Call(path, args),
|
||||
span: call_span,
|
||||
..
|
||||
})) => {
|
||||
err.primary_message(
|
||||
"cannot initialize a tuple struct which contains private fields",
|
||||
);
|
||||
self.suggest_alternative_construction_methods(
|
||||
def_id,
|
||||
err,
|
||||
path.span,
|
||||
*call_span,
|
||||
&args[..],
|
||||
);
|
||||
// Use spans of the tuple struct definition.
|
||||
self.r
|
||||
.field_idents(def_id)
|
||||
.map(|fields| fields.iter().map(|f| f.span).collect::<Vec<_>>())
|
||||
}
|
||||
_ => None,
|
||||
};
|
||||
let field_spans = update_message(self, err, &source);
|
||||
|
||||
if let Some(spans) =
|
||||
field_spans.filter(|spans| spans.len() > 0 && fields.len() == spans.len())
|
||||
|
|
|
|||
|
|
@ -1167,6 +1167,11 @@ pub struct Resolver<'ra, 'tcx> {
|
|||
/// Crate-local macro expanded `macro_export` referred to by a module-relative path.
|
||||
macro_expanded_macro_export_errors: BTreeSet<(Span, Span)> = BTreeSet::new(),
|
||||
|
||||
/// When a type is re-exported that has an inaccessible constructor because it has fields that
|
||||
/// are inaccessible from the import's scope, we mark that as the type won't be able to be built
|
||||
/// through the re-export. We use this information to extend the existing diagnostic.
|
||||
inaccessible_ctor_reexport: FxHashMap<Span, Span>,
|
||||
|
||||
arenas: &'ra ResolverArenas<'ra>,
|
||||
dummy_binding: NameBinding<'ra>,
|
||||
builtin_types_bindings: FxHashMap<Symbol, NameBinding<'ra>>,
|
||||
|
|
@ -1595,6 +1600,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
|
|||
glob_map: Default::default(),
|
||||
used_imports: FxHashSet::default(),
|
||||
maybe_unused_trait_imports: Default::default(),
|
||||
inaccessible_ctor_reexport: Default::default(),
|
||||
|
||||
arenas,
|
||||
dummy_binding: arenas.new_pub_res_binding(Res::Err, DUMMY_SP, LocalExpnId::ROOT),
|
||||
|
|
|
|||
|
|
@ -0,0 +1,20 @@
|
|||
#![allow(dead_code, unused_variables)]
|
||||
//@ run-rustfix
|
||||
pub use my_mod::Foo;
|
||||
//~^ 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
|
||||
//~| 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
|
||||
|
||||
mod my_mod {
|
||||
pub struct Foo(u32);
|
||||
|
||||
mod my_sub_mod {
|
||||
fn my_func() {
|
||||
let crate::my_mod::Foo(x) = crate::my_mod::Foo(42);
|
||||
//~^ ERROR cannot initialize a tuple struct which contains private fields
|
||||
//~| HELP the type can be constructed directly, because its fields are available from the current scope
|
||||
//~| ERROR cannot match against a tuple struct which contains private fields
|
||||
//~| HELP the type can be constructed directly, because its fields are available from the current scope
|
||||
}
|
||||
}
|
||||
}
|
||||
fn main() {}
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
#![allow(dead_code, unused_variables)]
|
||||
//@ run-rustfix
|
||||
pub use my_mod::Foo;
|
||||
//~^ 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
|
||||
//~| 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
|
||||
|
||||
mod my_mod {
|
||||
pub struct Foo(u32);
|
||||
|
||||
mod my_sub_mod {
|
||||
fn my_func() {
|
||||
let crate::Foo(x) = crate::Foo(42);
|
||||
//~^ ERROR cannot initialize a tuple struct which contains private fields
|
||||
//~| HELP the type can be constructed directly, because its fields are available from the current scope
|
||||
//~| ERROR cannot match against a tuple struct which contains private fields
|
||||
//~| HELP the type can be constructed directly, because its fields are available from the current scope
|
||||
}
|
||||
}
|
||||
}
|
||||
fn main() {}
|
||||
|
|
@ -0,0 +1,36 @@
|
|||
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);
|
||||
| ++++++++
|
||||
|
||||
error[E0532]: cannot match against a tuple struct which contains private fields
|
||||
--> $DIR/ctor-not-accessible-due-to-inaccessible-field-in-reexport.rs:12:17
|
||||
|
|
||||
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::my_mod::Foo(x) = crate::Foo(42);
|
||||
| ++++++++
|
||||
|
||||
error: aborting due to 2 previous errors
|
||||
|
||||
Some errors have detailed explanations: E0423, E0532.
|
||||
For more information about an error, try `rustc --explain E0423`.
|
||||
Loading…
Add table
Add a link
Reference in a new issue