Skip to content

Commit 9a66e44

Browse files
committedNov 19, 2023
Auto merge of #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

Lines changed: 7 additions & 9 deletions
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

Lines changed: 5 additions & 1 deletion
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

Lines changed: 79 additions & 2 deletions
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

Lines changed: 7 additions & 8 deletions
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

Lines changed: 1 addition & 0 deletions
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

Lines changed: 1 addition & 0 deletions
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) =

‎compiler/rustc_resolve/src/late/diagnostics.rs

Lines changed: 245 additions & 73 deletions
Large diffs are not rendered by default.

‎src/tools/clippy/clippy_lints/src/booleans.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -424,8 +424,9 @@ impl<'a, 'tcx> NonminimalBoolVisitor<'a, 'tcx> {
424424
improvements.push(suggestion);
425425
}
426426
}
427-
let nonminimal_bool_lint = |suggestions: Vec<_>| {
427+
let nonminimal_bool_lint = |mut suggestions: Vec<_>| {
428428
if self.cx.tcx.lint_level_at_node(NONMINIMAL_BOOL, e.hir_id).0 != Level::Allow {
429+
suggestions.sort();
429430
span_lint_hir_and_then(
430431
self.cx,
431432
NONMINIMAL_BOOL,

‎tests/ui/hygiene/extern-prelude-from-opaque-fail-2018.stderr

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,8 @@ LL | a!();
2525
| ---- in this macro invocation
2626
|
2727
= help: consider importing one of these items:
28-
std::mem
2928
core::mem
29+
std::mem
3030
= note: this error originates in the macro `a` (in Nightly builds, run with -Z macro-backtrace for more info)
3131

3232
error[E0433]: failed to resolve: use of undeclared crate or module `my_core`

‎tests/ui/parser/bad-pointer-type.stderr

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,10 @@ LL | fn foo(_: *()) {
66
|
77
help: add `mut` or `const` here
88
|
9-
LL | fn foo(_: *const ()) {
10-
| +++++
119
LL | fn foo(_: *mut ()) {
1210
| +++
11+
LL | fn foo(_: *const ()) {
12+
| +++++
1313

1414
error: aborting due to previous error
1515

‎tests/ui/parser/double-pointer.stderr

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,10 @@ LL | let dptr: **const i32 = &ptr;
66
|
77
help: add `mut` or `const` here
88
|
9-
LL | let dptr: *const *const i32 = &ptr;
10-
| +++++
119
LL | let dptr: *mut *const i32 = &ptr;
1210
| +++
11+
LL | let dptr: *const *const i32 = &ptr;
12+
| +++++
1313

1414
error: aborting due to previous error
1515

‎tests/ui/privacy/suggest-box-new.fixed

Lines changed: 0 additions & 15 deletions
This file was deleted.

‎tests/ui/privacy/suggest-box-new.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
// run-rustfix
21
#![allow(dead_code)]
32
struct U <T> {
43
wtf: Option<Box<U<T>>>,
@@ -12,4 +11,9 @@ fn main() {
1211
})),
1312
x: ()
1413
};
14+
let _ = std::collections::HashMap();
15+
//~^ ERROR expected function, tuple struct or tuple variant, found struct `std::collections::HashMap`
16+
let _ = std::collections::HashMap {};
17+
//~^ ERROR cannot construct `HashMap<_, _, _>` with struct literal syntax due to private fields
18+
let _ = Box {}; //~ ERROR cannot construct `Box<_, _>` with struct literal syntax due to private fields
1519
}
Lines changed: 85 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,29 @@
1+
error[E0423]: expected function, tuple struct or tuple variant, found struct `std::collections::HashMap`
2+
--> $DIR/suggest-box-new.rs:14:13
3+
|
4+
LL | let _ = std::collections::HashMap();
5+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
6+
--> $SRC_DIR/std/src/collections/hash/map.rs:LL:COL
7+
|
8+
= note: `std::collections::HashMap` defined here
9+
|
10+
help: you might have meant to use an associated function to build this type
11+
|
12+
LL | let _ = std::collections::HashMap::new();
13+
| ~~~~~~~
14+
LL | let _ = std::collections::HashMap::with_capacity(_);
15+
| ~~~~~~~~~~~~~~~~~~
16+
LL | let _ = std::collections::HashMap::with_hasher(_);
17+
| ~~~~~~~~~~~~~~~~
18+
LL | let _ = std::collections::HashMap::with_capacity_and_hasher(_, _);
19+
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
20+
help: consider using the `Default` trait
21+
|
22+
LL | let _ = <std::collections::HashMap as std::default::Default>::default();
23+
| + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
24+
125
error[E0423]: cannot initialize a tuple struct which contains private fields
2-
--> $DIR/suggest-box-new.rs:9:19
26+
--> $DIR/suggest-box-new.rs:8:19
327
|
428
LL | wtf: Some(Box(U {
529
| ^^^
@@ -10,11 +34,67 @@ note: constructor is not visible here due to private fields
1034
= note: private field
1135
|
1236
= note: private field
13-
help: you might have meant to use the `new` associated function
37+
help: you might have meant to use an associated function to build this type
38+
|
39+
LL | wtf: Some(Box::new(_)),
40+
| ~~~~~~~~
41+
LL | wtf: Some(Box::new_uninit()),
42+
| ~~~~~~~~~~~~~~
43+
LL | wtf: Some(Box::new_zeroed()),
44+
| ~~~~~~~~~~~~~~
45+
LL | wtf: Some(Box::new_in(_, _)),
46+
| ~~~~~~~~~~~~~~
47+
and 10 other candidates
48+
help: consider using the `Default` trait
49+
|
50+
LL | wtf: Some(<Box as std::default::Default>::default()),
51+
| + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
52+
53+
error: cannot construct `HashMap<_, _, _>` with struct literal syntax due to private fields
54+
--> $DIR/suggest-box-new.rs:16:13
55+
|
56+
LL | let _ = std::collections::HashMap {};
57+
| ^^^^^^^^^^^^^^^^^^^^^^^^^
58+
|
59+
= note: ... and other private field `base` that was not provided
60+
help: you might have meant to use an associated function to build this type
61+
|
62+
LL | let _ = std::collections::HashMap::new();
63+
| ~~~~~~~
64+
LL | let _ = std::collections::HashMap::with_capacity(_);
65+
| ~~~~~~~~~~~~~~~~~~
66+
LL | let _ = std::collections::HashMap::with_hasher(_);
67+
| ~~~~~~~~~~~~~~~~
68+
LL | let _ = std::collections::HashMap::with_capacity_and_hasher(_, _);
69+
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
70+
help: consider using the `Default` trait
71+
|
72+
LL | let _ = <std::collections::HashMap as std::default::Default>::default();
73+
| + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
74+
75+
error: cannot construct `Box<_, _>` with struct literal syntax due to private fields
76+
--> $DIR/suggest-box-new.rs:18:13
77+
|
78+
LL | let _ = Box {};
79+
| ^^^
80+
|
81+
= note: ... and other private fields `0` and `1` that were not provided
82+
help: you might have meant to use an associated function to build this type
83+
|
84+
LL | let _ = Box::new(_);
85+
| ~~~~~~~~
86+
LL | let _ = Box::new_uninit();
87+
| ~~~~~~~~~~~~~~
88+
LL | let _ = Box::new_zeroed();
89+
| ~~~~~~~~~~~~~~
90+
LL | let _ = Box::new_in(_, _);
91+
| ~~~~~~~~~~~~~~
92+
and 10 other candidates
93+
help: consider using the `Default` trait
1494
|
15-
LL | wtf: Some(Box::new(U {
16-
| +++++
95+
LL | let _ = <Box as std::default::Default>::default();
96+
| + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1797

18-
error: aborting due to previous error
98+
error: aborting due to 4 previous errors
1999

20100
For more information about this error, try `rustc --explain E0423`.

‎tests/ui/suggestions/suggest-tryinto-edition-change.stderr

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,8 @@ error[E0433]: failed to resolve: use of undeclared type `TryFrom`
44
LL | let _i: i16 = TryFrom::try_from(0_i32).unwrap();
55
| ^^^^^^^ use of undeclared type `TryFrom`
66
|
7-
= note: 'std::convert::TryFrom' is included in the prelude starting in Edition 2021
87
= note: 'core::convert::TryFrom' is included in the prelude starting in Edition 2021
8+
= note: 'std::convert::TryFrom' is included in the prelude starting in Edition 2021
99
help: consider importing one of these items
1010
|
1111
LL + use core::convert::TryFrom;
@@ -19,8 +19,8 @@ error[E0433]: failed to resolve: use of undeclared type `TryInto`
1919
LL | let _i: i16 = TryInto::try_into(0_i32).unwrap();
2020
| ^^^^^^^ use of undeclared type `TryInto`
2121
|
22-
= note: 'std::convert::TryInto' is included in the prelude starting in Edition 2021
2322
= note: 'core::convert::TryInto' is included in the prelude starting in Edition 2021
23+
= note: 'std::convert::TryInto' is included in the prelude starting in Edition 2021
2424
help: consider importing one of these items
2525
|
2626
LL + use core::convert::TryInto;
@@ -34,8 +34,8 @@ error[E0433]: failed to resolve: use of undeclared type `FromIterator`
3434
LL | let _v: Vec<_> = FromIterator::from_iter(&[1]);
3535
| ^^^^^^^^^^^^ use of undeclared type `FromIterator`
3636
|
37-
= note: 'std::iter::FromIterator' is included in the prelude starting in Edition 2021
3837
= note: 'core::iter::FromIterator' is included in the prelude starting in Edition 2021
38+
= note: 'std::iter::FromIterator' is included in the prelude starting in Edition 2021
3939
help: a trait with a similar name exists
4040
|
4141
LL | let _v: Vec<_> = IntoIterator::from_iter(&[1]);

‎tests/ui/xcrate/auxiliary/xcrate_unit_struct.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,11 @@ pub struct TupleStruct(pub usize, pub &'static str);
1818

1919
#[derive(Copy, Clone)]
2020
pub struct StructWithFields {
21+
pub foo: isize,
22+
}
23+
24+
#[derive(Copy, Clone)]
25+
pub struct StructWithPrivFields {
2126
foo: isize,
2227
}
2328

‎tests/ui/xcrate/xcrate-unit-struct.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,5 +8,7 @@ extern crate xcrate_unit_struct;
88
fn main() {
99
let _ = xcrate_unit_struct::StructWithFields;
1010
//~^ ERROR expected value, found struct `xcrate_unit_struct::StructWithFields`
11+
let _ = xcrate_unit_struct::StructWithPrivFields;
12+
//~^ ERROR expected value, found struct `xcrate_unit_struct::StructWithPrivFields`
1113
let _ = xcrate_unit_struct::Struct;
1214
}

‎tests/ui/xcrate/xcrate-unit-struct.stderr

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,17 @@ LL | let _ = xcrate_unit_struct::StructWithFields;
99
LL | pub struct StructWithFields {
1010
| --------------------------- `xcrate_unit_struct::StructWithFields` defined here
1111

12-
error: aborting due to previous error
12+
error[E0423]: expected value, found struct `xcrate_unit_struct::StructWithPrivFields`
13+
--> $DIR/xcrate-unit-struct.rs:11:13
14+
|
15+
LL | let _ = xcrate_unit_struct::StructWithPrivFields;
16+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
17+
|
18+
::: $DIR/auxiliary/xcrate_unit_struct.rs:25:1
19+
|
20+
LL | pub struct StructWithPrivFields {
21+
| ------------------------------- `xcrate_unit_struct::StructWithPrivFields` defined here
22+
23+
error: aborting due to 2 previous errors
1324

1425
For more information about this error, try `rustc --explain E0423`.

0 commit comments

Comments
 (0)
Please sign in to comment.