Auto merge of #135846 - estebank:non-exhaustive-dfv-ctor-2, r=BoxyUwU
Detect struct construction with private field in field with default
When trying to construct a struct that has a public field of a private type, suggest using `..` if that field has a default value.
```
error[E0603]: struct `Priv1` is private
--> $DIR/non-exhaustive-ctor-2.rs:19:39
|
LL | let _ = S { field: (), field1: m::Priv1 {} };
| ------ ^^^^^ private struct
| |
| while setting this field
|
note: the struct `Priv1` is defined here
--> $DIR/non-exhaustive-ctor-2.rs:14:4
|
LL | struct Priv1 {}
| ^^^^^^^^^^^^
help: the type `Priv1` of field `field1` is private, but you can construct the default value defined for it in `S` using `..` in the struct initializer expression
|
LL | let _ = S { field: (), .. };
| ~~
```
This commit is contained in:
commit
21a19c297d
11 changed files with 321 additions and 36 deletions
|
|
@ -395,6 +395,7 @@ provide! { tcx, def_id, other, cdata,
|
|||
|
||||
crate_extern_paths => { cdata.source().paths().cloned().collect() }
|
||||
expn_that_defined => { cdata.get_expn_that_defined(def_id.index, tcx.sess) }
|
||||
default_field => { cdata.get_default_field(def_id.index) }
|
||||
is_doc_hidden => { cdata.get_attr_flags(def_id.index).contains(AttrFlags::IS_DOC_HIDDEN) }
|
||||
doc_link_resolutions => { tcx.arena.alloc(cdata.get_doc_link_resolutions(def_id.index)) }
|
||||
doc_link_traits_in_scope => {
|
||||
|
|
|
|||
|
|
@ -1864,6 +1864,12 @@ rustc_queries! {
|
|||
feedable
|
||||
}
|
||||
|
||||
/// Returns whether the field corresponding to the `DefId` has a default field value.
|
||||
query default_field(def_id: DefId) -> Option<DefId> {
|
||||
desc { |tcx| "looking up the `const` corresponding to the default for `{}`", tcx.def_path_str(def_id) }
|
||||
separate_provide_extern
|
||||
}
|
||||
|
||||
query check_well_formed(key: LocalDefId) -> Result<(), ErrorGuaranteed> {
|
||||
desc { |tcx| "checking that `{}` is well-formed", tcx.def_path_str(key) }
|
||||
return_result_from_ensure_ok
|
||||
|
|
|
|||
|
|
@ -420,14 +420,18 @@ impl<'a, 'ra, 'tcx> BuildReducedGraphVisitor<'a, 'ra, 'tcx> {
|
|||
// The fields are not expanded yet.
|
||||
return;
|
||||
}
|
||||
let fields = fields
|
||||
let field_name = |i, field: &ast::FieldDef| {
|
||||
field.ident.unwrap_or_else(|| Ident::from_str_and_span(&format!("{i}"), field.span))
|
||||
};
|
||||
let field_names: Vec<_> =
|
||||
fields.iter().enumerate().map(|(i, field)| field_name(i, field)).collect();
|
||||
let defaults = fields
|
||||
.iter()
|
||||
.enumerate()
|
||||
.map(|(i, field)| {
|
||||
field.ident.unwrap_or_else(|| Ident::from_str_and_span(&format!("{i}"), field.span))
|
||||
})
|
||||
.filter_map(|(i, field)| field.default.as_ref().map(|_| field_name(i, field).name))
|
||||
.collect();
|
||||
self.r.field_names.insert(def_id, fields);
|
||||
self.r.field_names.insert(def_id, field_names);
|
||||
self.r.field_defaults.insert(def_id, defaults);
|
||||
}
|
||||
|
||||
fn insert_field_visibilities_local(&mut self, def_id: DefId, fields: &[ast::FieldDef]) {
|
||||
|
|
|
|||
|
|
@ -1943,8 +1943,15 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
|
|||
}
|
||||
|
||||
fn report_privacy_error(&mut self, privacy_error: &PrivacyError<'ra>) {
|
||||
let PrivacyError { ident, binding, outermost_res, parent_scope, single_nested, dedup_span } =
|
||||
*privacy_error;
|
||||
let PrivacyError {
|
||||
ident,
|
||||
binding,
|
||||
outermost_res,
|
||||
parent_scope,
|
||||
single_nested,
|
||||
dedup_span,
|
||||
ref source,
|
||||
} = *privacy_error;
|
||||
|
||||
let res = binding.res();
|
||||
let ctor_fields_span = self.ctor_fields_span(binding);
|
||||
|
|
@ -1960,6 +1967,8 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
|
|||
let mut err =
|
||||
self.dcx().create_err(errors::IsPrivate { span: ident.span, ident_descr, ident });
|
||||
|
||||
self.mention_default_field_values(source, ident, &mut err);
|
||||
|
||||
let mut not_publicly_reexported = false;
|
||||
if let Some((this_res, outer_ident)) = outermost_res {
|
||||
let import_suggestions = self.lookup_import_candidates(
|
||||
|
|
@ -2141,6 +2150,85 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
|
|||
err.emit();
|
||||
}
|
||||
|
||||
/// When a private field is being set that has a default field value, we suggest using `..` and
|
||||
/// setting the value of that field implicitly with its default.
|
||||
///
|
||||
/// If we encounter code like
|
||||
/// ```text
|
||||
/// struct Priv;
|
||||
/// pub struct S {
|
||||
/// pub field: Priv = Priv,
|
||||
/// }
|
||||
/// ```
|
||||
/// which is used from a place where `Priv` isn't accessible
|
||||
/// ```text
|
||||
/// let _ = S { field: m::Priv1 {} };
|
||||
/// // ^^^^^ private struct
|
||||
/// ```
|
||||
/// we will suggest instead using the `default_field_values` syntax instead:
|
||||
/// ```text
|
||||
/// let _ = S { .. };
|
||||
/// ```
|
||||
fn mention_default_field_values(
|
||||
&self,
|
||||
source: &Option<ast::Expr>,
|
||||
ident: Ident,
|
||||
err: &mut Diag<'_>,
|
||||
) {
|
||||
let Some(expr) = source else { return };
|
||||
let ast::ExprKind::Struct(struct_expr) = &expr.kind else { return };
|
||||
// We don't have to handle type-relative paths because they're forbidden in ADT
|
||||
// expressions, but that would change with `#[feature(more_qualified_paths)]`.
|
||||
let Some(Res::Def(_, def_id)) =
|
||||
self.partial_res_map[&struct_expr.path.segments.iter().last().unwrap().id].full_res()
|
||||
else {
|
||||
return;
|
||||
};
|
||||
let Some(default_fields) = self.field_defaults(def_id) else { return };
|
||||
if struct_expr.fields.is_empty() {
|
||||
return;
|
||||
}
|
||||
let last_span = struct_expr.fields.iter().last().unwrap().span;
|
||||
let mut iter = struct_expr.fields.iter().peekable();
|
||||
let mut prev: Option<Span> = None;
|
||||
while let Some(field) = iter.next() {
|
||||
if field.expr.span.overlaps(ident.span) {
|
||||
err.span_label(field.ident.span, "while setting this field");
|
||||
if default_fields.contains(&field.ident.name) {
|
||||
let sugg = if last_span == field.span {
|
||||
vec![(field.span, "..".to_string())]
|
||||
} else {
|
||||
vec![
|
||||
(
|
||||
// Account for trailing commas and ensure we remove them.
|
||||
match (prev, iter.peek()) {
|
||||
(_, Some(next)) => field.span.with_hi(next.span.lo()),
|
||||
(Some(prev), _) => field.span.with_lo(prev.hi()),
|
||||
(None, None) => field.span,
|
||||
},
|
||||
String::new(),
|
||||
),
|
||||
(last_span.shrink_to_hi(), ", ..".to_string()),
|
||||
]
|
||||
};
|
||||
err.multipart_suggestion_verbose(
|
||||
format!(
|
||||
"the type `{ident}` of field `{}` is private, but you can construct \
|
||||
the default value defined for it in `{}` using `..` in the struct \
|
||||
initializer expression",
|
||||
field.ident,
|
||||
self.tcx.item_name(def_id),
|
||||
),
|
||||
sugg,
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
break;
|
||||
}
|
||||
}
|
||||
prev = Some(field.span);
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn find_similarly_named_module_or_crate(
|
||||
&self,
|
||||
ident: Symbol,
|
||||
|
|
|
|||
|
|
@ -1029,6 +1029,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
|
|||
binding,
|
||||
dedup_span: path_span,
|
||||
outermost_res: None,
|
||||
source: None,
|
||||
parent_scope: *parent_scope,
|
||||
single_nested: path_span != root_span,
|
||||
});
|
||||
|
|
@ -1435,7 +1436,16 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
|
|||
parent_scope: &ParentScope<'ra>,
|
||||
ignore_import: Option<Import<'ra>>,
|
||||
) -> PathResult<'ra> {
|
||||
self.resolve_path_with_ribs(path, opt_ns, parent_scope, None, None, None, ignore_import)
|
||||
self.resolve_path_with_ribs(
|
||||
path,
|
||||
opt_ns,
|
||||
parent_scope,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
ignore_import,
|
||||
)
|
||||
}
|
||||
#[instrument(level = "debug", skip(self))]
|
||||
pub(crate) fn resolve_path<'r>(
|
||||
|
|
@ -1451,6 +1461,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
|
|||
path,
|
||||
opt_ns,
|
||||
parent_scope,
|
||||
None,
|
||||
finalize,
|
||||
None,
|
||||
ignore_binding,
|
||||
|
|
@ -1463,6 +1474,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
|
|||
path: &[Segment],
|
||||
opt_ns: Option<Namespace>, // `None` indicates a module path in import
|
||||
parent_scope: &ParentScope<'ra>,
|
||||
source: Option<PathSource<'_, '_, '_>>,
|
||||
finalize: Option<Finalize>,
|
||||
ribs: Option<&PerNS<Vec<Rib<'ra>>>>,
|
||||
ignore_binding: Option<NameBinding<'ra>>,
|
||||
|
|
@ -1645,6 +1657,11 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
|
|||
if finalize.is_some() {
|
||||
for error in &mut self.get_mut().privacy_errors[privacy_errors_len..] {
|
||||
error.outermost_res = Some((res, ident));
|
||||
error.source = match source {
|
||||
Some(PathSource::Struct(Some(expr)))
|
||||
| Some(PathSource::Expr(Some(expr))) => Some(expr.clone()),
|
||||
_ => None,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -424,7 +424,7 @@ pub(crate) enum PathSource<'a, 'ast, 'ra> {
|
|||
/// Paths in path patterns `Path`.
|
||||
Pat,
|
||||
/// Paths in struct expressions and patterns `Path { .. }`.
|
||||
Struct,
|
||||
Struct(Option<&'a Expr>),
|
||||
/// Paths in tuple struct patterns `Path(..)`.
|
||||
TupleStruct(Span, &'ra [Span]),
|
||||
/// `m::A::B` in `<T as m::A>::B::C`.
|
||||
|
|
@ -447,7 +447,7 @@ impl PathSource<'_, '_, '_> {
|
|||
match self {
|
||||
PathSource::Type
|
||||
| PathSource::Trait(_)
|
||||
| PathSource::Struct
|
||||
| PathSource::Struct(_)
|
||||
| PathSource::DefineOpaques => TypeNS,
|
||||
PathSource::Expr(..)
|
||||
| PathSource::Pat
|
||||
|
|
@ -464,7 +464,7 @@ impl PathSource<'_, '_, '_> {
|
|||
PathSource::Type
|
||||
| PathSource::Expr(..)
|
||||
| PathSource::Pat
|
||||
| PathSource::Struct
|
||||
| PathSource::Struct(_)
|
||||
| PathSource::TupleStruct(..)
|
||||
| PathSource::ReturnTypeNotation => true,
|
||||
PathSource::Trait(_)
|
||||
|
|
@ -481,7 +481,7 @@ impl PathSource<'_, '_, '_> {
|
|||
PathSource::Type => "type",
|
||||
PathSource::Trait(_) => "trait",
|
||||
PathSource::Pat => "unit struct, unit variant or constant",
|
||||
PathSource::Struct => "struct, variant or union type",
|
||||
PathSource::Struct(_) => "struct, variant or union type",
|
||||
PathSource::TraitItem(ValueNS, PathSource::TupleStruct(..))
|
||||
| PathSource::TupleStruct(..) => "tuple struct or tuple variant",
|
||||
PathSource::TraitItem(ns, _) => match ns {
|
||||
|
|
@ -576,7 +576,7 @@ impl PathSource<'_, '_, '_> {
|
|||
|| matches!(res, Res::Def(DefKind::Const | DefKind::AssocConst, _))
|
||||
}
|
||||
PathSource::TupleStruct(..) => res.expected_in_tuple_struct_pat(),
|
||||
PathSource::Struct => matches!(
|
||||
PathSource::Struct(_) => matches!(
|
||||
res,
|
||||
Res::Def(
|
||||
DefKind::Struct
|
||||
|
|
@ -616,8 +616,8 @@ impl PathSource<'_, '_, '_> {
|
|||
(PathSource::Trait(_), false) => E0405,
|
||||
(PathSource::Type | PathSource::DefineOpaques, true) => E0573,
|
||||
(PathSource::Type | PathSource::DefineOpaques, false) => E0412,
|
||||
(PathSource::Struct, true) => E0574,
|
||||
(PathSource::Struct, false) => E0422,
|
||||
(PathSource::Struct(_), true) => E0574,
|
||||
(PathSource::Struct(_), false) => E0422,
|
||||
(PathSource::Expr(..), true) | (PathSource::Delegation, true) => E0423,
|
||||
(PathSource::Expr(..), false) | (PathSource::Delegation, false) => E0425,
|
||||
(PathSource::Pat | PathSource::TupleStruct(..), true) => E0532,
|
||||
|
|
@ -1482,11 +1482,13 @@ impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> {
|
|||
path: &[Segment],
|
||||
opt_ns: Option<Namespace>, // `None` indicates a module path in import
|
||||
finalize: Option<Finalize>,
|
||||
source: PathSource<'_, 'ast, 'ra>,
|
||||
) -> PathResult<'ra> {
|
||||
self.r.cm().resolve_path_with_ribs(
|
||||
path,
|
||||
opt_ns,
|
||||
&self.parent_scope,
|
||||
Some(source),
|
||||
finalize,
|
||||
Some(&self.ribs),
|
||||
None,
|
||||
|
|
@ -1966,7 +1968,7 @@ impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> {
|
|||
&mut self,
|
||||
partial_res: PartialRes,
|
||||
path: &[Segment],
|
||||
source: PathSource<'_, '_, '_>,
|
||||
source: PathSource<'_, 'ast, 'ra>,
|
||||
path_span: Span,
|
||||
) {
|
||||
let proj_start = path.len() - partial_res.unresolved_segments();
|
||||
|
|
@ -2019,7 +2021,7 @@ impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> {
|
|||
| PathSource::ReturnTypeNotation => false,
|
||||
PathSource::Expr(..)
|
||||
| PathSource::Pat
|
||||
| PathSource::Struct
|
||||
| PathSource::Struct(_)
|
||||
| PathSource::TupleStruct(..)
|
||||
| PathSource::DefineOpaques
|
||||
| PathSource::Delegation => true,
|
||||
|
|
@ -3866,7 +3868,7 @@ impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> {
|
|||
self.smart_resolve_path(pat.id, qself, path, PathSource::Pat);
|
||||
}
|
||||
PatKind::Struct(ref qself, ref path, ref _fields, ref rest) => {
|
||||
self.smart_resolve_path(pat.id, qself, path, PathSource::Struct);
|
||||
self.smart_resolve_path(pat.id, qself, path, PathSource::Struct(None));
|
||||
self.record_patterns_with_skipped_bindings(pat, rest);
|
||||
}
|
||||
PatKind::Or(ref ps) => {
|
||||
|
|
@ -4110,7 +4112,7 @@ impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> {
|
|||
id: NodeId,
|
||||
qself: &Option<Box<QSelf>>,
|
||||
path: &Path,
|
||||
source: PathSource<'_, 'ast, '_>,
|
||||
source: PathSource<'_, 'ast, 'ra>,
|
||||
) {
|
||||
self.smart_resolve_path_fragment(
|
||||
qself,
|
||||
|
|
@ -4127,7 +4129,7 @@ impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> {
|
|||
&mut self,
|
||||
qself: &Option<Box<QSelf>>,
|
||||
path: &[Segment],
|
||||
source: PathSource<'_, 'ast, '_>,
|
||||
source: PathSource<'_, 'ast, 'ra>,
|
||||
finalize: Finalize,
|
||||
record_partial_res: RecordPartialRes,
|
||||
parent_qself: Option<&QSelf>,
|
||||
|
|
@ -4365,7 +4367,7 @@ impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> {
|
|||
std_path.push(Segment::from_ident(Ident::with_dummy_span(sym::std)));
|
||||
std_path.extend(path);
|
||||
if let PathResult::Module(_) | PathResult::NonModule(_) =
|
||||
self.resolve_path(&std_path, Some(ns), None)
|
||||
self.resolve_path(&std_path, Some(ns), None, source)
|
||||
{
|
||||
// Check if we wrote `str::from_utf8` instead of `std::str::from_utf8`
|
||||
let item_span =
|
||||
|
|
@ -4439,7 +4441,7 @@ impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> {
|
|||
span: Span,
|
||||
defer_to_typeck: bool,
|
||||
finalize: Finalize,
|
||||
source: PathSource<'_, 'ast, '_>,
|
||||
source: PathSource<'_, 'ast, 'ra>,
|
||||
) -> Result<Option<PartialRes>, Spanned<ResolutionError<'ra>>> {
|
||||
let mut fin_res = None;
|
||||
|
||||
|
|
@ -4488,7 +4490,7 @@ impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> {
|
|||
path: &[Segment],
|
||||
ns: Namespace,
|
||||
finalize: Finalize,
|
||||
source: PathSource<'_, 'ast, '_>,
|
||||
source: PathSource<'_, 'ast, 'ra>,
|
||||
) -> Result<Option<PartialRes>, Spanned<ResolutionError<'ra>>> {
|
||||
debug!(
|
||||
"resolve_qpath(qself={:?}, path={:?}, ns={:?}, finalize={:?})",
|
||||
|
|
@ -4551,7 +4553,7 @@ impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> {
|
|||
)));
|
||||
}
|
||||
|
||||
let result = match self.resolve_path(path, Some(ns), Some(finalize)) {
|
||||
let result = match self.resolve_path(path, Some(ns), Some(finalize), source) {
|
||||
PathResult::NonModule(path_res) => path_res,
|
||||
PathResult::Module(ModuleOrUniformRoot::Module(module)) if !module.is_normal() => {
|
||||
PartialRes::new(module.res().unwrap())
|
||||
|
|
@ -4774,7 +4776,7 @@ impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> {
|
|||
}
|
||||
|
||||
ExprKind::Struct(ref se) => {
|
||||
self.smart_resolve_path(expr.id, &se.qself, &se.path, PathSource::Struct);
|
||||
self.smart_resolve_path(expr.id, &se.qself, &se.path, PathSource::Struct(parent));
|
||||
// This is the same as `visit::walk_expr(self, expr);`, but we want to pass the
|
||||
// parent in for accurate suggestions when encountering `Foo { bar }` that should
|
||||
// have been `Foo { bar: self.bar }`.
|
||||
|
|
|
|||
|
|
@ -174,7 +174,7 @@ impl<'ast, 'ra, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> {
|
|||
&mut self,
|
||||
path: &[Segment],
|
||||
span: Span,
|
||||
source: PathSource<'_, '_, '_>,
|
||||
source: PathSource<'_, 'ast, 'ra>,
|
||||
res: Option<Res>,
|
||||
) -> BaseError {
|
||||
// Make the base error.
|
||||
|
|
@ -318,7 +318,7 @@ impl<'ast, 'ra, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> {
|
|||
(String::new(), "the crate root".to_string(), Some(CRATE_DEF_ID.to_def_id()), None)
|
||||
} else {
|
||||
let mod_path = &path[..path.len() - 1];
|
||||
let mod_res = self.resolve_path(mod_path, Some(TypeNS), None);
|
||||
let mod_res = self.resolve_path(mod_path, Some(TypeNS), None, source);
|
||||
let mod_prefix = match mod_res {
|
||||
PathResult::Module(ModuleOrUniformRoot::Module(module)) => module.res(),
|
||||
_ => None,
|
||||
|
|
@ -419,7 +419,7 @@ impl<'ast, 'ra, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> {
|
|||
path: &[Segment],
|
||||
following_seg: Option<&Segment>,
|
||||
span: Span,
|
||||
source: PathSource<'_, '_, '_>,
|
||||
source: PathSource<'_, 'ast, 'ra>,
|
||||
res: Option<Res>,
|
||||
qself: Option<&QSelf>,
|
||||
) -> (Diag<'tcx>, Vec<ImportSuggestion>) {
|
||||
|
|
@ -1014,7 +1014,7 @@ impl<'ast, 'ra, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> {
|
|||
fn suggest_typo(
|
||||
&mut self,
|
||||
err: &mut Diag<'_>,
|
||||
source: PathSource<'_, '_, '_>,
|
||||
source: PathSource<'_, 'ast, 'ra>,
|
||||
path: &[Segment],
|
||||
following_seg: Option<&Segment>,
|
||||
span: Span,
|
||||
|
|
@ -1333,7 +1333,7 @@ impl<'ast, 'ra, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> {
|
|||
fn suggest_swapping_misplaced_self_ty_and_trait(
|
||||
&mut self,
|
||||
err: &mut Diag<'_>,
|
||||
source: PathSource<'_, '_, '_>,
|
||||
source: PathSource<'_, 'ast, 'ra>,
|
||||
res: Option<Res>,
|
||||
span: Span,
|
||||
) {
|
||||
|
|
@ -1341,7 +1341,7 @@ impl<'ast, 'ra, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> {
|
|||
self.diag_metadata.currently_processing_impl_trait.clone()
|
||||
&& let TyKind::Path(_, self_ty_path) = &self_ty.kind
|
||||
&& let PathResult::Module(ModuleOrUniformRoot::Module(module)) =
|
||||
self.resolve_path(&Segment::from_path(self_ty_path), Some(TypeNS), None)
|
||||
self.resolve_path(&Segment::from_path(self_ty_path), Some(TypeNS), None, source)
|
||||
&& let ModuleKind::Def(DefKind::Trait, ..) = module.kind
|
||||
&& trait_ref.path.span == span
|
||||
&& let PathSource::Trait(_) = source
|
||||
|
|
@ -1449,13 +1449,13 @@ impl<'ast, 'ra, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> {
|
|||
fn get_single_associated_item(
|
||||
&mut self,
|
||||
path: &[Segment],
|
||||
source: &PathSource<'_, '_, '_>,
|
||||
source: &PathSource<'_, 'ast, 'ra>,
|
||||
filter_fn: &impl Fn(Res) -> bool,
|
||||
) -> Option<TypoSuggestion> {
|
||||
if let crate::PathSource::TraitItem(_, _) = source {
|
||||
let mod_path = &path[..path.len() - 1];
|
||||
if let PathResult::Module(ModuleOrUniformRoot::Module(module)) =
|
||||
self.resolve_path(mod_path, None, None)
|
||||
self.resolve_path(mod_path, None, None, *source)
|
||||
{
|
||||
let targets: Vec<_> = self
|
||||
.r
|
||||
|
|
@ -1854,7 +1854,7 @@ impl<'ast, 'ra, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> {
|
|||
PathSource::Expr(Some(Expr {
|
||||
kind: ExprKind::Index(..) | ExprKind::Call(..), ..
|
||||
}))
|
||||
| PathSource::Struct,
|
||||
| PathSource::Struct(_),
|
||||
) => {
|
||||
// Don't suggest macro if it's unstable.
|
||||
let suggestable = def_id.is_local()
|
||||
|
|
@ -2502,7 +2502,7 @@ impl<'ast, 'ra, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> {
|
|||
// Search in module.
|
||||
let mod_path = &path[..path.len() - 1];
|
||||
if let PathResult::Module(ModuleOrUniformRoot::Module(module)) =
|
||||
self.resolve_path(mod_path, Some(TypeNS), None)
|
||||
self.resolve_path(mod_path, Some(TypeNS), None, PathSource::Type)
|
||||
{
|
||||
self.r.add_module_candidates(module, &mut names, &filter_fn, None);
|
||||
}
|
||||
|
|
@ -3673,7 +3673,12 @@ impl<'ast, 'ra, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> {
|
|||
if let TyKind::Path(None, path) = &ty.kind {
|
||||
// Check if the path being borrowed is likely to be owned.
|
||||
let path: Vec<_> = Segment::from_path(path);
|
||||
match self.resolve_path(&path, Some(TypeNS), None) {
|
||||
match self.resolve_path(
|
||||
&path,
|
||||
Some(TypeNS),
|
||||
None,
|
||||
PathSource::Type,
|
||||
) {
|
||||
PathResult::Module(ModuleOrUniformRoot::Module(module)) => {
|
||||
match module.res() {
|
||||
Some(Res::PrimTy(PrimTy::Str)) => {
|
||||
|
|
|
|||
|
|
@ -822,6 +822,7 @@ struct PrivacyError<'ra> {
|
|||
parent_scope: ParentScope<'ra>,
|
||||
/// Is the format `use a::{b,c}`?
|
||||
single_nested: bool,
|
||||
source: Option<ast::Expr>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
|
|
@ -1064,6 +1065,7 @@ pub struct Resolver<'ra, 'tcx> {
|
|||
|
||||
/// N.B., this is used only for better diagnostics, not name resolution itself.
|
||||
field_names: LocalDefIdMap<Vec<Ident>>,
|
||||
field_defaults: LocalDefIdMap<Vec<Symbol>>,
|
||||
|
||||
/// Span of the privacy modifier in fields of an item `DefId` accessible with dot syntax.
|
||||
/// Used for hints during error reporting.
|
||||
|
|
@ -1538,6 +1540,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
|
|||
extern_prelude,
|
||||
|
||||
field_names: Default::default(),
|
||||
field_defaults: Default::default(),
|
||||
field_visibility_spans: FxHashMap::default(),
|
||||
|
||||
determined_imports: Vec::new(),
|
||||
|
|
@ -2318,6 +2321,21 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
|
|||
}
|
||||
}
|
||||
|
||||
fn field_defaults(&self, def_id: DefId) -> Option<Vec<Symbol>> {
|
||||
match def_id.as_local() {
|
||||
Some(def_id) => self.field_defaults.get(&def_id).cloned(),
|
||||
None => Some(
|
||||
self.tcx
|
||||
.associated_item_def_ids(def_id)
|
||||
.iter()
|
||||
.filter_map(|&def_id| {
|
||||
self.tcx.default_field(def_id).map(|_| self.tcx.item_name(def_id))
|
||||
})
|
||||
.collect(),
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
/// Checks if an expression refers to a function marked with
|
||||
/// `#[rustc_legacy_const_generics]` and returns the argument index list
|
||||
/// from the attribute.
|
||||
|
|
|
|||
|
|
@ -3,3 +3,13 @@
|
|||
pub struct A {
|
||||
pub a: isize = 42,
|
||||
}
|
||||
|
||||
struct Priv;
|
||||
|
||||
pub struct B {
|
||||
pub a: Priv = Priv,
|
||||
}
|
||||
|
||||
pub struct C {
|
||||
pub a: Priv,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,29 @@
|
|||
//@ aux-build:struct_field_default.rs
|
||||
#![feature(default_field_values)]
|
||||
|
||||
extern crate struct_field_default as xc;
|
||||
|
||||
use m::S;
|
||||
|
||||
mod m {
|
||||
pub struct S {
|
||||
pub field: () = (),
|
||||
pub field1: Priv1 = Priv1 {},
|
||||
pub field2: Priv2 = Priv2,
|
||||
}
|
||||
struct Priv1 {}
|
||||
struct Priv2;
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let _ = S { field: (), field1: m::Priv1 {} };
|
||||
//~^ ERROR missing field `field2`
|
||||
//~| ERROR struct `Priv1` is private
|
||||
let _ = S { field: (), field1: m::Priv1 {}, field2: m::Priv2 };
|
||||
//~^ ERROR struct `Priv1` is private
|
||||
//~| ERROR unit struct `Priv2` is private
|
||||
let _ = xc::B { a: xc::Priv };
|
||||
//~^ ERROR unit struct `Priv` is private
|
||||
let _ = xc::C { a: xc::Priv };
|
||||
//~^ ERROR unit struct `Priv` is private
|
||||
}
|
||||
|
|
@ -0,0 +1,105 @@
|
|||
error[E0603]: struct `Priv1` is private
|
||||
--> $DIR/non-exhaustive-ctor-2.rs:19:39
|
||||
|
|
||||
LL | let _ = S { field: (), field1: m::Priv1 {} };
|
||||
| ------ ^^^^^ private struct
|
||||
| |
|
||||
| while setting this field
|
||||
|
|
||||
note: the struct `Priv1` is defined here
|
||||
--> $DIR/non-exhaustive-ctor-2.rs:14:4
|
||||
|
|
||||
LL | struct Priv1 {}
|
||||
| ^^^^^^^^^^^^
|
||||
help: the type `Priv1` of field `field1` is private, but you can construct the default value defined for it in `S` using `..` in the struct initializer expression
|
||||
|
|
||||
LL - let _ = S { field: (), field1: m::Priv1 {} };
|
||||
LL + let _ = S { field: (), .. };
|
||||
|
|
||||
|
||||
error[E0603]: struct `Priv1` is private
|
||||
--> $DIR/non-exhaustive-ctor-2.rs:22:39
|
||||
|
|
||||
LL | let _ = S { field: (), field1: m::Priv1 {}, field2: m::Priv2 };
|
||||
| ------ ^^^^^ private struct
|
||||
| |
|
||||
| while setting this field
|
||||
|
|
||||
note: the struct `Priv1` is defined here
|
||||
--> $DIR/non-exhaustive-ctor-2.rs:14:4
|
||||
|
|
||||
LL | struct Priv1 {}
|
||||
| ^^^^^^^^^^^^
|
||||
help: the type `Priv1` of field `field1` is private, but you can construct the default value defined for it in `S` using `..` in the struct initializer expression
|
||||
|
|
||||
LL - let _ = S { field: (), field1: m::Priv1 {}, field2: m::Priv2 };
|
||||
LL + let _ = S { field: (), field2: m::Priv2, .. };
|
||||
|
|
||||
|
||||
error[E0603]: unit struct `Priv2` is private
|
||||
--> $DIR/non-exhaustive-ctor-2.rs:22:60
|
||||
|
|
||||
LL | let _ = S { field: (), field1: m::Priv1 {}, field2: m::Priv2 };
|
||||
| ------ ^^^^^ private unit struct
|
||||
| |
|
||||
| while setting this field
|
||||
|
|
||||
note: the unit struct `Priv2` is defined here
|
||||
--> $DIR/non-exhaustive-ctor-2.rs:15:4
|
||||
|
|
||||
LL | struct Priv2;
|
||||
| ^^^^^^^^^^^^^
|
||||
help: the type `Priv2` of field `field2` is private, but you can construct the default value defined for it in `S` using `..` in the struct initializer expression
|
||||
|
|
||||
LL - let _ = S { field: (), field1: m::Priv1 {}, field2: m::Priv2 };
|
||||
LL + let _ = S { field: (), field1: m::Priv1 {}, .. };
|
||||
|
|
||||
|
||||
error[E0603]: unit struct `Priv` is private
|
||||
--> $DIR/non-exhaustive-ctor-2.rs:25:28
|
||||
|
|
||||
LL | let _ = xc::B { a: xc::Priv };
|
||||
| - ^^^^ private unit struct
|
||||
| |
|
||||
| while setting this field
|
||||
|
|
||||
note: the unit struct `Priv` is defined here
|
||||
--> $DIR/auxiliary/struct_field_default.rs:7:1
|
||||
|
|
||||
LL | struct Priv;
|
||||
| ^^^^^^^^^^^
|
||||
help: the type `Priv` of field `a` is private, but you can construct the default value defined for it in `B` using `..` in the struct initializer expression
|
||||
|
|
||||
LL - let _ = xc::B { a: xc::Priv };
|
||||
LL + let _ = xc::B { .. };
|
||||
|
|
||||
|
||||
error[E0603]: unit struct `Priv` is private
|
||||
--> $DIR/non-exhaustive-ctor-2.rs:27:28
|
||||
|
|
||||
LL | let _ = xc::C { a: xc::Priv };
|
||||
| - ^^^^ private unit struct
|
||||
| |
|
||||
| while setting this field
|
||||
|
|
||||
note: the unit struct `Priv` is defined here
|
||||
--> $DIR/auxiliary/struct_field_default.rs:7:1
|
||||
|
|
||||
LL | struct Priv;
|
||||
| ^^^^^^^^^^^
|
||||
|
||||
error[E0063]: missing field `field2` in initializer of `S`
|
||||
--> $DIR/non-exhaustive-ctor-2.rs:19:13
|
||||
|
|
||||
LL | let _ = S { field: (), field1: m::Priv1 {} };
|
||||
| ^ missing `field2`
|
||||
|
|
||||
help: all remaining fields have default values, you can use those values with `..`
|
||||
|
|
||||
LL | let _ = S { field: (), field1: m::Priv1 {}, .. };
|
||||
| ++++
|
||||
|
||||
error: aborting due to 6 previous errors
|
||||
|
||||
Some errors have detailed explanations: E0063, E0603.
|
||||
For more information about an error, try `rustc --explain E0063`.
|
||||
Loading…
Add table
Add a link
Reference in a new issue