Skip to content

Commit

Permalink
Auto merge of rust-lang#117683 - estebank:priv-builder-sugg, r=cjgillot
Browse files Browse the repository at this point in the history
When encountering struct fn call literal with private fields, suggest all builders

When encountering code like `Box(42)`, suggest `Box::new(42)` and *all* other associated functions that return `-> Box<T>`.

Add a way to give pre-sorted suggestions.
  • Loading branch information
bors committed Nov 19, 2023
2 parents d19980e + ac56b06 commit 9a66e44
Show file tree
Hide file tree
Showing 18 changed files with 464 additions and 124 deletions.
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);
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,
);
}
_ => {
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();

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

0 comments on commit 9a66e44

Please sign in to comment.