about summary refs log tree commit diff
diff options
context:
space:
mode:
authorMatthias Krüger <matthias.krueger@famsik.de>2022-12-22 11:03:50 +0100
committerGitHub <noreply@github.com>2022-12-22 11:03:50 +0100
commitecdabb490cc71960dca3ab893e20825c7f2247ec (patch)
treea57525e9b0955dc4375b5c37593a09d36681204e
parent2cace6aa5d1db3e95d1d1fe8c2c9c432e8ae7c3d (diff)
parent20052c83c037ee76dfc0e95f5c42c929250e6b71 (diff)
downloadrust-ecdabb490cc71960dca3ab893e20825c7f2247ec.tar.gz
rust-ecdabb490cc71960dca3ab893e20825c7f2247ec.zip
Rollup merge of #105843 - compiler-errors:sugg-const, r=lcnr
Suggest associated const on possible capitalization mistake

Suggest `i32::MAX` if we typed `i32::max` without making a function call.

Fixes #93844
-rw-r--r--compiler/rustc_hir_typeck/src/demand.rs25
-rw-r--r--compiler/rustc_hir_typeck/src/expr.rs10
-rw-r--r--compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs84
-rw-r--r--src/test/ui/suggestions/assoc-ct-for-assoc-method.rs25
-rw-r--r--src/test/ui/suggestions/assoc-ct-for-assoc-method.stderr47
5 files changed, 178 insertions, 13 deletions
diff --git a/compiler/rustc_hir_typeck/src/demand.rs b/compiler/rustc_hir_typeck/src/demand.rs
index 479aaf2e1a7..e68bd1297c8 100644
--- a/compiler/rustc_hir_typeck/src/demand.rs
+++ b/compiler/rustc_hir_typeck/src/demand.rs
@@ -23,24 +23,24 @@ use std::cmp::min;
 use std::iter;
 
 impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
-    pub fn emit_coerce_suggestions(
+    pub fn emit_type_mismatch_suggestions(
         &self,
         err: &mut Diagnostic,
         expr: &hir::Expr<'tcx>,
         expr_ty: Ty<'tcx>,
         expected: Ty<'tcx>,
         expected_ty_expr: Option<&'tcx hir::Expr<'tcx>>,
-        error: Option<TypeError<'tcx>>,
+        _error: Option<TypeError<'tcx>>,
     ) {
         if expr_ty == expected {
             return;
         }
 
-        self.annotate_expected_due_to_let_ty(err, expr, error);
-
         // Use `||` to give these suggestions a precedence
         let _ = self.suggest_missing_parentheses(err, expr)
+            || self.suggest_associated_const(err, expr, expected)
             || self.suggest_deref_ref_or_into(err, expr, expected, expr_ty, expected_ty_expr)
+            || self.suggest_option_to_bool(err, expr, expr_ty, expected)
             || self.suggest_compatible_variants(err, expr, expected, expr_ty)
             || self.suggest_non_zero_new_unwrap(err, expr, expected, expr_ty)
             || self.suggest_calling_boxed_future_when_appropriate(err, expr, expected, expr_ty)
@@ -49,9 +49,24 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             || self.suggest_block_to_brackets_peeling_refs(err, expr, expr_ty, expected)
             || self.suggest_copied_or_cloned(err, expr, expr_ty, expected)
             || self.suggest_into(err, expr, expr_ty, expected)
-            || self.suggest_option_to_bool(err, expr, expr_ty, expected)
             || self.suggest_floating_point_literal(err, expr, expected);
+    }
 
+    pub fn emit_coerce_suggestions(
+        &self,
+        err: &mut Diagnostic,
+        expr: &hir::Expr<'tcx>,
+        expr_ty: Ty<'tcx>,
+        expected: Ty<'tcx>,
+        expected_ty_expr: Option<&'tcx hir::Expr<'tcx>>,
+        error: Option<TypeError<'tcx>>,
+    ) {
+        if expr_ty == expected {
+            return;
+        }
+
+        self.annotate_expected_due_to_let_ty(err, expr, error);
+        self.emit_type_mismatch_suggestions(err, expr, expr_ty, expected, expected_ty_expr, error);
         self.note_type_is_not_clone(err, expected, expr_ty, expr);
         self.note_need_for_fn_pointer(err, expected, expr_ty);
         self.note_internal_mutation_in_method(err, expr, expected, expr_ty);
diff --git a/compiler/rustc_hir_typeck/src/expr.rs b/compiler/rustc_hir_typeck/src/expr.rs
index edbbb7272ac..ae641b26eee 100644
--- a/compiler/rustc_hir_typeck/src/expr.rs
+++ b/compiler/rustc_hir_typeck/src/expr.rs
@@ -104,16 +104,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         }
 
         if let Some(mut err) = self.demand_suptype_diag(expr.span, expected_ty, ty) {
-            // FIXME(compiler-errors): We probably should fold some of the
-            // `suggest_` functions from  `emit_coerce_suggestions` into here,
-            // since some of those aren't necessarily just coerce suggestions.
-            let _ = self.suggest_deref_ref_or_into(
+            let _ = self.emit_type_mismatch_suggestions(
                 &mut err,
                 expr.peel_drop_temps(),
-                expected_ty,
                 ty,
+                expected_ty,
                 None,
-            ) || self.suggest_option_to_bool(&mut err, expr, ty, expected_ty);
+                None,
+            );
             extend_err(&mut err);
             err.emit();
         }
diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs
index c9d179de39f..efec0244633 100644
--- a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs
+++ b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs
@@ -1,6 +1,7 @@
 use super::FnCtxt;
 
 use crate::errors::{AddReturnTypeSuggestion, ExpectedReturnTypeLabel};
+use crate::method::probe::{IsSuggestion, Mode, ProbeScope};
 use rustc_ast::util::parser::{ExprPrecedence, PREC_POSTFIX};
 use rustc_errors::{Applicability, Diagnostic, MultiSpan};
 use rustc_hir as hir;
@@ -15,10 +16,11 @@ use rustc_infer::traits::{self, StatementAsExpression};
 use rustc_middle::lint::in_external_macro;
 use rustc_middle::ty::{
     self, suggest_constraining_type_params, Binder, DefIdTree, IsSuggestable, ToPredicate, Ty,
+    TypeVisitable,
 };
 use rustc_session::errors::ExprParenthesesNeeded;
-use rustc_span::symbol::sym;
-use rustc_span::Span;
+use rustc_span::symbol::{sym, Ident};
+use rustc_span::{Span, Symbol};
 use rustc_trait_selection::infer::InferCtxtExt;
 use rustc_trait_selection::traits::error_reporting::DefIdOrName;
 use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt as _;
@@ -1236,6 +1238,84 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         }
     }
 
+    pub(crate) fn suggest_associated_const(
+        &self,
+        err: &mut Diagnostic,
+        expr: &hir::Expr<'_>,
+        expected_ty: Ty<'tcx>,
+    ) -> bool {
+        let Some((DefKind::AssocFn, old_def_id)) = self.typeck_results.borrow().type_dependent_def(expr.hir_id) else {
+            return false;
+        };
+        let old_item_name = self.tcx.item_name(old_def_id);
+        let capitalized_name = Symbol::intern(&old_item_name.as_str().to_uppercase());
+        if old_item_name == capitalized_name {
+            return false;
+        }
+        let (item, segment) = match expr.kind {
+            hir::ExprKind::Path(QPath::Resolved(
+                Some(ty),
+                hir::Path { segments: [segment], .. },
+            ))
+            | hir::ExprKind::Path(QPath::TypeRelative(ty, segment)) => {
+                let self_ty = <dyn AstConv<'_>>::ast_ty_to_ty(self, ty);
+                if let Ok(pick) = self.probe_for_name(
+                    Mode::Path,
+                    Ident::new(capitalized_name, segment.ident.span),
+                    IsSuggestion(true),
+                    self_ty,
+                    expr.hir_id,
+                    ProbeScope::TraitsInScope,
+                ) {
+                    (pick.item, segment)
+                } else {
+                    return false;
+                }
+            }
+            hir::ExprKind::Path(QPath::Resolved(
+                None,
+                hir::Path { segments: [.., segment], .. },
+            )) => {
+                // we resolved through some path that doesn't end in the item name,
+                // better not do a bad suggestion by accident.
+                if old_item_name != segment.ident.name {
+                    return false;
+                }
+                if let Some(item) = self
+                    .tcx
+                    .associated_items(self.tcx.parent(old_def_id))
+                    .filter_by_name_unhygienic(capitalized_name)
+                    .next()
+                {
+                    (*item, segment)
+                } else {
+                    return false;
+                }
+            }
+            _ => return false,
+        };
+        if item.def_id == old_def_id || self.tcx.def_kind(item.def_id) != DefKind::AssocConst {
+            // Same item
+            return false;
+        }
+        let item_ty = self.tcx.type_of(item.def_id);
+        // FIXME(compiler-errors): This check is *so* rudimentary
+        if item_ty.needs_subst() {
+            return false;
+        }
+        if self.can_coerce(item_ty, expected_ty) {
+            err.span_suggestion_verbose(
+                segment.ident.span,
+                format!("try referring to the associated const `{capitalized_name}` instead",),
+                capitalized_name,
+                Applicability::MachineApplicable,
+            );
+            true
+        } else {
+            false
+        }
+    }
+
     fn is_loop(&self, id: hir::HirId) -> bool {
         let node = self.tcx.hir().get(id);
         matches!(node, Node::Expr(Expr { kind: ExprKind::Loop(..), .. }))
diff --git a/src/test/ui/suggestions/assoc-ct-for-assoc-method.rs b/src/test/ui/suggestions/assoc-ct-for-assoc-method.rs
new file mode 100644
index 00000000000..fe222776989
--- /dev/null
+++ b/src/test/ui/suggestions/assoc-ct-for-assoc-method.rs
@@ -0,0 +1,25 @@
+struct MyS;
+
+impl MyS {
+    const FOO: i32 = 1;
+    fn foo() -> MyS {
+        MyS
+    }
+}
+
+fn main() {
+    let x: i32 = MyS::foo;
+    //~^ ERROR mismatched types
+    //~| HELP try referring to the
+
+    let z: i32 = i32::max;
+    //~^ ERROR mismatched types
+    //~| HELP try referring to the
+
+    // This example is still broken though... This is a hard suggestion to make,
+    // because we don't have access to the associated const probing code to make
+    // this suggestion where it's emitted, i.e. in trait selection.
+    let y: i32 = i32::max - 42;
+    //~^ ERROR cannot subtract
+    //~| HELP use parentheses
+}
diff --git a/src/test/ui/suggestions/assoc-ct-for-assoc-method.stderr b/src/test/ui/suggestions/assoc-ct-for-assoc-method.stderr
new file mode 100644
index 00000000000..afef38f1296
--- /dev/null
+++ b/src/test/ui/suggestions/assoc-ct-for-assoc-method.stderr
@@ -0,0 +1,47 @@
+error[E0308]: mismatched types
+  --> $DIR/assoc-ct-for-assoc-method.rs:11:18
+   |
+LL |     let x: i32 = MyS::foo;
+   |            ---   ^^^^^^^^ expected `i32`, found fn item
+   |            |
+   |            expected due to this
+   |
+   = note: expected type `i32`
+           found fn item `fn() -> MyS {MyS::foo}`
+help: try referring to the associated const `FOO` instead
+   |
+LL |     let x: i32 = MyS::FOO;
+   |                       ~~~
+
+error[E0308]: mismatched types
+  --> $DIR/assoc-ct-for-assoc-method.rs:15:18
+   |
+LL |     let z: i32 = i32::max;
+   |            ---   ^^^^^^^^ expected `i32`, found fn item
+   |            |
+   |            expected due to this
+   |
+   = note: expected type `i32`
+           found fn item `fn(i32, i32) -> i32 {<i32 as Ord>::max}`
+help: try referring to the associated const `MAX` instead
+   |
+LL |     let z: i32 = i32::MAX;
+   |                       ~~~
+
+error[E0369]: cannot subtract `{integer}` from `fn(i32, i32) -> i32 {<i32 as Ord>::max}`
+  --> $DIR/assoc-ct-for-assoc-method.rs:22:27
+   |
+LL |     let y: i32 = i32::max - 42;
+   |                  -------- ^ -- {integer}
+   |                  |
+   |                  fn(i32, i32) -> i32 {<i32 as Ord>::max}
+   |
+help: use parentheses to call this associated function
+   |
+LL |     let y: i32 = i32::max(/* i32 */, /* i32 */) - 42;
+   |                          ++++++++++++++++++++++
+
+error: aborting due to 3 previous errors
+
+Some errors have detailed explanations: E0308, E0369.
+For more information about an error, try `rustc --explain E0308`.