Skip to content

Rollup of 3 pull requests #145231

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 17 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
50 changes: 49 additions & 1 deletion compiler/rustc_borrowck/src/diagnostics/move_errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@ use rustc_middle::bug;
use rustc_middle::mir::*;
use rustc_middle::ty::{self, Ty, TyCtxt};
use rustc_mir_dataflow::move_paths::{LookupResult, MovePathIndex};
use rustc_span::{BytePos, ExpnKind, MacroKind, Span};
use rustc_span::def_id::DefId;
use rustc_span::{BytePos, DUMMY_SP, ExpnKind, MacroKind, Span};
use rustc_trait_selection::error_reporting::traits::FindExprBySpan;
use rustc_trait_selection::infer::InferCtxtExt;
use tracing::debug;
Expand Down Expand Up @@ -507,12 +508,18 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
);

let closure_span = tcx.def_span(def_id);

self.cannot_move_out_of(span, &place_description)
.with_span_label(upvar_span, "captured outer variable")
.with_span_label(
closure_span,
format!("captured by this `{closure_kind}` closure"),
)
.with_span_help(
self.get_closure_bound_clause_span(*def_id),
"`Fn` and `FnMut` closures require captured values to be able to be \
consumed multiple times, but an `FnOnce` consume them only once",
)
}
_ => {
let source = self.borrowed_content_source(deref_base);
Expand Down Expand Up @@ -561,6 +568,47 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
err
}

fn get_closure_bound_clause_span(&self, def_id: DefId) -> Span {
let tcx = self.infcx.tcx;
let typeck_result = tcx.typeck(self.mir_def_id());
// Check whether the closure is an argument to a call, if so,
// get the instantiated where-bounds of that call.
let closure_hir_id = tcx.local_def_id_to_hir_id(def_id.expect_local());
let hir::Node::Expr(parent) = tcx.parent_hir_node(closure_hir_id) else { return DUMMY_SP };

let predicates = match parent.kind {
hir::ExprKind::Call(callee, _) => {
let Some(ty) = typeck_result.node_type_opt(callee.hir_id) else { return DUMMY_SP };
let ty::FnDef(fn_def_id, args) = ty.kind() else { return DUMMY_SP };
tcx.predicates_of(fn_def_id).instantiate(tcx, args)
}
hir::ExprKind::MethodCall(..) => {
let Some((_, method)) = typeck_result.type_dependent_def(parent.hir_id) else {
return DUMMY_SP;
};
let args = typeck_result.node_args(parent.hir_id);
tcx.predicates_of(method).instantiate(tcx, args)
}
_ => return DUMMY_SP,
};

// Check whether one of the where-bounds requires the closure to impl `Fn[Mut]`.
for (pred, span) in predicates.predicates.iter().zip(predicates.spans.iter()) {
if let Some(clause) = pred.as_trait_clause()
&& let ty::Closure(clause_closure_def_id, _) = clause.self_ty().skip_binder().kind()
&& *clause_closure_def_id == def_id
&& (tcx.lang_items().fn_mut_trait() == Some(clause.def_id())
|| tcx.lang_items().fn_trait() == Some(clause.def_id()))
{
// Found `<TyOfCapturingClosure as FnMut>`
// We point at the `Fn()` or `FnMut()` bound that coerced the closure, which
// could be changed to `FnOnce()` to avoid the move error.
return *span;
}
}
DUMMY_SP
}

fn add_move_hints(&self, error: GroupedMoveError<'tcx>, err: &mut Diag<'_>, span: Span) {
match error {
GroupedMoveError::MovesFromPlace { mut binds_to, move_from, .. } => {
Expand Down
7 changes: 4 additions & 3 deletions compiler/rustc_errors/src/diagnostic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -847,17 +847,18 @@ impl<'a, G: EmissionGuarantee> Diag<'a, G> {
self
}

with_fn! { with_span_help,
/// Prints the span with some help above it.
/// This is like [`Diag::help()`], but it gets its own span.
#[rustc_lint_diagnostics]
pub fn span_help<S: Into<MultiSpan>>(
pub fn span_help(
&mut self,
sp: S,
sp: impl Into<MultiSpan>,
msg: impl Into<SubdiagMessage>,
) -> &mut Self {
self.sub(Level::Help, msg, sp.into());
self
}
} }

/// Disallow attaching suggestions to this diagnostic.
/// Any suggestions attached e.g. with the `span_suggestion_*` methods
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 => {
Expand Down
6 changes: 6 additions & 0 deletions compiler/rustc_middle/src/query/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
14 changes: 9 additions & 5 deletions compiler/rustc_resolve/src/build_reduced_graph.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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]) {
Expand Down
92 changes: 90 additions & 2 deletions compiler/rustc_resolve/src/diagnostics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand All @@ -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(
Expand Down Expand Up @@ -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,
Expand Down
19 changes: 18 additions & 1 deletion compiler/rustc_resolve/src/ident.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
});
Expand Down Expand Up @@ -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>(
Expand All @@ -1451,6 +1461,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
path,
opt_ns,
parent_scope,
None,
finalize,
None,
ignore_binding,
Expand All @@ -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>>,
Expand Down Expand Up @@ -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,
};
}
}

Expand Down
Loading
Loading