Skip to content

Commit be0958f

Browse files
committed
Suggest builder functions on struct literal with private fields
1 parent 1dfec45 commit be0958f

File tree

3 files changed

+109
-2
lines changed

3 files changed

+109
-2
lines changed

compiler/rustc_hir_typeck/src/expr.rs

+68-1
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,72 @@ 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 = self.tcx.erase_late_bound_regions(ret_ty);
2121+
if !self.can_eq(self.param_env, ret_ty, adt_ty) {
2122+
return None;
2123+
}
2124+
// Check for `-> Self`
2125+
let input_len = fn_sig.inputs().skip_binder().len();
2126+
if def.did() == def_id {
2127+
let order = if item.name.as_str().starts_with("new") { 0 } else { 1 };
2128+
Some((order, item.name, input_len))
2129+
} else {
2130+
None
2131+
}
2132+
})
2133+
.collect::<Vec<_>>();
2134+
items.sort_by_key(|(order, _, _)| *order);
2135+
match &items[..] {
2136+
[] => {}
2137+
[(_, name, args)] => {
2138+
err.span_suggestion_verbose(
2139+
span.shrink_to_hi().with_hi(expr_span.hi()),
2140+
format!("you might have meant to use the `{name}` associated function",),
2141+
format!(
2142+
"::{name}({})",
2143+
std::iter::repeat("_").take(*args).collect::<Vec<_>>().join(", ")
2144+
),
2145+
Applicability::MaybeIncorrect,
2146+
);
2147+
}
2148+
_ => {
2149+
err.span_suggestions(
2150+
span.shrink_to_hi().with_hi(expr_span.hi()),
2151+
"you might have meant to use an associated function to build this type",
2152+
items
2153+
.iter()
2154+
.map(|(_, name, args)| {
2155+
format!(
2156+
"::{name}({})",
2157+
std::iter::repeat("_")
2158+
.take(*args)
2159+
.collect::<Vec<_>>()
2160+
.join(", ")
2161+
)
2162+
})
2163+
.collect::<Vec<String>>(),
2164+
Applicability::MaybeIncorrect,
2165+
);
2166+
}
2167+
}
2168+
}
2169+
21032170
err.emit();
21042171
}
21052172

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

+3
Original file line numberDiff line numberDiff line change
@@ -13,4 +13,7 @@ fn main() {
1313
};
1414
let _ = std::collections::HashMap();
1515
//~^ 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
1619
}

tests/ui/privacy/suggest-box-new.stderr

+38-1
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,43 @@ help: consider using the `Default` trait
5050
LL | wtf: Some(<Box as std::default::Default>::default()),
5151
| + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5252

53-
error: aborting due to 2 previous errors
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+
71+
error: cannot construct `Box<_, _>` with struct literal syntax due to private fields
72+
--> $DIR/suggest-box-new.rs:18:13
73+
|
74+
LL | let _ = Box {};
75+
| ^^^
76+
|
77+
= note: ... and other private fields `0` and `1` that were not provided
78+
help: you might have meant to use an associated function to build this type
79+
|
80+
LL | let _ = Box::new(_);
81+
| ~~~~~~~~
82+
LL | let _ = Box::new_uninit();
83+
| ~~~~~~~~~~~~~~
84+
LL | let _ = Box::new_zeroed();
85+
| ~~~~~~~~~~~~~~
86+
LL | let _ = Box::new_in(_, _);
87+
| ~~~~~~~~~~~~~~
88+
and 10 other candidates
89+
90+
error: aborting due to 4 previous errors
5491

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

0 commit comments

Comments
 (0)