diff --git a/compiler/rustc_typeck/src/astconv/generics.rs b/compiler/rustc_typeck/src/astconv/generics.rs index 67e37ca8d8e49..0ea0ccaceabd4 100644 --- a/compiler/rustc_typeck/src/astconv/generics.rs +++ b/compiler/rustc_typeck/src/astconv/generics.rs @@ -6,8 +6,9 @@ use crate::astconv::{ use crate::errors::AssocTypeBindingNotAllowed; use crate::structured_errors::{StructuredDiagnostic, WrongNumberOfGenericArgs}; use rustc_ast::ast::ParamKindOrd; -use rustc_errors::{struct_span_err, Applicability, ErrorReported}; +use rustc_errors::{struct_span_err, Applicability, DiagnosticBuilder, ErrorReported}; use rustc_hir as hir; +use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::DefId; use rustc_hir::GenericArg; use rustc_middle::ty::{ @@ -43,23 +44,57 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { } } + let add_braces_suggestion = |arg: &GenericArg<'_>, err: &mut DiagnosticBuilder<'_>| { + let suggestions = vec![ + (arg.span().shrink_to_lo(), String::from("{ ")), + (arg.span().shrink_to_hi(), String::from(" }")), + ]; + err.multipart_suggestion( + "if this generic argument was intended as a const parameter, \ + surround it with braces", + suggestions, + Applicability::MaybeIncorrect, + ); + }; + // Specific suggestion set for diagnostics match (arg, ¶m.kind) { ( - GenericArg::Type(hir::Ty { kind: hir::TyKind::Path { .. }, .. }), - GenericParamDefKind::Const { .. }, - ) => { - let suggestions = vec![ - (arg.span().shrink_to_lo(), String::from("{ ")), - (arg.span().shrink_to_hi(), String::from(" }")), - ]; - err.multipart_suggestion( - "if this generic argument was intended as a const parameter, \ - try surrounding it with braces:", - suggestions, - Applicability::MaybeIncorrect, - ); - } + GenericArg::Type(hir::Ty { + kind: hir::TyKind::Path(rustc_hir::QPath::Resolved(_, path)), + .. + }), + GenericParamDefKind::Const, + ) => match path.res { + Res::Err => { + add_braces_suggestion(arg, &mut err); + err.set_primary_message( + "unresolved item provided when a constant was expected", + ) + .emit(); + return; + } + Res::Def(DefKind::TyParam, src_def_id) => { + if let Some(param_local_id) = param.def_id.as_local() { + let param_hir_id = tcx.hir().local_def_id_to_hir_id(param_local_id); + let param_name = tcx.hir().ty_param_name(param_hir_id); + let param_type = tcx.type_of(param.def_id); + if param_type.is_suggestable() { + err.span_suggestion( + tcx.def_span(src_def_id), + "consider changing this type paramater to a `const`-generic", + format!("const {}: {}", param_name, param_type), + Applicability::MaybeIncorrect, + ); + }; + } + } + _ => add_braces_suggestion(arg, &mut err), + }, + ( + GenericArg::Type(hir::Ty { kind: hir::TyKind::Path(_), .. }), + GenericParamDefKind::Const, + ) => add_braces_suggestion(arg, &mut err), ( GenericArg::Type(hir::Ty { kind: hir::TyKind::Array(_, len), .. }), GenericParamDefKind::Const { .. }, diff --git a/src/test/ui/const-generics/const-param-shadowing.stderr b/src/test/ui/const-generics/const-param-shadowing.stderr index 7447ca3ff3631..17ccd2f3527b0 100644 --- a/src/test/ui/const-generics/const-param-shadowing.stderr +++ b/src/test/ui/const-generics/const-param-shadowing.stderr @@ -4,7 +4,7 @@ error[E0747]: type provided when a constant was expected LL | fn test() -> Foo { | ^ | -help: if this generic argument was intended as a const parameter, try surrounding it with braces: +help: if this generic argument was intended as a const parameter, surround it with braces | LL | fn test() -> Foo<{ N }> { | ^ ^ diff --git a/src/test/ui/const-generics/diagnostics.rs b/src/test/ui/const-generics/diagnostics.rs new file mode 100644 index 0000000000000..1581af5ab2751 --- /dev/null +++ b/src/test/ui/const-generics/diagnostics.rs @@ -0,0 +1,18 @@ +#![crate_type="lib"] +#![feature(min_const_generics)] +#![allow(incomplete_features)] + +struct A; +trait Foo {} +impl Foo for A {} +//~^ ERROR cannot find type +//~| unresolved item provided when a constant + +struct B; +impl Foo for B {} +//~^ ERROR type provided when a constant + +struct C; +impl Foo for C {} +//~^ ERROR cannot find type +//~| unresolved item provided when a constant diff --git a/src/test/ui/const-generics/diagnostics.stderr b/src/test/ui/const-generics/diagnostics.stderr new file mode 100644 index 0000000000000..7d038ff955d6c --- /dev/null +++ b/src/test/ui/const-generics/diagnostics.stderr @@ -0,0 +1,52 @@ +error[E0412]: cannot find type `N` in this scope + --> $DIR/diagnostics.rs:7:16 + | +LL | struct A; + | ---------------------- similarly named struct `A` defined here +LL | trait Foo {} +LL | impl Foo for A {} + | ^ help: a struct with a similar name exists: `A` + +error[E0412]: cannot find type `T` in this scope + --> $DIR/diagnostics.rs:16:32 + | +LL | struct A; + | ---------------------- similarly named struct `A` defined here +... +LL | impl Foo for C {} + | ^ help: a struct with a similar name exists: `A` + +error[E0747]: unresolved item provided when a constant was expected + --> $DIR/diagnostics.rs:7:16 + | +LL | impl Foo for A {} + | ^ + | +help: if this generic argument was intended as a const parameter, surround it with braces + | +LL | impl Foo for A<{ N }> {} + | ^ ^ + +error[E0747]: type provided when a constant was expected + --> $DIR/diagnostics.rs:12:19 + | +LL | impl Foo for B {} + | - ^ + | | + | help: consider changing this type paramater to a `const`-generic: `const N: u8` + +error[E0747]: unresolved item provided when a constant was expected + --> $DIR/diagnostics.rs:16:32 + | +LL | impl Foo for C {} + | ^ + | +help: if this generic argument was intended as a const parameter, surround it with braces + | +LL | impl Foo for C {} + | ^ ^ + +error: aborting due to 5 previous errors + +Some errors have detailed explanations: E0412, E0747. +For more information about an error, try `rustc --explain E0412`. diff --git a/src/test/ui/const-generics/invalid-enum.rs b/src/test/ui/const-generics/invalid-enum.rs index 4ca10ed8b71a2..32939dcd2861b 100644 --- a/src/test/ui/const-generics/invalid-enum.rs +++ b/src/test/ui/const-generics/invalid-enum.rs @@ -3,14 +3,14 @@ #[derive(PartialEq, Eq)] enum CompileFlag { - A, - B, + A, + B, } pub fn test_1() {} pub fn test_2(x: T) {} pub struct Example{ - x: T, + x: T, } impl Example { @@ -20,15 +20,15 @@ impl Example { pub fn main() { test_1::(); //~^ ERROR: expected type, found variant - //~| ERROR: type provided when a constant was expected + //~| ERROR: unresolved item provided when a constant was expected test_2::<_, CompileFlag::A>(0); //~^ ERROR: expected type, found variant - //~| ERROR: type provided when a constant was expected + //~| ERROR: unresolved item provided when a constant was expected let _: Example = Example { x: 0 }; //~^ ERROR: expected type, found variant - //~| ERROR: type provided when a constant was expected + //~| ERROR: unresolved item provided when a constant was expected let _: Example = Example { x: 0 }; //~^ ERROR: type provided when a constant was expected diff --git a/src/test/ui/const-generics/invalid-enum.stderr b/src/test/ui/const-generics/invalid-enum.stderr index 7822fc072e35c..cfbc61f02543b 100644 --- a/src/test/ui/const-generics/invalid-enum.stderr +++ b/src/test/ui/const-generics/invalid-enum.stderr @@ -25,13 +25,13 @@ LL | let _: Example = Example { x: 0 }; | not a type | help: try using the variant's enum: `CompileFlag` -error[E0747]: type provided when a constant was expected +error[E0747]: unresolved item provided when a constant was expected --> $DIR/invalid-enum.rs:29:18 | LL | let _: Example = Example { x: 0 }; | ^^^^^^^^^^^^^^ | -help: if this generic argument was intended as a const parameter, try surrounding it with braces: +help: if this generic argument was intended as a const parameter, surround it with braces | LL | let _: Example<{ CompileFlag::A }, _> = Example { x: 0 }; | ^ ^ @@ -42,29 +42,29 @@ error[E0747]: type provided when a constant was expected LL | let _: Example = Example { x: 0 }; | ^^^^^^^^^^^^^^^^^^^ | -help: if this generic argument was intended as a const parameter, try surrounding it with braces: +help: if this generic argument was intended as a const parameter, surround it with braces | LL | let _: Example<{ Example::ASSOC_FLAG }, _> = Example { x: 0 }; | ^ ^ -error[E0747]: type provided when a constant was expected +error[E0747]: unresolved item provided when a constant was expected --> $DIR/invalid-enum.rs:21:12 | LL | test_1::(); | ^^^^^^^^^^^^^^ | -help: if this generic argument was intended as a const parameter, try surrounding it with braces: +help: if this generic argument was intended as a const parameter, surround it with braces | LL | test_1::<{ CompileFlag::A }>(); | ^ ^ -error[E0747]: type provided when a constant was expected +error[E0747]: unresolved item provided when a constant was expected --> $DIR/invalid-enum.rs:25:15 | LL | test_2::<_, CompileFlag::A>(0); | ^^^^^^^^^^^^^^ | -help: if this generic argument was intended as a const parameter, try surrounding it with braces: +help: if this generic argument was intended as a const parameter, surround it with braces | LL | test_2::<_, { CompileFlag::A }>(0); | ^ ^