about summary refs log tree commit diff
diff options
context:
space:
mode:
authorEsteban Küber <esteban@kuber.com.ar>2023-11-08 23:45:09 +0000
committerEsteban Küber <esteban@kuber.com.ar>2023-11-19 17:50:47 +0000
commitbe0958f5ab7b7d24cd24e2796d21572419d2cd53 (patch)
tree3122f164fbd5d14b0b454dd2868b2f04aca33774
parent1dfec45dc99d7d699bdf470d042300102e1589bc (diff)
downloadrust-be0958f5ab7b7d24cd24e2796d21572419d2cd53.tar.gz
rust-be0958f5ab7b7d24cd24e2796d21572419d2cd53.zip
Suggest builder functions on struct literal with private fields
-rw-r--r--compiler/rustc_hir_typeck/src/expr.rs69
-rw-r--r--tests/ui/privacy/suggest-box-new.rs3
-rw-r--r--tests/ui/privacy/suggest-box-new.stderr39
3 files changed, 109 insertions, 2 deletions
diff --git a/compiler/rustc_hir_typeck/src/expr.rs b/compiler/rustc_hir_typeck/src/expr.rs
index 9180d4aad32..9f27a6e5d84 100644
--- a/compiler/rustc_hir_typeck/src/expr.rs
+++ b/compiler/rustc_hir_typeck/src/expr.rs
@@ -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,
@@ -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>],
     ) {
@@ -2100,6 +2101,72 @@ 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.erase_late_bound_regions(ret_ty);
+                    if !self.can_eq(self.param_env, ret_ty, adt_ty) {
+                        return None;
+                    }
+                    // Check for `-> Self`
+                    let input_len = fn_sig.inputs().skip_binder().len();
+                    if def.did() == def_id {
+                        let order = if item.name.as_str().starts_with("new") { 0 } else { 1 };
+                        Some((order, item.name, input_len))
+                    } else {
+                        None
+                    }
+                })
+                .collect::<Vec<_>>();
+            items.sort_by_key(|(order, _, _)| *order);
+            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",),
+                        format!(
+                            "::{name}({})",
+                            std::iter::repeat("_").take(*args).collect::<Vec<_>>().join(", ")
+                        ),
+                        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)| {
+                                format!(
+                                    "::{name}({})",
+                                    std::iter::repeat("_")
+                                        .take(*args)
+                                        .collect::<Vec<_>>()
+                                        .join(", ")
+                                )
+                            })
+                            .collect::<Vec<String>>(),
+                        Applicability::MaybeIncorrect,
+                    );
+                }
+            }
+        }
+
         err.emit();
     }
 
diff --git a/tests/ui/privacy/suggest-box-new.rs b/tests/ui/privacy/suggest-box-new.rs
index 657dd37dc68..7125285fc77 100644
--- a/tests/ui/privacy/suggest-box-new.rs
+++ b/tests/ui/privacy/suggest-box-new.rs
@@ -13,4 +13,7 @@ fn main() {
     };
     let _ = std::collections::HashMap();
     //~^ ERROR expected function, tuple struct or tuple variant, found struct `std::collections::HashMap`
+    let _ = std::collections::HashMap {};
+    //~^ ERROR cannot construct `HashMap<_, _, _>` with struct literal syntax due to private fields
+    let _ = Box {}; //~ ERROR cannot construct `Box<_, _>` with struct literal syntax due to private fields
 }
diff --git a/tests/ui/privacy/suggest-box-new.stderr b/tests/ui/privacy/suggest-box-new.stderr
index f58f661a468..79629dab6be 100644
--- a/tests/ui/privacy/suggest-box-new.stderr
+++ b/tests/ui/privacy/suggest-box-new.stderr
@@ -50,6 +50,43 @@ help: consider using the `Default` trait
 LL |         wtf: Some(<Box as std::default::Default>::default()),
    |                   +    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
-error: aborting due to 2 previous errors
+error: cannot construct `HashMap<_, _, _>` with struct literal syntax due to private fields
+  --> $DIR/suggest-box-new.rs:16:13
+   |
+LL |     let _ = std::collections::HashMap {};
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: ... and other private field `base` that was not provided
+help: you might have meant to use an associated function to build this type
+   |
+LL |     let _ = std::collections::HashMap::new();
+   |                                      ~~~~~~~
+LL |     let _ = std::collections::HashMap::with_capacity(_);
+   |                                      ~~~~~~~~~~~~~~~~~~
+LL |     let _ = std::collections::HashMap::with_hasher(_);
+   |                                      ~~~~~~~~~~~~~~~~
+LL |     let _ = std::collections::HashMap::with_capacity_and_hasher(_, _);
+   |                                      ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+error: cannot construct `Box<_, _>` with struct literal syntax due to private fields
+  --> $DIR/suggest-box-new.rs:18:13
+   |
+LL |     let _ = Box {};
+   |             ^^^
+   |
+   = note: ... and other private fields `0` and `1` that were not provided
+help: you might have meant to use an associated function to build this type
+   |
+LL |     let _ = Box::new(_);
+   |                ~~~~~~~~
+LL |     let _ = Box::new_uninit();
+   |                ~~~~~~~~~~~~~~
+LL |     let _ = Box::new_zeroed();
+   |                ~~~~~~~~~~~~~~
+LL |     let _ = Box::new_in(_, _);
+   |                ~~~~~~~~~~~~~~
+     and 10 other candidates
+
+error: aborting due to 4 previous errors
 
 For more information about this error, try `rustc --explain E0423`.