-
Notifications
You must be signed in to change notification settings - Fork 13.3k
When encountering struct fn call literal with private fields, suggest all builders #117683
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
Changes from all commits
42aa127
f1ae02f
a4f47de
e0e379b
12a8bb8
987155f
1dfec45
be0958f
69edf8e
00265f0
4d16171
a519c9b
b0d7ccb
ac56b06
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -1897,7 +1897,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { | |
.collect(); | ||
|
||
if !private_fields.is_empty() { | ||
self.report_private_fields(adt_ty, span, private_fields, ast_fields); | ||
self.report_private_fields(adt_ty, span, expr.span, private_fields, ast_fields); | ||
} else { | ||
self.report_missing_fields( | ||
adt_ty, | ||
|
@@ -2056,6 +2056,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { | |
&self, | ||
adt_ty: Ty<'tcx>, | ||
span: Span, | ||
expr_span: Span, | ||
private_fields: Vec<&ty::FieldDef>, | ||
used_fields: &'tcx [hir::ExprField<'tcx>], | ||
) { | ||
|
@@ -2100,6 +2101,81 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { | |
were = pluralize!("was", remaining_private_fields_len), | ||
)); | ||
} | ||
|
||
if let ty::Adt(def, _) = adt_ty.kind() { | ||
let def_id = def.did(); | ||
let mut items = self | ||
.tcx | ||
.inherent_impls(def_id) | ||
.iter() | ||
.flat_map(|i| self.tcx.associated_items(i).in_definition_order()) | ||
// Only assoc fn with no receivers. | ||
.filter(|item| { | ||
matches!(item.kind, ty::AssocKind::Fn) && !item.fn_has_self_parameter | ||
}) | ||
.filter_map(|item| { | ||
// Only assoc fns that return `Self` | ||
let fn_sig = self.tcx.fn_sig(item.def_id).skip_binder(); | ||
let ret_ty = fn_sig.output(); | ||
let ret_ty = | ||
self.tcx.normalize_erasing_late_bound_regions(self.param_env, ret_ty); | ||
if !self.can_eq(self.param_env, ret_ty, adt_ty) { | ||
return None; | ||
} | ||
let input_len = fn_sig.inputs().skip_binder().len(); | ||
let order = !item.name.as_str().starts_with("new"); | ||
Some((order, item.name, input_len)) | ||
}) | ||
.collect::<Vec<_>>(); | ||
items.sort_by_key(|(order, _, _)| *order); | ||
let suggestion = |name, args| { | ||
format!( | ||
"::{name}({})", | ||
std::iter::repeat("_").take(args).collect::<Vec<_>>().join(", ") | ||
) | ||
}; | ||
match &items[..] { | ||
[] => {} | ||
[(_, name, args)] => { | ||
err.span_suggestion_verbose( | ||
span.shrink_to_hi().with_hi(expr_span.hi()), | ||
format!("you might have meant to use the `{name}` associated function"), | ||
suggestion(name, *args), | ||
Applicability::MaybeIncorrect, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. shouldn't this be There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
); | ||
} | ||
_ => { | ||
err.span_suggestions( | ||
span.shrink_to_hi().with_hi(expr_span.hi()), | ||
"you might have meant to use an associated function to build this type", | ||
items | ||
.iter() | ||
.map(|(_, name, args)| suggestion(name, *args)) | ||
.collect::<Vec<String>>(), | ||
Applicability::MaybeIncorrect, | ||
); | ||
} | ||
} | ||
if let Some(default_trait) = self.tcx.get_diagnostic_item(sym::Default) | ||
&& self | ||
.infcx | ||
.type_implements_trait(default_trait, [adt_ty], self.param_env) | ||
.may_apply() | ||
{ | ||
err.multipart_suggestion( | ||
"consider using the `Default` trait", | ||
vec![ | ||
(span.shrink_to_lo(), "<".to_string()), | ||
( | ||
span.shrink_to_hi().with_hi(expr_span.hi()), | ||
" as std::default::Default>::default()".to_string(), | ||
), | ||
], | ||
Applicability::MaybeIncorrect, | ||
); | ||
} | ||
} | ||
|
||
err.emit(); | ||
} | ||
|
||
|
@@ -2703,7 +2779,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { | |
self.get_field_candidates_considering_privacy(span, ty, mod_id, id) | ||
{ | ||
let field_names = found_fields.iter().map(|field| field.name).collect::<Vec<_>>(); | ||
let candidate_fields: Vec<_> = found_fields | ||
let mut candidate_fields: Vec<_> = found_fields | ||
.into_iter() | ||
.filter_map(|candidate_field| { | ||
self.check_for_nested_field_satisfying( | ||
|
@@ -2724,6 +2800,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { | |
.collect::<String>() | ||
}) | ||
.collect::<Vec<_>>(); | ||
candidate_fields.sort(); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Could you add a FIXME so we don't forget to track down the non determinism?
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I sorted in all these to maintain the current behavior, I haven't validated that all of them needed to be sorted. |
||
|
||
let len = candidate_fields.len(); | ||
if len > 0 { | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should we give higher priority to methods with fewer inputs?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I feel like definition order is a more reliable heuristic