use either::Either; use hir::GenericArgsProhibitedReason; use ide_db::assists::Assist; use ide_db::source_change::SourceChange; use ide_db::text_edit::TextEdit; use syntax::{AstNode, TextRange, ast}; use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext, fix}; // Diagnostic: generic-args-prohibited // // This diagnostic is shown when generic arguments are provided for a type that does not accept // generic arguments. pub(crate) fn generic_args_prohibited( ctx: &DiagnosticsContext<'_>, d: &hir::GenericArgsProhibited, ) -> Diagnostic { Diagnostic::new_with_syntax_node_ptr( ctx, DiagnosticCode::RustcHardError("E0109"), describe_reason(d.reason), d.args.map(Into::into), ) .stable() .with_fixes(fixes(ctx, d)) } fn describe_reason(reason: GenericArgsProhibitedReason) -> String { let kind = match reason { GenericArgsProhibitedReason::Module => "modules", GenericArgsProhibitedReason::TyParam => "type parameters", GenericArgsProhibitedReason::SelfTy => "`Self`", GenericArgsProhibitedReason::PrimitiveTy => "builtin types", GenericArgsProhibitedReason::EnumVariant => { return "you can specify generic arguments on either the enum or the variant, but not both" .to_owned(); } GenericArgsProhibitedReason::Const => "constants", GenericArgsProhibitedReason::Static => "statics", GenericArgsProhibitedReason::LocalVariable => "local variables", }; format!("generic arguments are not allowed on {kind}") } fn fixes(ctx: &DiagnosticsContext<'_>, d: &hir::GenericArgsProhibited) -> Option> { let file_id = d.args.file_id.file_id()?; let syntax = d.args.to_node(ctx.sema.db); let range = match &syntax { Either::Left(_) => syntax.syntax().text_range(), Either::Right(param_list) => { let path_segment = ast::PathSegment::cast(param_list.syntax().parent()?)?; let start = if let Some(coloncolon) = path_segment.coloncolon_token() { coloncolon.text_range().start() } else { param_list.syntax().text_range().start() }; let end = if let Some(ret_type) = path_segment.ret_type() { ret_type.syntax().text_range().end() } else { param_list.syntax().text_range().end() }; TextRange::new(start, end) } }; Some(vec![fix( "remove_generic_args", "Remove these generics", SourceChange::from_text_edit(file_id.file_id(ctx.sema.db), TextEdit::delete(range)), syntax.syntax().text_range(), )]) } #[cfg(test)] mod tests { // This diagnostic was the first to be emitted in ty lowering, so the tests here also test // diagnostics in ty lowering in general (which is why there are so many of them). use crate::tests::{check_diagnostics, check_fix}; #[test] fn primitives() { check_diagnostics( r#" //- /core.rs crate:core library #![rustc_coherence_is_core] impl str { pub fn trim() {} } //- /lib.rs crate:foo deps:core fn bar() {} fn foo() { let _: (bool<()>, ()); // ^^^^ 💡 error: generic arguments are not allowed on builtin types let _ = >::trim; // ^^^^ 💡 error: generic arguments are not allowed on builtin types bar::>(); // ^^^^^^^^^^^^^^^^^^^^^ 💡 error: generic arguments are not allowed on builtin types } "#, ); } #[test] fn modules() { check_diagnostics( r#" pub mod foo { pub mod bar { pub struct Baz; impl Baz { pub fn qux() {} } } } fn foo() { let _: foo::<'_>::bar::Baz; // ^^^^^^ 💡 error: generic arguments are not allowed on modules let _ = ::Baz>::qux; // ^^^^ 💡 error: generic arguments are not allowed on modules } "#, ); } #[test] fn type_parameters() { check_diagnostics( r#" fn foo() { let _: T<'a>; // ^^^^ 💡 error: generic arguments are not allowed on type parameters let _: U::<{ 1 + 2 }>; // ^^^^^^^^^^^^^ 💡 error: generic arguments are not allowed on type parameters } "#, ); } #[test] fn fn_like_generic_args() { check_diagnostics( r#" fn foo() { let _: bool(bool, i32) -> (); // ^^^^^^^^^^^ 💡 error: generic arguments are not allowed on builtin types } "#, ); } #[test] fn fn_signature() { check_diagnostics( r#" fn foo( _a: bool<'_>, // ^^^^ 💡 error: generic arguments are not allowed on builtin types _b: i32::, // ^^^^^^^ 💡 error: generic arguments are not allowed on builtin types _c: &(&str<1>) // ^^^ 💡 error: generic arguments are not allowed on builtin types ) -> ((), i32) { // ^^^^^^ 💡 error: generic arguments are not allowed on builtin types ((), 0) } "#, ); } #[test] fn const_static_type() { check_diagnostics( r#" const A: i32 = 0; // ^^^^^^ 💡 error: generic arguments are not allowed on builtin types static A: i32::<{ 1 + 3 }> = 0; // ^^^^^^^^^^^^^ 💡 error: generic arguments are not allowed on builtin types "#, ); } #[test] fn fix() { check_fix( r#" fn foo() { let _: bool<'_, (), { 1 + 1 }>$0; }"#, r#" fn foo() { let _: bool; }"#, ); check_fix( r#" fn foo() { let _: bool::$0<'_, (), { 1 + 1 }>; }"#, r#" fn foo() { let _: bool; }"#, ); check_fix( r#" fn foo() { let _: bool(i$032); }"#, r#" fn foo() { let _: bool; }"#, ); check_fix( r#" fn foo() { let _: bool$0(i32) -> i64; }"#, r#" fn foo() { let _: bool; }"#, ); check_fix( r#" fn foo() { let _: bool::(i$032) -> i64; }"#, r#" fn foo() { let _: bool; }"#, ); check_fix( r#" fn foo() { let _: bool::(i32)$0; }"#, r#" fn foo() { let _: bool; }"#, ); } #[test] fn in_fields() { check_diagnostics( r#" struct A(bool); // ^^^^^ 💡 error: generic arguments are not allowed on builtin types struct B { v: bool<(), 1> } // ^^^^^^^ 💡 error: generic arguments are not allowed on builtin types union C { a: bool, // ^^^^^ 💡 error: generic arguments are not allowed on builtin types b: i32, // ^^^^^^ 💡 error: generic arguments are not allowed on builtin types } enum D { A(bool), // ^^^^^ 💡 error: generic arguments are not allowed on builtin types B { v: i32 }, // ^^^^^^ 💡 error: generic arguments are not allowed on builtin types } "#, ); } #[test] fn in_generics() { check_diagnostics( r#" mod foo { pub trait Trait {} } struct A::Trait>(A) // ^^^^^^ 💡 error: generic arguments are not allowed on modules where bool: foo::Trait; // ^^^^^ 💡 error: generic arguments are not allowed on builtin types union B::Trait> // ^^^^^^ 💡 error: generic arguments are not allowed on modules where bool: foo::Trait // ^^^^^ 💡 error: generic arguments are not allowed on builtin types { a: A } enum C::Trait> // ^^^^^^ 💡 error: generic arguments are not allowed on modules where bool: foo::Trait // ^^^^^ 💡 error: generic arguments are not allowed on builtin types {} fn f::Trait>() // ^^^^^^ 💡 error: generic arguments are not allowed on modules where bool: foo::Trait // ^^^^^ 💡 error: generic arguments are not allowed on builtin types {} type D::Trait> = A // ^^^^^^ 💡 error: generic arguments are not allowed on modules where bool: foo::Trait; // ^^^^^ 💡 error: generic arguments are not allowed on builtin types trait E::Trait> // ^^^^^^ 💡 error: generic arguments are not allowed on modules where bool: foo::Trait // ^^^^^ 💡 error: generic arguments are not allowed on builtin types { fn f::Trait>() // ^^^^^^ 💡 error: generic arguments are not allowed on modules where bool: foo::Trait // ^^^^^ 💡 error: generic arguments are not allowed on builtin types {} type D::Trait> = A // ^^^^^^ 💡 error: generic arguments are not allowed on modules where bool: foo::Trait; // ^^^^^ 💡 error: generic arguments are not allowed on builtin types } impl::Trait> E<()> for () // ^^^^^^ 💡 error: generic arguments are not allowed on modules where bool: foo::Trait // ^^^^^ 💡 error: generic arguments are not allowed on builtin types { fn f::Trait>() // ^^^^^^ 💡 error: generic arguments are not allowed on modules where bool: foo::Trait // ^^^^^ 💡 error: generic arguments are not allowed on builtin types {} type D::Trait> = A // ^^^^^^ 💡 error: generic arguments are not allowed on modules where bool: foo::Trait; // ^^^^^ 💡 error: generic arguments are not allowed on builtin types } "#, ); } #[test] fn assoc_items() { check_diagnostics( r#" struct Foo; trait Trait { fn f() -> bool { true } // ^^^^^ 💡 error: generic arguments are not allowed on builtin types type T = i32; // ^^^^^^ 💡 error: generic arguments are not allowed on builtin types } impl Trait for Foo { fn f() -> bool { true } // ^^^^^ 💡 error: generic arguments are not allowed on builtin types type T = i32; // ^^^^^^ 💡 error: generic arguments are not allowed on builtin types } impl Foo { fn f() -> bool { true } // ^^^^^ 💡 error: generic arguments are not allowed on builtin types type T = i32; // ^^^^^^ 💡 error: generic arguments are not allowed on builtin types } "#, ); } #[test] fn const_param_ty() { check_diagnostics( r#" fn foo< const A: bool, // ^^^^^ 💡 error: generic arguments are not allowed on builtin types B, C, const D: bool, // ^^^^^ 💡 error: generic arguments are not allowed on builtin types const E: bool, // ^^^^^ 💡 error: generic arguments are not allowed on builtin types >() {} "#, ); } #[test] fn generic_defaults() { check_diagnostics( r#" struct Foo>(A); // ^^^^^ 💡 error: generic arguments are not allowed on builtin types "#, ); } #[test] fn impl_self_ty() { check_diagnostics( r#" struct Foo(A); trait Trait {} impl Foo> {} // ^^^^^ 💡 error: generic arguments are not allowed on builtin types impl Trait for Foo> {} // ^^^^^ 💡 error: generic arguments are not allowed on builtin types "#, ); } #[test] fn impl_trait() { check_diagnostics( r#" mod foo { pub trait Trait {} } impl foo::<()>::Trait for () {} // ^^^^^^ 💡 error: generic arguments are not allowed on modules "#, ); } #[test] fn type_alias() { check_diagnostics( r#" pub trait Trait { type Assoc; } type T = bool; // ^^^^^ 💡 error: generic arguments are not allowed on builtin types impl Trait for () { type Assoc = i32; // ^^^^^^ 💡 error: generic arguments are not allowed on builtin types } "#, ); } #[test] fn in_record_expr() { check_diagnostics( r#" mod foo { pub struct Bar { pub field: i32 } } fn baz() { let _ = foo::<()>::Bar { field: 0 }; // ^^^^^^ 💡 error: generic arguments are not allowed on modules } "#, ); } #[test] fn in_record_pat() { check_diagnostics( r#" mod foo { pub struct Bar { field: i32 } } fn baz(v: foo::Bar) { let foo::<()>::Bar { .. } = v; // ^^^^^^ 💡 error: generic arguments are not allowed on modules } "#, ); } #[test] fn in_tuple_struct_pat() { check_diagnostics( r#" mod foo { pub struct Bar(i32); } fn baz(v: foo::Bar) { let foo::<()>::Bar(..) = v; // ^^^^^^ 💡 error: generic arguments are not allowed on modules } "#, ); } #[test] fn in_path_pat() { check_diagnostics( r#" mod foo { pub struct Bar; } fn baz(v: foo::Bar) { let foo::<()>::Bar = v; // ^^^^^^ 💡 error: generic arguments are not allowed on modules } "#, ); } #[test] fn in_path_expr() { check_diagnostics( r#" mod foo { pub struct Bar; } fn baz() { let _ = foo::<()>::Bar; // ^^^^^^ 💡 error: generic arguments are not allowed on modules } "#, ); } #[test] fn const_param_and_static() { check_diagnostics( r#" const CONST: i32 = 0; static STATIC: i32 = 0; fn baz() { let _ = CONST_PARAM::<()>; // ^^^^^^ 💡 error: generic arguments are not allowed on constants let _ = STATIC::<()>; // ^^^^^^ 💡 error: generic arguments are not allowed on statics } "#, ); } #[test] fn local_variable() { check_diagnostics( r#" fn baz() { let x = 1; let _ = x::<()>; // ^^^^^^ 💡 error: generic arguments are not allowed on local variables } "#, ); } #[test] fn enum_variant() { check_diagnostics( r#" enum Enum { Variant(A), } mod enum_ { pub(super) use super::Enum::Variant as V; } fn baz() { let v = Enum::<()>::Variant::<()>(()); // ^^^^^^ 💡 error: you can specify generic arguments on either the enum or the variant, but not both let Enum::<()>::Variant::<()>(..) = v; // ^^^^^^ 💡 error: you can specify generic arguments on either the enum or the variant, but not both let _ = Enum::<()>::Variant(()); let _ = Enum::Variant::<()>(()); } fn foo() { use Enum::Variant; let _ = Variant::<()>(()); let _ = enum_::V::<()>(()); let _ = enum_::<()>::V::<()>(()); // ^^^^^^ 💡 error: generic arguments are not allowed on modules } "#, ); } #[test] fn dyn_trait() { check_diagnostics( r#" mod foo { pub trait Trait {} } fn bar() { let _: &dyn foo::<()>::Trait; // ^^^^^^ 💡 error: generic arguments are not allowed on modules let _: &foo::<()>::Trait; // ^^^^^^ 💡 error: generic arguments are not allowed on modules } "#, ); } #[test] fn regression_18768() { check_diagnostics( r#" //- minicore: result //- /foo.rs crate:foo edition:2018 pub mod lib { mod core { pub use core::*; } pub use self::core::result; } pub mod __private { pub use crate::lib::result::Result::{self, Err, Ok}; } //- /bar.rs crate:bar deps:foo edition:2018 fn bar() { _ = foo::__private::Result::<(), ()>::Ok; } "#, ); } #[test] fn enum_variant_type_ns() { check_diagnostics( r#" enum KvnDeserializerErr { UnexpectedKeyword { found: I, expected: I }, } fn foo() { let _x: KvnDeserializerErr<()> = KvnDeserializerErr::<()>::UnexpectedKeyword { found: (), expected: () }; let _x: KvnDeserializerErr<()> = KvnDeserializerErr::<()>::UnexpectedKeyword::<()> { found: (), expected: () }; // ^^^^^^ 💡 error: you can specify generic arguments on either the enum or the variant, but not both } "#, ); } }