Suggest associated const on capitalization error
This commit is contained in:
parent
bdbe392a13
commit
20052c83c0
5 changed files with 188 additions and 23 deletions
|
@ -23,6 +23,35 @@ use std::cmp::min;
|
||||||
use std::iter;
|
use std::iter;
|
||||||
|
|
||||||
impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||||
|
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>>,
|
||||||
|
) {
|
||||||
|
if expr_ty == expected {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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)
|
||||||
|
|| self.suggest_no_capture_closure(err, expected, expr_ty)
|
||||||
|
|| self.suggest_boxing_when_appropriate(err, expr, expected, expr_ty)
|
||||||
|
|| 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_floating_point_literal(err, expr, expected);
|
||||||
|
}
|
||||||
|
|
||||||
pub fn emit_coerce_suggestions(
|
pub fn emit_coerce_suggestions(
|
||||||
&self,
|
&self,
|
||||||
err: &mut Diagnostic,
|
err: &mut Diagnostic,
|
||||||
|
@ -37,21 +66,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||||
}
|
}
|
||||||
|
|
||||||
self.annotate_expected_due_to_let_ty(err, expr, error);
|
self.annotate_expected_due_to_let_ty(err, expr, error);
|
||||||
|
self.emit_type_mismatch_suggestions(err, expr, expr_ty, expected, expected_ty_expr, error);
|
||||||
// Use `||` to give these suggestions a precedence
|
|
||||||
let _ = self.suggest_missing_parentheses(err, expr)
|
|
||||||
|| self.suggest_deref_ref_or_into(err, expr, expected, expr_ty, expected_ty_expr)
|
|
||||||
|| 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)
|
|
||||||
|| self.suggest_no_capture_closure(err, expected, expr_ty)
|
|
||||||
|| self.suggest_boxing_when_appropriate(err, expr, expected, expr_ty)
|
|
||||||
|| 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);
|
|
||||||
|
|
||||||
self.note_type_is_not_clone(err, expected, expr_ty, expr);
|
self.note_type_is_not_clone(err, expected, expr_ty, expr);
|
||||||
self.note_need_for_fn_pointer(err, expected, expr_ty);
|
self.note_need_for_fn_pointer(err, expected, expr_ty);
|
||||||
self.note_internal_mutation_in_method(err, expr, expected, expr_ty);
|
self.note_internal_mutation_in_method(err, expr, expected, expr_ty);
|
||||||
|
|
|
@ -104,16 +104,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(mut err) = self.demand_suptype_diag(expr.span, expected_ty, ty) {
|
if let Some(mut err) = self.demand_suptype_diag(expr.span, expected_ty, ty) {
|
||||||
// FIXME(compiler-errors): We probably should fold some of the
|
let _ = self.emit_type_mismatch_suggestions(
|
||||||
// `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(
|
|
||||||
&mut err,
|
&mut err,
|
||||||
expr.peel_drop_temps(),
|
expr.peel_drop_temps(),
|
||||||
expected_ty,
|
|
||||||
ty,
|
ty,
|
||||||
|
expected_ty,
|
||||||
None,
|
None,
|
||||||
) || self.suggest_option_to_bool(&mut err, expr, ty, expected_ty);
|
None,
|
||||||
|
);
|
||||||
extend_err(&mut err);
|
extend_err(&mut err);
|
||||||
err.emit();
|
err.emit();
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
use super::FnCtxt;
|
use super::FnCtxt;
|
||||||
|
|
||||||
use crate::errors::{AddReturnTypeSuggestion, ExpectedReturnTypeLabel};
|
use crate::errors::{AddReturnTypeSuggestion, ExpectedReturnTypeLabel};
|
||||||
|
use crate::method::probe::{IsSuggestion, Mode, ProbeScope};
|
||||||
use rustc_ast::util::parser::{ExprPrecedence, PREC_POSTFIX};
|
use rustc_ast::util::parser::{ExprPrecedence, PREC_POSTFIX};
|
||||||
use rustc_errors::{Applicability, Diagnostic, MultiSpan};
|
use rustc_errors::{Applicability, Diagnostic, MultiSpan};
|
||||||
use rustc_hir as hir;
|
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::lint::in_external_macro;
|
||||||
use rustc_middle::ty::{
|
use rustc_middle::ty::{
|
||||||
self, suggest_constraining_type_params, Binder, DefIdTree, IsSuggestable, ToPredicate, Ty,
|
self, suggest_constraining_type_params, Binder, DefIdTree, IsSuggestable, ToPredicate, Ty,
|
||||||
|
TypeVisitable,
|
||||||
};
|
};
|
||||||
use rustc_session::errors::ExprParenthesesNeeded;
|
use rustc_session::errors::ExprParenthesesNeeded;
|
||||||
use rustc_span::symbol::sym;
|
use rustc_span::symbol::{sym, Ident};
|
||||||
use rustc_span::Span;
|
use rustc_span::{Span, Symbol};
|
||||||
use rustc_trait_selection::infer::InferCtxtExt;
|
use rustc_trait_selection::infer::InferCtxtExt;
|
||||||
use rustc_trait_selection::traits::error_reporting::DefIdOrName;
|
use rustc_trait_selection::traits::error_reporting::DefIdOrName;
|
||||||
use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt as _;
|
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 {
|
fn is_loop(&self, id: hir::HirId) -> bool {
|
||||||
let node = self.tcx.hir().get(id);
|
let node = self.tcx.hir().get(id);
|
||||||
matches!(node, Node::Expr(Expr { kind: ExprKind::Loop(..), .. }))
|
matches!(node, Node::Expr(Expr { kind: ExprKind::Loop(..), .. }))
|
||||||
|
|
25
src/test/ui/suggestions/assoc-ct-for-assoc-method.rs
Normal file
25
src/test/ui/suggestions/assoc-ct-for-assoc-method.rs
Normal file
|
@ -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
|
||||||
|
}
|
47
src/test/ui/suggestions/assoc-ct-for-assoc-method.stderr
Normal file
47
src/test/ui/suggestions/assoc-ct-for-assoc-method.stderr
Normal file
|
@ -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`.
|
Loading…
Add table
Add a link
Reference in a new issue