Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Suggest associated const on possible capitalization mistake #105843

Merged
merged 1 commit into from
Dec 22, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 20 additions & 5 deletions compiler/rustc_hir_typeck/src/demand.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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);
Expand Down
10 changes: 4 additions & 6 deletions compiler/rustc_hir_typeck/src/expr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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();
}
Expand Down
84 changes: 82 additions & 2 deletions compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -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 _;
Expand Down Expand Up @@ -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(..), .. }))
Expand Down
25 changes: 25 additions & 0 deletions src/test/ui/suggestions/assoc-ct-for-assoc-method.rs
Original file line number Diff line number Diff line change
@@ -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 changes: 47 additions & 0 deletions src/test/ui/suggestions/assoc-ct-for-assoc-method.stderr
Original file line number Diff line number Diff line change
@@ -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`.