Skip to content

Commit 9a66e44

Browse files
committed
Auto merge of rust-lang#117683 - estebank:priv-builder-sugg, r=cjgillot
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.
2 parents d19980e + ac56b06 commit 9a66e44

File tree

18 files changed

+464
-124
lines changed

18 files changed

+464
-124
lines changed

compiler/rustc_errors/src/diagnostic.rs

+7-9
Original file line numberDiff line numberDiff line change
@@ -777,17 +777,15 @@ impl Diagnostic {
777777
applicability: Applicability,
778778
style: SuggestionStyle,
779779
) -> &mut Self {
780-
let mut suggestions: Vec<_> = suggestions.into_iter().collect();
781-
suggestions.sort();
782-
783-
debug_assert!(
784-
!(sp.is_empty() && suggestions.iter().any(|suggestion| suggestion.is_empty())),
785-
"Span must not be empty and have no suggestion"
786-
);
787-
788780
let substitutions = suggestions
789781
.into_iter()
790-
.map(|snippet| Substitution { parts: vec![SubstitutionPart { snippet, span: sp }] })
782+
.map(|snippet| {
783+
debug_assert!(
784+
!(sp.is_empty() && snippet.is_empty()),
785+
"Span must not be empty and have no suggestion"
786+
);
787+
Substitution { parts: vec![SubstitutionPart { snippet, span: sp }] }
788+
})
791789
.collect();
792790
self.push_suggestion(CodeSuggestion {
793791
substitutions,

compiler/rustc_hir_analysis/src/astconv/mod.rs

+5-1
Original file line numberDiff line numberDiff line change
@@ -945,7 +945,11 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
945945
Applicability::MachineApplicable,
946946
);
947947
} else {
948-
match (types, traits) {
948+
let mut types = types.to_vec();
949+
types.sort();
950+
let mut traits = traits.to_vec();
951+
traits.sort();
952+
match (&types[..], &traits[..]) {
949953
([], []) => {
950954
err.span_suggestion_verbose(
951955
span,

compiler/rustc_hir_typeck/src/expr.rs

+79-2
Original file line numberDiff line numberDiff line change
@@ -1897,7 +1897,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
18971897
.collect();
18981898

18991899
if !private_fields.is_empty() {
1900-
self.report_private_fields(adt_ty, span, private_fields, ast_fields);
1900+
self.report_private_fields(adt_ty, span, expr.span, private_fields, ast_fields);
19011901
} else {
19021902
self.report_missing_fields(
19031903
adt_ty,
@@ -2056,6 +2056,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
20562056
&self,
20572057
adt_ty: Ty<'tcx>,
20582058
span: Span,
2059+
expr_span: Span,
20592060
private_fields: Vec<&ty::FieldDef>,
20602061
used_fields: &'tcx [hir::ExprField<'tcx>],
20612062
) {
@@ -2100,6 +2101,81 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
21002101
were = pluralize!("was", remaining_private_fields_len),
21012102
));
21022103
}
2104+
2105+
if let ty::Adt(def, _) = adt_ty.kind() {
2106+
let def_id = def.did();
2107+
let mut items = self
2108+
.tcx
2109+
.inherent_impls(def_id)
2110+
.iter()
2111+
.flat_map(|i| self.tcx.associated_items(i).in_definition_order())
2112+
// Only assoc fn with no receivers.
2113+
.filter(|item| {
2114+
matches!(item.kind, ty::AssocKind::Fn) && !item.fn_has_self_parameter
2115+
})
2116+
.filter_map(|item| {
2117+
// Only assoc fns that return `Self`
2118+
let fn_sig = self.tcx.fn_sig(item.def_id).skip_binder();
2119+
let ret_ty = fn_sig.output();
2120+
let ret_ty =
2121+
self.tcx.normalize_erasing_late_bound_regions(self.param_env, ret_ty);
2122+
if !self.can_eq(self.param_env, ret_ty, adt_ty) {
2123+
return None;
2124+
}
2125+
let input_len = fn_sig.inputs().skip_binder().len();
2126+
let order = !item.name.as_str().starts_with("new");
2127+
Some((order, item.name, input_len))
2128+
})
2129+
.collect::<Vec<_>>();
2130+
items.sort_by_key(|(order, _, _)| *order);
2131+
let suggestion = |name, args| {
2132+
format!(
2133+
"::{name}({})",
2134+
std::iter::repeat("_").take(args).collect::<Vec<_>>().join(", ")
2135+
)
2136+
};
2137+
match &items[..] {
2138+
[] => {}
2139+
[(_, name, args)] => {
2140+
err.span_suggestion_verbose(
2141+
span.shrink_to_hi().with_hi(expr_span.hi()),
2142+
format!("you might have meant to use the `{name}` associated function"),
2143+
suggestion(name, *args),
2144+
Applicability::MaybeIncorrect,
2145+
);
2146+
}
2147+
_ => {
2148+
err.span_suggestions(
2149+
span.shrink_to_hi().with_hi(expr_span.hi()),
2150+
"you might have meant to use an associated function to build this type",
2151+
items
2152+
.iter()
2153+
.map(|(_, name, args)| suggestion(name, *args))
2154+
.collect::<Vec<String>>(),
2155+
Applicability::MaybeIncorrect,
2156+
);
2157+
}
2158+
}
2159+
if let Some(default_trait) = self.tcx.get_diagnostic_item(sym::Default)
2160+
&& self
2161+
.infcx
2162+
.type_implements_trait(default_trait, [adt_ty], self.param_env)
2163+
.may_apply()
2164+
{
2165+
err.multipart_suggestion(
2166+
"consider using the `Default` trait",
2167+
vec![
2168+
(span.shrink_to_lo(), "<".to_string()),
2169+
(
2170+
span.shrink_to_hi().with_hi(expr_span.hi()),
2171+
" as std::default::Default>::default()".to_string(),
2172+
),
2173+
],
2174+
Applicability::MaybeIncorrect,
2175+
);
2176+
}
2177+
}
2178+
21032179
err.emit();
21042180
}
21052181

@@ -2703,7 +2779,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
27032779
self.get_field_candidates_considering_privacy(span, ty, mod_id, id)
27042780
{
27052781
let field_names = found_fields.iter().map(|field| field.name).collect::<Vec<_>>();
2706-
let candidate_fields: Vec<_> = found_fields
2782+
let mut candidate_fields: Vec<_> = found_fields
27072783
.into_iter()
27082784
.filter_map(|candidate_field| {
27092785
self.check_for_nested_field_satisfying(
@@ -2724,6 +2800,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
27242800
.collect::<String>()
27252801
})
27262802
.collect::<Vec<_>>();
2803+
candidate_fields.sort();
27272804

27282805
let len = candidate_fields.len();
27292806
if len > 0 {

compiler/rustc_hir_typeck/src/method/suggest.rs

+7-8
Original file line numberDiff line numberDiff line change
@@ -1426,6 +1426,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
14261426
if !suggs.is_empty()
14271427
&& let Some(span) = sugg_span
14281428
{
1429+
suggs.sort();
14291430
err.span_suggestions(
14301431
span.with_hi(item_name.span.lo()),
14311432
"use fully-qualified syntax to disambiguate",
@@ -2000,8 +2001,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
20002001
self.tcx.get_diagnostic_item(sym::Borrow),
20012002
self.tcx.get_diagnostic_item(sym::BorrowMut),
20022003
];
2003-
let candidate_fields: Vec<_> = fields
2004-
.iter()
2004+
let mut candidate_fields: Vec<_> = fields
2005+
.into_iter()
20052006
.filter_map(|candidate_field| {
20062007
self.check_for_nested_field_satisfying(
20072008
span,
@@ -2035,6 +2036,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
20352036
.join(".")
20362037
})
20372038
.collect();
2039+
candidate_fields.sort();
20382040

20392041
let len = candidate_fields.len();
20402042
if len > 0 {
@@ -2567,13 +2569,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
25672569
self.tcx.item_name(*trait_did),
25682570
)
25692571
});
2572+
let mut sugg: Vec<_> = path_strings.chain(glob_path_strings).collect();
2573+
sugg.sort();
25702574

2571-
err.span_suggestions(
2572-
span,
2573-
msg,
2574-
path_strings.chain(glob_path_strings),
2575-
Applicability::MaybeIncorrect,
2576-
);
2575+
err.span_suggestions(span, msg, sugg, Applicability::MaybeIncorrect);
25772576
}
25782577

25792578
fn suggest_valid_traits(

compiler/rustc_parse/src/validate_attr.rs

+1
Original file line numberDiff line numberDiff line change
@@ -186,6 +186,7 @@ fn emit_malformed_attribute(
186186
msg.push_str(&format!("`{code}`"));
187187
suggestions.push(code);
188188
}
189+
suggestions.sort();
189190
if should_warn(name) {
190191
sess.buffer_lint(&ILL_FORMED_ATTRIBUTE_INPUT, span, ast::CRATE_NODE_ID, msg);
191192
} else {

compiler/rustc_resolve/src/diagnostics.rs

+1
Original file line numberDiff line numberDiff line change
@@ -2608,6 +2608,7 @@ fn show_candidates(
26082608
path_strings.extend(core_path_strings);
26092609
path_strings.dedup_by(|a, b| a.0 == b.0);
26102610
}
2611+
accessible_path_strings.sort();
26112612

26122613
if !accessible_path_strings.is_empty() {
26132614
let (determiner, kind, name, through) =

0 commit comments

Comments
 (0)