about summary refs log tree commit diff
path: root/compiler/rustc_resolve/src/late
diff options
context:
space:
mode:
authorEsteban Küber <esteban@kuber.com.ar>2023-11-07 19:32:48 +0000
committerEsteban Küber <esteban@kuber.com.ar>2023-11-19 17:47:41 +0000
commit42aa1273b0dd317c4aafdcf649f40cbad4499b60 (patch)
treed5684d2eb75fb342c0c34f7f09e1cc52f2ef0462 /compiler/rustc_resolve/src/late
parent27794f95fdccda6a6ed292f6724b546cff35e13d (diff)
downloadrust-42aa1273b0dd317c4aafdcf649f40cbad4499b60.tar.gz
rust-42aa1273b0dd317c4aafdcf649f40cbad4499b60.zip
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>`.
Diffstat (limited to 'compiler/rustc_resolve/src/late')
-rw-r--r--compiler/rustc_resolve/src/late/diagnostics.rs90
1 files changed, 74 insertions, 16 deletions
diff --git a/compiler/rustc_resolve/src/late/diagnostics.rs b/compiler/rustc_resolve/src/late/diagnostics.rs
index 6aecd3610c6..3b59af89a98 100644
--- a/compiler/rustc_resolve/src/late/diagnostics.rs
+++ b/compiler/rustc_resolve/src/late/diagnostics.rs
@@ -15,7 +15,7 @@ use rustc_ast_pretty::pprust::where_bound_predicate_to_string;
 use rustc_data_structures::fx::FxHashSet;
 use rustc_errors::{
     pluralize, struct_span_err, Applicability, Diagnostic, DiagnosticBuilder, ErrorGuaranteed,
-    MultiSpan,
+    MultiSpan, SuggestionStyle,
 };
 use rustc_hir as hir;
 use rustc_hir::def::{self, CtorKind, CtorOf, DefKind};
@@ -29,6 +29,8 @@ use rustc_span::hygiene::MacroKind;
 use rustc_span::symbol::{kw, sym, Ident, Symbol};
 use rustc_span::Span;
 
+use rustc_middle::ty;
+
 use std::borrow::Cow;
 use std::iter;
 use std::ops::Deref;
@@ -1593,29 +1595,85 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> {
                         Some(Vec::from(pattern_spans))
                     }
                     // e.g. `let _ = Enum::TupleVariant(field1, field2);`
-                    _ if source.is_call() => {
+                    PathSource::Expr(Some(Expr { kind: ExprKind::Call(_, ref args), .. })) => {
                         err.set_primary_message(
                             "cannot initialize a tuple struct which contains private fields",
                         );
-                        if !def_id.is_local()
-                            && self
+                        if !def_id.is_local() {
+                            // Look at all the associated functions without receivers in the type's
+                            // inherent impls to look for builders that return `Self`
+                            let mut items = self
                                 .r
                                 .tcx
                                 .inherent_impls(def_id)
                                 .iter()
-                                .flat_map(|impl_def_id| {
-                                    self.r.tcx.provided_trait_methods(*impl_def_id)
+                                .flat_map(|i| self.r.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
                                 })
-                                .any(|assoc| !assoc.fn_has_self_parameter && assoc.name == sym::new)
-                        {
-                            // FIXME: look for associated functions with Self return type,
-                            // instead of relying only on the name and lack of self receiver.
-                            err.span_suggestion_verbose(
-                                span.shrink_to_hi(),
-                                "you might have meant to use the `new` associated function",
-                                "::new".to_string(),
-                                Applicability::MaybeIncorrect,
-                            );
+                                .filter_map(|item| {
+                                    // Only assoc fns that return `Self`
+                                    let fn_sig = self.r.tcx.fn_sig(item.def_id).skip_binder();
+                                    let ret_ty = fn_sig.output();
+                                    let ret_ty = self.r.tcx.erase_late_bound_regions(ret_ty);
+                                    let ty::Adt(def, _args) = ret_ty.kind() else {
+                                        return None;
+                                    };
+                                    // Check for `-> Self`
+                                    if def.did() == def_id {
+                                        let order = if item.name.as_str().starts_with("new")
+                                            && fn_sig.inputs().skip_binder().len() == args.len()
+                                        {
+                                            0
+                                        } else if item.name.as_str().starts_with("new")
+                                            || item.name.as_str().starts_with("default")
+                                        {
+                                            // Give higher precedence to functions with a name that
+                                            // imply construction.
+                                            1
+                                        } else if fn_sig.inputs().skip_binder().len() == args.len()
+                                        {
+                                            2
+                                        } else {
+                                            3
+                                        };
+                                        return Some((order, item.name));
+                                    }
+                                    None
+                                })
+                                .collect::<Vec<_>>();
+                            items.sort_by_key(|(order, _)| *order);
+                            match &items[..] {
+                                [] => {}
+                                [(_, name)] => {
+                                    err.span_suggestion_verbose(
+                                        span.shrink_to_hi(),
+                                        format!(
+                                            "you might have meant to use the `{name}` associated \
+                                             function",
+                                        ),
+                                        format!("::{name}"),
+                                        Applicability::MaybeIncorrect,
+                                    );
+                                }
+                                _ => {
+                                    // We use this instead of `span_suggestions` to retain output
+                                    // sort order.
+                                    err.span_suggestions_with_style(
+                                        span.shrink_to_hi(),
+                                        "you might have meant to use an associated function to \
+                                         build this type",
+                                        items
+                                            .iter()
+                                            .map(|(_, name)| format!("::{name}"))
+                                            .collect::<Vec<String>>(),
+                                        Applicability::MaybeIncorrect,
+                                        SuggestionStyle::ShowAlways,
+                                    );
+                                }
+                            }
                         }
                         // Use spans of the tuple struct definition.
                         self.r.field_def_ids(def_id).map(|field_ids| {