Skip to content

Commit ecdabb4

Browse files
authored
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
2 parents 2cace6a + 20052c8 commit ecdabb4

File tree

5 files changed

+178
-13
lines changed

5 files changed

+178
-13
lines changed

compiler/rustc_hir_typeck/src/demand.rs

+20-5
Original file line numberDiff line numberDiff line change
@@ -23,24 +23,24 @@ use std::cmp::min;
2323
use std::iter;
2424

2525
impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
26-
pub fn emit_coerce_suggestions(
26+
pub fn emit_type_mismatch_suggestions(
2727
&self,
2828
err: &mut Diagnostic,
2929
expr: &hir::Expr<'tcx>,
3030
expr_ty: Ty<'tcx>,
3131
expected: Ty<'tcx>,
3232
expected_ty_expr: Option<&'tcx hir::Expr<'tcx>>,
33-
error: Option<TypeError<'tcx>>,
33+
_error: Option<TypeError<'tcx>>,
3434
) {
3535
if expr_ty == expected {
3636
return;
3737
}
3838

39-
self.annotate_expected_due_to_let_ty(err, expr, error);
40-
4139
// Use `||` to give these suggestions a precedence
4240
let _ = self.suggest_missing_parentheses(err, expr)
41+
|| self.suggest_associated_const(err, expr, expected)
4342
|| self.suggest_deref_ref_or_into(err, expr, expected, expr_ty, expected_ty_expr)
43+
|| self.suggest_option_to_bool(err, expr, expr_ty, expected)
4444
|| self.suggest_compatible_variants(err, expr, expected, expr_ty)
4545
|| self.suggest_non_zero_new_unwrap(err, expr, expected, expr_ty)
4646
|| self.suggest_calling_boxed_future_when_appropriate(err, expr, expected, expr_ty)
@@ -49,9 +49,24 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
4949
|| self.suggest_block_to_brackets_peeling_refs(err, expr, expr_ty, expected)
5050
|| self.suggest_copied_or_cloned(err, expr, expr_ty, expected)
5151
|| self.suggest_into(err, expr, expr_ty, expected)
52-
|| self.suggest_option_to_bool(err, expr, expr_ty, expected)
5352
|| self.suggest_floating_point_literal(err, expr, expected);
53+
}
5454

55+
pub fn emit_coerce_suggestions(
56+
&self,
57+
err: &mut Diagnostic,
58+
expr: &hir::Expr<'tcx>,
59+
expr_ty: Ty<'tcx>,
60+
expected: Ty<'tcx>,
61+
expected_ty_expr: Option<&'tcx hir::Expr<'tcx>>,
62+
error: Option<TypeError<'tcx>>,
63+
) {
64+
if expr_ty == expected {
65+
return;
66+
}
67+
68+
self.annotate_expected_due_to_let_ty(err, expr, error);
69+
self.emit_type_mismatch_suggestions(err, expr, expr_ty, expected, expected_ty_expr, error);
5570
self.note_type_is_not_clone(err, expected, expr_ty, expr);
5671
self.note_need_for_fn_pointer(err, expected, expr_ty);
5772
self.note_internal_mutation_in_method(err, expr, expected, expr_ty);

compiler/rustc_hir_typeck/src/expr.rs

+4-6
Original file line numberDiff line numberDiff line change
@@ -104,16 +104,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
104104
}
105105

106106
if let Some(mut err) = self.demand_suptype_diag(expr.span, expected_ty, ty) {
107-
// FIXME(compiler-errors): We probably should fold some of the
108-
// `suggest_` functions from `emit_coerce_suggestions` into here,
109-
// since some of those aren't necessarily just coerce suggestions.
110-
let _ = self.suggest_deref_ref_or_into(
107+
let _ = self.emit_type_mismatch_suggestions(
111108
&mut err,
112109
expr.peel_drop_temps(),
113-
expected_ty,
114110
ty,
111+
expected_ty,
115112
None,
116-
) || self.suggest_option_to_bool(&mut err, expr, ty, expected_ty);
113+
None,
114+
);
117115
extend_err(&mut err);
118116
err.emit();
119117
}

compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs

+82-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
use super::FnCtxt;
22

33
use crate::errors::{AddReturnTypeSuggestion, ExpectedReturnTypeLabel};
4+
use crate::method::probe::{IsSuggestion, Mode, ProbeScope};
45
use rustc_ast::util::parser::{ExprPrecedence, PREC_POSTFIX};
56
use rustc_errors::{Applicability, Diagnostic, MultiSpan};
67
use rustc_hir as hir;
@@ -15,10 +16,11 @@ use rustc_infer::traits::{self, StatementAsExpression};
1516
use rustc_middle::lint::in_external_macro;
1617
use rustc_middle::ty::{
1718
self, suggest_constraining_type_params, Binder, DefIdTree, IsSuggestable, ToPredicate, Ty,
19+
TypeVisitable,
1820
};
1921
use rustc_session::errors::ExprParenthesesNeeded;
20-
use rustc_span::symbol::sym;
21-
use rustc_span::Span;
22+
use rustc_span::symbol::{sym, Ident};
23+
use rustc_span::{Span, Symbol};
2224
use rustc_trait_selection::infer::InferCtxtExt;
2325
use rustc_trait_selection::traits::error_reporting::DefIdOrName;
2426
use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt as _;
@@ -1236,6 +1238,84 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
12361238
}
12371239
}
12381240

1241+
pub(crate) fn suggest_associated_const(
1242+
&self,
1243+
err: &mut Diagnostic,
1244+
expr: &hir::Expr<'_>,
1245+
expected_ty: Ty<'tcx>,
1246+
) -> bool {
1247+
let Some((DefKind::AssocFn, old_def_id)) = self.typeck_results.borrow().type_dependent_def(expr.hir_id) else {
1248+
return false;
1249+
};
1250+
let old_item_name = self.tcx.item_name(old_def_id);
1251+
let capitalized_name = Symbol::intern(&old_item_name.as_str().to_uppercase());
1252+
if old_item_name == capitalized_name {
1253+
return false;
1254+
}
1255+
let (item, segment) = match expr.kind {
1256+
hir::ExprKind::Path(QPath::Resolved(
1257+
Some(ty),
1258+
hir::Path { segments: [segment], .. },
1259+
))
1260+
| hir::ExprKind::Path(QPath::TypeRelative(ty, segment)) => {
1261+
let self_ty = <dyn AstConv<'_>>::ast_ty_to_ty(self, ty);
1262+
if let Ok(pick) = self.probe_for_name(
1263+
Mode::Path,
1264+
Ident::new(capitalized_name, segment.ident.span),
1265+
IsSuggestion(true),
1266+
self_ty,
1267+
expr.hir_id,
1268+
ProbeScope::TraitsInScope,
1269+
) {
1270+
(pick.item, segment)
1271+
} else {
1272+
return false;
1273+
}
1274+
}
1275+
hir::ExprKind::Path(QPath::Resolved(
1276+
None,
1277+
hir::Path { segments: [.., segment], .. },
1278+
)) => {
1279+
// we resolved through some path that doesn't end in the item name,
1280+
// better not do a bad suggestion by accident.
1281+
if old_item_name != segment.ident.name {
1282+
return false;
1283+
}
1284+
if let Some(item) = self
1285+
.tcx
1286+
.associated_items(self.tcx.parent(old_def_id))
1287+
.filter_by_name_unhygienic(capitalized_name)
1288+
.next()
1289+
{
1290+
(*item, segment)
1291+
} else {
1292+
return false;
1293+
}
1294+
}
1295+
_ => return false,
1296+
};
1297+
if item.def_id == old_def_id || self.tcx.def_kind(item.def_id) != DefKind::AssocConst {
1298+
// Same item
1299+
return false;
1300+
}
1301+
let item_ty = self.tcx.type_of(item.def_id);
1302+
// FIXME(compiler-errors): This check is *so* rudimentary
1303+
if item_ty.needs_subst() {
1304+
return false;
1305+
}
1306+
if self.can_coerce(item_ty, expected_ty) {
1307+
err.span_suggestion_verbose(
1308+
segment.ident.span,
1309+
format!("try referring to the associated const `{capitalized_name}` instead",),
1310+
capitalized_name,
1311+
Applicability::MachineApplicable,
1312+
);
1313+
true
1314+
} else {
1315+
false
1316+
}
1317+
}
1318+
12391319
fn is_loop(&self, id: hir::HirId) -> bool {
12401320
let node = self.tcx.hir().get(id);
12411321
matches!(node, Node::Expr(Expr { kind: ExprKind::Loop(..), .. }))
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
struct MyS;
2+
3+
impl MyS {
4+
const FOO: i32 = 1;
5+
fn foo() -> MyS {
6+
MyS
7+
}
8+
}
9+
10+
fn main() {
11+
let x: i32 = MyS::foo;
12+
//~^ ERROR mismatched types
13+
//~| HELP try referring to the
14+
15+
let z: i32 = i32::max;
16+
//~^ ERROR mismatched types
17+
//~| HELP try referring to the
18+
19+
// This example is still broken though... This is a hard suggestion to make,
20+
// because we don't have access to the associated const probing code to make
21+
// this suggestion where it's emitted, i.e. in trait selection.
22+
let y: i32 = i32::max - 42;
23+
//~^ ERROR cannot subtract
24+
//~| HELP use parentheses
25+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
error[E0308]: mismatched types
2+
--> $DIR/assoc-ct-for-assoc-method.rs:11:18
3+
|
4+
LL | let x: i32 = MyS::foo;
5+
| --- ^^^^^^^^ expected `i32`, found fn item
6+
| |
7+
| expected due to this
8+
|
9+
= note: expected type `i32`
10+
found fn item `fn() -> MyS {MyS::foo}`
11+
help: try referring to the associated const `FOO` instead
12+
|
13+
LL | let x: i32 = MyS::FOO;
14+
| ~~~
15+
16+
error[E0308]: mismatched types
17+
--> $DIR/assoc-ct-for-assoc-method.rs:15:18
18+
|
19+
LL | let z: i32 = i32::max;
20+
| --- ^^^^^^^^ expected `i32`, found fn item
21+
| |
22+
| expected due to this
23+
|
24+
= note: expected type `i32`
25+
found fn item `fn(i32, i32) -> i32 {<i32 as Ord>::max}`
26+
help: try referring to the associated const `MAX` instead
27+
|
28+
LL | let z: i32 = i32::MAX;
29+
| ~~~
30+
31+
error[E0369]: cannot subtract `{integer}` from `fn(i32, i32) -> i32 {<i32 as Ord>::max}`
32+
--> $DIR/assoc-ct-for-assoc-method.rs:22:27
33+
|
34+
LL | let y: i32 = i32::max - 42;
35+
| -------- ^ -- {integer}
36+
| |
37+
| fn(i32, i32) -> i32 {<i32 as Ord>::max}
38+
|
39+
help: use parentheses to call this associated function
40+
|
41+
LL | let y: i32 = i32::max(/* i32 */, /* i32 */) - 42;
42+
| ++++++++++++++++++++++
43+
44+
error: aborting due to 3 previous errors
45+
46+
Some errors have detailed explanations: E0308, E0369.
47+
For more information about an error, try `rustc --explain E0308`.

0 commit comments

Comments
 (0)