ide_diagnostics/handlers/
incorrect_generics_len.rs

1use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext};
2use hir::IncorrectGenericsLenKind;
3
4// Diagnostic: incorrect-generics-len
5//
6// This diagnostic is triggered if the number of generic arguments does not match their declaration.
7pub(crate) fn incorrect_generics_len(
8    ctx: &DiagnosticsContext<'_>,
9    d: &hir::IncorrectGenericsLen,
10) -> Diagnostic {
11    let owner_description = d.def.description();
12    let expected = d.expected;
13    let provided = d.provided;
14    let kind_description = match d.kind {
15        IncorrectGenericsLenKind::Lifetimes => "lifetime",
16        IncorrectGenericsLenKind::TypesAndConsts => "generic",
17    };
18    let message = format!(
19        "this {owner_description} takes {expected} {kind_description} argument{} \
20            but {provided} {kind_description} argument{} {} supplied",
21        if expected == 1 { "" } else { "s" },
22        if provided == 1 { "" } else { "s" },
23        if provided == 1 { "was" } else { "were" },
24    );
25    Diagnostic::new_with_syntax_node_ptr(
26        ctx,
27        DiagnosticCode::RustcHardError("E0107"),
28        message,
29        d.generics_or_segment.map(Into::into),
30    )
31}
32
33#[cfg(test)]
34mod tests {
35    use crate::tests::check_diagnostics;
36
37    #[test]
38    fn partially_specified_generics() {
39        check_diagnostics(
40            r#"
41struct Bar<T, U>(T, U);
42
43fn foo() {
44    let _ = Bar::<()>;
45            // ^^^^^^ error: this struct takes 2 generic arguments but 1 generic argument was supplied
46}
47
48        "#,
49        );
50    }
51
52    #[test]
53    fn enum_variant() {
54        check_diagnostics(
55            r#"
56enum Enum<T, U> {
57    Variant(T, U),
58}
59
60fn foo() {
61    let _ = Enum::<()>::Variant;
62             // ^^^^^^ error: this enum takes 2 generic arguments but 1 generic argument was supplied
63    let _ = Enum::Variant::<()>;
64                      // ^^^^^^ error: this enum takes 2 generic arguments but 1 generic argument was supplied
65}
66
67        "#,
68        );
69    }
70
71    #[test]
72    fn lifetimes() {
73        check_diagnostics(
74            r#"
75struct Foo<'a, 'b>(&'a &'b ());
76
77fn foo(Foo(_): Foo) -> Foo {
78    let _: Foo = Foo(&&());
79    let _: Foo::<> = Foo::<>(&&());
80    let _: Foo::<'static>
81           // ^^^^^^^^^^^ error: this struct takes 2 lifetime arguments but 1 lifetime argument was supplied
82                          = Foo::<'static>(&&());
83                            // ^^^^^^^^^^^ error: this struct takes 2 lifetime arguments but 1 lifetime argument was supplied
84    |_: Foo| -> Foo {loop{}};
85
86    loop {}
87}
88
89        "#,
90        );
91    }
92
93    #[test]
94    fn no_error_for_elided_lifetimes() {
95        check_diagnostics(
96            r#"
97struct Foo<'a>(&'a ());
98
99fn foo(_v: &()) -> Foo { loop {} }
100        "#,
101        );
102    }
103
104    #[test]
105    fn errs_for_elided_lifetimes_if_lifetimes_are_explicitly_provided() {
106        check_diagnostics(
107            r#"
108struct Foo<'a, 'b>(&'a &'b ());
109
110fn foo(_v: Foo<'_>
111           // ^^^^ error: this struct takes 2 lifetime arguments but 1 lifetime argument was supplied
112) -> Foo<'static> { loop {} }
113     // ^^^^^^^^^ error: this struct takes 2 lifetime arguments but 1 lifetime argument was supplied
114        "#,
115        );
116    }
117
118    #[test]
119    fn types_and_consts() {
120        check_diagnostics(
121            r#"
122struct Foo<'a, T>(&'a T);
123fn foo(_v: Foo) {}
124        // ^^^ error: this struct takes 1 generic argument but 0 generic arguments were supplied
125
126struct Bar<T, const N: usize>(T);
127fn bar() {
128    let _ = Bar::<()>;
129            // ^^^^^^ error: this struct takes 2 generic arguments but 1 generic argument was supplied
130}
131        "#,
132        );
133    }
134
135    #[test]
136    fn respects_defaults() {
137        check_diagnostics(
138            r#"
139struct Foo<T = (), const N: usize = 0>(T);
140fn foo(_v: Foo) {}
141
142struct Bar<T, const N: usize = 0>(T);
143fn bar(_v: Bar<()>) {}
144        "#,
145        );
146    }
147
148    #[test]
149    fn constant() {
150        check_diagnostics(
151            r#"
152const CONST: i32 = 0;
153fn baz() {
154    let _ = CONST::<()>;
155              // ^^^^^^ error: this constant takes 0 generic arguments but 1 generic argument was supplied
156}
157        "#,
158        );
159    }
160
161    #[test]
162    fn assoc_type() {
163        check_diagnostics(
164            r#"
165trait Trait {
166    type Assoc;
167}
168
169fn foo<T: Trait<Assoc<i32> = bool>>() {}
170                  // ^^^^^ error: this type alias takes 0 generic arguments but 1 generic argument was supplied
171        "#,
172        );
173    }
174
175    #[test]
176    fn regression_19669() {
177        check_diagnostics(
178            r#"
179//- minicore: from
180fn main() {
181    let _: i32 = Into::into(0);
182}
183"#,
184        );
185    }
186
187    #[test]
188    fn generic_assoc_type_infer_lifetime_in_expr_position() {
189        check_diagnostics(
190            r#"
191//- minicore: sized
192struct Player;
193
194struct Foo<'c, C> {
195    _v: &'c C,
196}
197trait WithSignals: Sized {
198    type SignalCollection<'c, C>;
199    fn __signals_from_external(&self) -> Self::SignalCollection<'_, Self>;
200}
201impl WithSignals for Player {
202    type SignalCollection<'c, C> = Foo<'c, C>;
203    fn __signals_from_external(&self) -> Self::SignalCollection<'_, Self> {
204        Self::SignalCollection { _v: self }
205    }
206}
207        "#,
208        );
209    }
210
211    #[test]
212    fn enum_type_alias_default_param() {
213        check_diagnostics(
214            r#"
215//- minicore: result
216
217struct Error;
218
219type Result<T, E = Error> = core::result::Result<T, E>;
220
221fn main() {
222    let _ = Result::<()>::Ok(());
223}
224        "#,
225        );
226    }
227}