Skip to content

Commit 212d28d

Browse files
committed
Auto merge of #145231 - samueltardieu:rollup-jqcabpo, r=samueltardieu
Rollup of 3 pull requests Successful merges: - #135846 (Detect struct construction with private field in field with default) - #144558 (Point at the `Fn()` or `FnMut()` bound that coerced a closure, which caused a move error) - #145149 (Make config method invoke inside parse use dwn_ctx) r? `@ghost` `@rustbot` modify labels: rollup
2 parents c8ca44c + a1d8df8 commit 212d28d

30 files changed

+1701
-368
lines changed

compiler/rustc_borrowck/src/diagnostics/move_errors.rs

Lines changed: 49 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,8 @@ use rustc_middle::bug;
99
use rustc_middle::mir::*;
1010
use rustc_middle::ty::{self, Ty, TyCtxt};
1111
use rustc_mir_dataflow::move_paths::{LookupResult, MovePathIndex};
12-
use rustc_span::{BytePos, ExpnKind, MacroKind, Span};
12+
use rustc_span::def_id::DefId;
13+
use rustc_span::{BytePos, DUMMY_SP, ExpnKind, MacroKind, Span};
1314
use rustc_trait_selection::error_reporting::traits::FindExprBySpan;
1415
use rustc_trait_selection::infer::InferCtxtExt;
1516
use tracing::debug;
@@ -507,12 +508,18 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
507508
);
508509

509510
let closure_span = tcx.def_span(def_id);
511+
510512
self.cannot_move_out_of(span, &place_description)
511513
.with_span_label(upvar_span, "captured outer variable")
512514
.with_span_label(
513515
closure_span,
514516
format!("captured by this `{closure_kind}` closure"),
515517
)
518+
.with_span_help(
519+
self.get_closure_bound_clause_span(*def_id),
520+
"`Fn` and `FnMut` closures require captured values to be able to be \
521+
consumed multiple times, but an `FnOnce` consume them only once",
522+
)
516523
}
517524
_ => {
518525
let source = self.borrowed_content_source(deref_base);
@@ -561,6 +568,47 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
561568
err
562569
}
563570

571+
fn get_closure_bound_clause_span(&self, def_id: DefId) -> Span {
572+
let tcx = self.infcx.tcx;
573+
let typeck_result = tcx.typeck(self.mir_def_id());
574+
// Check whether the closure is an argument to a call, if so,
575+
// get the instantiated where-bounds of that call.
576+
let closure_hir_id = tcx.local_def_id_to_hir_id(def_id.expect_local());
577+
let hir::Node::Expr(parent) = tcx.parent_hir_node(closure_hir_id) else { return DUMMY_SP };
578+
579+
let predicates = match parent.kind {
580+
hir::ExprKind::Call(callee, _) => {
581+
let Some(ty) = typeck_result.node_type_opt(callee.hir_id) else { return DUMMY_SP };
582+
let ty::FnDef(fn_def_id, args) = ty.kind() else { return DUMMY_SP };
583+
tcx.predicates_of(fn_def_id).instantiate(tcx, args)
584+
}
585+
hir::ExprKind::MethodCall(..) => {
586+
let Some((_, method)) = typeck_result.type_dependent_def(parent.hir_id) else {
587+
return DUMMY_SP;
588+
};
589+
let args = typeck_result.node_args(parent.hir_id);
590+
tcx.predicates_of(method).instantiate(tcx, args)
591+
}
592+
_ => return DUMMY_SP,
593+
};
594+
595+
// Check whether one of the where-bounds requires the closure to impl `Fn[Mut]`.
596+
for (pred, span) in predicates.predicates.iter().zip(predicates.spans.iter()) {
597+
if let Some(clause) = pred.as_trait_clause()
598+
&& let ty::Closure(clause_closure_def_id, _) = clause.self_ty().skip_binder().kind()
599+
&& *clause_closure_def_id == def_id
600+
&& (tcx.lang_items().fn_mut_trait() == Some(clause.def_id())
601+
|| tcx.lang_items().fn_trait() == Some(clause.def_id()))
602+
{
603+
// Found `<TyOfCapturingClosure as FnMut>`
604+
// We point at the `Fn()` or `FnMut()` bound that coerced the closure, which
605+
// could be changed to `FnOnce()` to avoid the move error.
606+
return *span;
607+
}
608+
}
609+
DUMMY_SP
610+
}
611+
564612
fn add_move_hints(&self, error: GroupedMoveError<'tcx>, err: &mut Diag<'_>, span: Span) {
565613
match error {
566614
GroupedMoveError::MovesFromPlace { mut binds_to, move_from, .. } => {

compiler/rustc_errors/src/diagnostic.rs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -847,17 +847,18 @@ impl<'a, G: EmissionGuarantee> Diag<'a, G> {
847847
self
848848
}
849849

850+
with_fn! { with_span_help,
850851
/// Prints the span with some help above it.
851852
/// This is like [`Diag::help()`], but it gets its own span.
852853
#[rustc_lint_diagnostics]
853-
pub fn span_help<S: Into<MultiSpan>>(
854+
pub fn span_help(
854855
&mut self,
855-
sp: S,
856+
sp: impl Into<MultiSpan>,
856857
msg: impl Into<SubdiagMessage>,
857858
) -> &mut Self {
858859
self.sub(Level::Help, msg, sp.into());
859860
self
860-
}
861+
} }
861862

862863
/// Disallow attaching suggestions to this diagnostic.
863864
/// Any suggestions attached e.g. with the `span_suggestion_*` methods

compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -395,6 +395,7 @@ provide! { tcx, def_id, other, cdata,
395395

396396
crate_extern_paths => { cdata.source().paths().cloned().collect() }
397397
expn_that_defined => { cdata.get_expn_that_defined(def_id.index, tcx.sess) }
398+
default_field => { cdata.get_default_field(def_id.index) }
398399
is_doc_hidden => { cdata.get_attr_flags(def_id.index).contains(AttrFlags::IS_DOC_HIDDEN) }
399400
doc_link_resolutions => { tcx.arena.alloc(cdata.get_doc_link_resolutions(def_id.index)) }
400401
doc_link_traits_in_scope => {

compiler/rustc_middle/src/query/mod.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1864,6 +1864,12 @@ rustc_queries! {
18641864
feedable
18651865
}
18661866

1867+
/// Returns whether the field corresponding to the `DefId` has a default field value.
1868+
query default_field(def_id: DefId) -> Option<DefId> {
1869+
desc { |tcx| "looking up the `const` corresponding to the default for `{}`", tcx.def_path_str(def_id) }
1870+
separate_provide_extern
1871+
}
1872+
18671873
query check_well_formed(key: LocalDefId) -> Result<(), ErrorGuaranteed> {
18681874
desc { |tcx| "checking that `{}` is well-formed", tcx.def_path_str(key) }
18691875
return_result_from_ensure_ok

compiler/rustc_resolve/src/build_reduced_graph.rs

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -420,14 +420,18 @@ impl<'a, 'ra, 'tcx> BuildReducedGraphVisitor<'a, 'ra, 'tcx> {
420420
// The fields are not expanded yet.
421421
return;
422422
}
423-
let fields = fields
423+
let field_name = |i, field: &ast::FieldDef| {
424+
field.ident.unwrap_or_else(|| Ident::from_str_and_span(&format!("{i}"), field.span))
425+
};
426+
let field_names: Vec<_> =
427+
fields.iter().enumerate().map(|(i, field)| field_name(i, field)).collect();
428+
let defaults = fields
424429
.iter()
425430
.enumerate()
426-
.map(|(i, field)| {
427-
field.ident.unwrap_or_else(|| Ident::from_str_and_span(&format!("{i}"), field.span))
428-
})
431+
.filter_map(|(i, field)| field.default.as_ref().map(|_| field_name(i, field).name))
429432
.collect();
430-
self.r.field_names.insert(def_id, fields);
433+
self.r.field_names.insert(def_id, field_names);
434+
self.r.field_defaults.insert(def_id, defaults);
431435
}
432436

433437
fn insert_field_visibilities_local(&mut self, def_id: DefId, fields: &[ast::FieldDef]) {

compiler/rustc_resolve/src/diagnostics.rs

Lines changed: 90 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1943,8 +1943,15 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
19431943
}
19441944

19451945
fn report_privacy_error(&mut self, privacy_error: &PrivacyError<'ra>) {
1946-
let PrivacyError { ident, binding, outermost_res, parent_scope, single_nested, dedup_span } =
1947-
*privacy_error;
1946+
let PrivacyError {
1947+
ident,
1948+
binding,
1949+
outermost_res,
1950+
parent_scope,
1951+
single_nested,
1952+
dedup_span,
1953+
ref source,
1954+
} = *privacy_error;
19481955

19491956
let res = binding.res();
19501957
let ctor_fields_span = self.ctor_fields_span(binding);
@@ -1960,6 +1967,8 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
19601967
let mut err =
19611968
self.dcx().create_err(errors::IsPrivate { span: ident.span, ident_descr, ident });
19621969

1970+
self.mention_default_field_values(source, ident, &mut err);
1971+
19631972
let mut not_publicly_reexported = false;
19641973
if let Some((this_res, outer_ident)) = outermost_res {
19651974
let import_suggestions = self.lookup_import_candidates(
@@ -2141,6 +2150,85 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
21412150
err.emit();
21422151
}
21432152

2153+
/// When a private field is being set that has a default field value, we suggest using `..` and
2154+
/// setting the value of that field implicitly with its default.
2155+
///
2156+
/// If we encounter code like
2157+
/// ```text
2158+
/// struct Priv;
2159+
/// pub struct S {
2160+
/// pub field: Priv = Priv,
2161+
/// }
2162+
/// ```
2163+
/// which is used from a place where `Priv` isn't accessible
2164+
/// ```text
2165+
/// let _ = S { field: m::Priv1 {} };
2166+
/// // ^^^^^ private struct
2167+
/// ```
2168+
/// we will suggest instead using the `default_field_values` syntax instead:
2169+
/// ```text
2170+
/// let _ = S { .. };
2171+
/// ```
2172+
fn mention_default_field_values(
2173+
&self,
2174+
source: &Option<ast::Expr>,
2175+
ident: Ident,
2176+
err: &mut Diag<'_>,
2177+
) {
2178+
let Some(expr) = source else { return };
2179+
let ast::ExprKind::Struct(struct_expr) = &expr.kind else { return };
2180+
// We don't have to handle type-relative paths because they're forbidden in ADT
2181+
// expressions, but that would change with `#[feature(more_qualified_paths)]`.
2182+
let Some(Res::Def(_, def_id)) =
2183+
self.partial_res_map[&struct_expr.path.segments.iter().last().unwrap().id].full_res()
2184+
else {
2185+
return;
2186+
};
2187+
let Some(default_fields) = self.field_defaults(def_id) else { return };
2188+
if struct_expr.fields.is_empty() {
2189+
return;
2190+
}
2191+
let last_span = struct_expr.fields.iter().last().unwrap().span;
2192+
let mut iter = struct_expr.fields.iter().peekable();
2193+
let mut prev: Option<Span> = None;
2194+
while let Some(field) = iter.next() {
2195+
if field.expr.span.overlaps(ident.span) {
2196+
err.span_label(field.ident.span, "while setting this field");
2197+
if default_fields.contains(&field.ident.name) {
2198+
let sugg = if last_span == field.span {
2199+
vec![(field.span, "..".to_string())]
2200+
} else {
2201+
vec![
2202+
(
2203+
// Account for trailing commas and ensure we remove them.
2204+
match (prev, iter.peek()) {
2205+
(_, Some(next)) => field.span.with_hi(next.span.lo()),
2206+
(Some(prev), _) => field.span.with_lo(prev.hi()),
2207+
(None, None) => field.span,
2208+
},
2209+
String::new(),
2210+
),
2211+
(last_span.shrink_to_hi(), ", ..".to_string()),
2212+
]
2213+
};
2214+
err.multipart_suggestion_verbose(
2215+
format!(
2216+
"the type `{ident}` of field `{}` is private, but you can construct \
2217+
the default value defined for it in `{}` using `..` in the struct \
2218+
initializer expression",
2219+
field.ident,
2220+
self.tcx.item_name(def_id),
2221+
),
2222+
sugg,
2223+
Applicability::MachineApplicable,
2224+
);
2225+
break;
2226+
}
2227+
}
2228+
prev = Some(field.span);
2229+
}
2230+
}
2231+
21442232
pub(crate) fn find_similarly_named_module_or_crate(
21452233
&self,
21462234
ident: Symbol,

compiler/rustc_resolve/src/ident.rs

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1029,6 +1029,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
10291029
binding,
10301030
dedup_span: path_span,
10311031
outermost_res: None,
1032+
source: None,
10321033
parent_scope: *parent_scope,
10331034
single_nested: path_span != root_span,
10341035
});
@@ -1435,7 +1436,16 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
14351436
parent_scope: &ParentScope<'ra>,
14361437
ignore_import: Option<Import<'ra>>,
14371438
) -> PathResult<'ra> {
1438-
self.resolve_path_with_ribs(path, opt_ns, parent_scope, None, None, None, ignore_import)
1439+
self.resolve_path_with_ribs(
1440+
path,
1441+
opt_ns,
1442+
parent_scope,
1443+
None,
1444+
None,
1445+
None,
1446+
None,
1447+
ignore_import,
1448+
)
14391449
}
14401450
#[instrument(level = "debug", skip(self))]
14411451
pub(crate) fn resolve_path<'r>(
@@ -1451,6 +1461,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
14511461
path,
14521462
opt_ns,
14531463
parent_scope,
1464+
None,
14541465
finalize,
14551466
None,
14561467
ignore_binding,
@@ -1463,6 +1474,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
14631474
path: &[Segment],
14641475
opt_ns: Option<Namespace>, // `None` indicates a module path in import
14651476
parent_scope: &ParentScope<'ra>,
1477+
source: Option<PathSource<'_, '_, '_>>,
14661478
finalize: Option<Finalize>,
14671479
ribs: Option<&PerNS<Vec<Rib<'ra>>>>,
14681480
ignore_binding: Option<NameBinding<'ra>>,
@@ -1645,6 +1657,11 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
16451657
if finalize.is_some() {
16461658
for error in &mut self.get_mut().privacy_errors[privacy_errors_len..] {
16471659
error.outermost_res = Some((res, ident));
1660+
error.source = match source {
1661+
Some(PathSource::Struct(Some(expr)))
1662+
| Some(PathSource::Expr(Some(expr))) => Some(expr.clone()),
1663+
_ => None,
1664+
};
16481665
}
16491666
}
16501667

0 commit comments

Comments
 (0)