Skip to content

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

Merged
merged 14 commits into from
Nov 19, 2023
Merged
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
16 changes: 7 additions & 9 deletions compiler/rustc_errors/src/diagnostic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -777,17 +777,15 @@ impl Diagnostic {
applicability: Applicability,
style: SuggestionStyle,
) -> &mut Self {
let mut suggestions: Vec<_> = suggestions.into_iter().collect();
suggestions.sort();

debug_assert!(
!(sp.is_empty() && suggestions.iter().any(|suggestion| suggestion.is_empty())),
"Span must not be empty and have no suggestion"
);

let substitutions = suggestions
.into_iter()
.map(|snippet| Substitution { parts: vec![SubstitutionPart { snippet, span: sp }] })
.map(|snippet| {
debug_assert!(
!(sp.is_empty() && snippet.is_empty()),
"Span must not be empty and have no suggestion"
);
Substitution { parts: vec![SubstitutionPart { snippet, span: sp }] }
})
.collect();
self.push_suggestion(CodeSuggestion {
substitutions,
Expand Down
6 changes: 5 additions & 1 deletion compiler/rustc_hir_analysis/src/astconv/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -945,7 +945,11 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
Applicability::MachineApplicable,
);
} else {
match (types, traits) {
let mut types = types.to_vec();
types.sort();
let mut traits = traits.to_vec();
traits.sort();
match (&types[..], &traits[..]) {
([], []) => {
err.span_suggestion_verbose(
span,
Expand Down
81 changes: 79 additions & 2 deletions compiler/rustc_hir_typeck/src/expr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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>],
) {
Expand Down Expand Up @@ -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);
Copy link
Contributor

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?

Copy link
Contributor Author

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

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,
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

shouldn't this be if args.len() > 0 { HasPlaceholders } else { MaybeIncorrect }? I was under the impression that was more severe than MaybeIncorrect

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

MaybeIncorrect is actually more severe than HasPlaceholders

);
}
_ => {
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();
}

Expand Down Expand Up @@ -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(
Expand All @@ -2724,6 +2800,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
.collect::<String>()
})
.collect::<Vec<_>>();
candidate_fields.sort();
Copy link
Contributor

Choose a reason for hiding this comment

The 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?

  • all places where you had to call sort.

Copy link
Contributor Author

Choose a reason for hiding this comment

The 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 {
Expand Down
15 changes: 7 additions & 8 deletions compiler/rustc_hir_typeck/src/method/suggest.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1426,6 +1426,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
if !suggs.is_empty()
&& let Some(span) = sugg_span
{
suggs.sort();
err.span_suggestions(
span.with_hi(item_name.span.lo()),
"use fully-qualified syntax to disambiguate",
Expand Down Expand Up @@ -2000,8 +2001,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
self.tcx.get_diagnostic_item(sym::Borrow),
self.tcx.get_diagnostic_item(sym::BorrowMut),
];
let candidate_fields: Vec<_> = fields
.iter()
let mut candidate_fields: Vec<_> = fields
.into_iter()
.filter_map(|candidate_field| {
self.check_for_nested_field_satisfying(
span,
Expand Down Expand Up @@ -2035,6 +2036,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
.join(".")
})
.collect();
candidate_fields.sort();

let len = candidate_fields.len();
if len > 0 {
Expand Down Expand Up @@ -2567,13 +2569,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
self.tcx.item_name(*trait_did),
)
});
let mut sugg: Vec<_> = path_strings.chain(glob_path_strings).collect();
sugg.sort();

err.span_suggestions(
span,
msg,
path_strings.chain(glob_path_strings),
Applicability::MaybeIncorrect,
);
err.span_suggestions(span, msg, sugg, Applicability::MaybeIncorrect);
}

fn suggest_valid_traits(
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_parse/src/validate_attr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,7 @@ fn emit_malformed_attribute(
msg.push_str(&format!("`{code}`"));
suggestions.push(code);
}
suggestions.sort();
if should_warn(name) {
sess.buffer_lint(&ILL_FORMED_ATTRIBUTE_INPUT, span, ast::CRATE_NODE_ID, msg);
} else {
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_resolve/src/diagnostics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2608,6 +2608,7 @@ fn show_candidates(
path_strings.extend(core_path_strings);
path_strings.dedup_by(|a, b| a.0 == b.0);
}
accessible_path_strings.sort();

if !accessible_path_strings.is_empty() {
let (determiner, kind, name, through) =
Expand Down
Loading