ide_diagnostics/handlers/
mismatched_arg_count.rs

1use either::Either;
2use hir::InFile;
3use ide_db::FileRange;
4use syntax::{
5    AstNode, AstPtr,
6    ast::{self, HasArgList},
7};
8
9use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext, adjusted_display_range};
10
11// Diagnostic: mismatched-tuple-struct-pat-arg-count
12//
13// This diagnostic is triggered if a function is invoked with an incorrect amount of arguments.
14pub(crate) fn mismatched_tuple_struct_pat_arg_count(
15    ctx: &DiagnosticsContext<'_>,
16    d: &hir::MismatchedTupleStructPatArgCount,
17) -> Diagnostic {
18    let s = if d.found == 1 { "" } else { "s" };
19    let s2 = if d.expected == 1 { "" } else { "s" };
20    let message = format!(
21        "this pattern has {} field{s}, but the corresponding tuple struct has {} field{s2}",
22        d.found, d.expected
23    );
24    Diagnostic::new(
25        DiagnosticCode::RustcHardError("E0023"),
26        message,
27        invalid_args_range(ctx, d.expr_or_pat, d.expected, d.found),
28    )
29    .stable()
30}
31
32// Diagnostic: mismatched-arg-count
33//
34// This diagnostic is triggered if a function is invoked with an incorrect amount of arguments.
35pub(crate) fn mismatched_arg_count(
36    ctx: &DiagnosticsContext<'_>,
37    d: &hir::MismatchedArgCount,
38) -> Diagnostic {
39    let s = if d.expected == 1 { "" } else { "s" };
40    let message = format!("expected {} argument{s}, found {}", d.expected, d.found);
41    Diagnostic::new(
42        DiagnosticCode::RustcHardError("E0107"),
43        message,
44        invalid_args_range(ctx, d.call_expr, d.expected, d.found),
45    )
46    .stable()
47}
48
49fn invalid_args_range(
50    ctx: &DiagnosticsContext<'_>,
51    source: InFile<AstPtr<Either<ast::Expr, ast::Pat>>>,
52    expected: usize,
53    found: usize,
54) -> FileRange {
55    adjusted_display_range(ctx, source, &|expr| {
56        let (text_range, r_paren_token, expected_arg) = match expr {
57            Either::Left(ast::Expr::CallExpr(call)) => {
58                let arg_list = call.arg_list()?;
59                (
60                    arg_list.syntax().text_range(),
61                    arg_list.r_paren_token(),
62                    arg_list.args().nth(expected).map(|it| it.syntax().text_range()),
63                )
64            }
65            Either::Left(ast::Expr::MethodCallExpr(call)) => {
66                let arg_list = call.arg_list()?;
67                (
68                    arg_list.syntax().text_range(),
69                    arg_list.r_paren_token(),
70                    arg_list.args().nth(expected).map(|it| it.syntax().text_range()),
71                )
72            }
73            Either::Right(ast::Pat::TupleStructPat(pat)) => {
74                let r_paren = pat.r_paren_token()?;
75                let l_paren = pat.l_paren_token()?;
76                (
77                    l_paren.text_range().cover(r_paren.text_range()),
78                    Some(r_paren),
79                    pat.fields().nth(expected).map(|it| it.syntax().text_range()),
80                )
81            }
82            _ => return None,
83        };
84        if found < expected {
85            if found == 0 {
86                return Some(text_range);
87            }
88            if let Some(r_paren) = r_paren_token {
89                return Some(r_paren.text_range());
90            }
91        }
92        if expected < found {
93            if expected == 0 {
94                return Some(text_range);
95            }
96            let zip = expected_arg.zip(r_paren_token);
97            if let Some((arg, r_paren)) = zip {
98                return Some(arg.cover(r_paren.text_range()));
99            }
100        }
101
102        None
103    })
104}
105
106#[cfg(test)]
107mod tests {
108    use crate::tests::check_diagnostics;
109
110    #[test]
111    fn simple_free_fn_zero() {
112        check_diagnostics(
113            r#"
114fn zero() {}
115fn f() { zero(1); }
116           //^^^ error: expected 0 arguments, found 1
117"#,
118        );
119
120        check_diagnostics(
121            r#"
122fn zero() {}
123fn f() { zero(); }
124"#,
125        );
126    }
127
128    #[test]
129    fn simple_free_fn_one() {
130        check_diagnostics(
131            r#"
132fn one(_arg: u8) {}
133fn f() { one(); }
134          //^^ error: expected 1 argument, found 0
135"#,
136        );
137
138        check_diagnostics(
139            r#"
140fn one(_arg: u8) {}
141fn f() { one(1); }
142"#,
143        );
144    }
145
146    #[test]
147    fn method_as_fn() {
148        check_diagnostics(
149            r#"
150struct S;
151impl S { fn method(&self) {} }
152
153fn f() {
154    S::method();
155}          //^^ error: expected 1 argument, found 0
156"#,
157        );
158
159        check_diagnostics(
160            r#"
161struct S;
162impl S { fn method(&self) {} }
163
164fn f() {
165    S::method(&S);
166    S.method();
167}
168"#,
169        );
170    }
171
172    #[test]
173    fn method_with_arg() {
174        check_diagnostics(
175            r#"
176struct S;
177impl S { fn method(&self, _arg: u8) {} }
178
179            fn f() {
180                S.method();
181            }         //^^ error: expected 1 argument, found 0
182            "#,
183        );
184
185        check_diagnostics(
186            r#"
187struct S;
188impl S { fn method(&self, _arg: u8) {} }
189
190fn f() {
191    S::method(&S, 0);
192    S.method(1);
193}
194"#,
195        );
196    }
197
198    #[test]
199    fn method_unknown_receiver() {
200        // note: this is incorrect code, so there might be errors on this in the
201        // future, but we shouldn't emit an argument count diagnostic here
202        check_diagnostics(
203            r#"
204trait Foo { fn method(&self, _arg: usize) {} }
205
206fn f() {
207    let x;
208    x.method();
209}
210"#,
211        );
212    }
213
214    #[test]
215    fn tuple_struct() {
216        check_diagnostics(
217            r#"
218struct Tup(u8, u16);
219fn f() {
220    Tup(0);
221}      //^ error: expected 2 arguments, found 1
222"#,
223        )
224    }
225
226    #[test]
227    fn enum_variant() {
228        check_diagnostics(
229            r#"
230enum En { Variant(u8, u16), }
231fn f() {
232    En::Variant(0);
233}              //^ error: expected 2 arguments, found 1
234"#,
235        )
236    }
237
238    #[test]
239    fn enum_variant_type_macro() {
240        check_diagnostics(
241            r#"
242macro_rules! Type {
243    () => { u32 };
244}
245enum Foo {
246    Bar(Type![])
247}
248impl Foo {
249    fn new() {
250        Foo::Bar(0);
251        Foo::Bar(0, 1);
252                  //^^ error: expected 1 argument, found 2
253        Foo::Bar();
254              //^^ error: expected 1 argument, found 0
255    }
256}
257        "#,
258        );
259    }
260
261    #[test]
262    fn rest_pat_in_macro_expansion() {
263        check_diagnostics(
264            r#"
265// issue #17292
266#![allow(dead_code)]
267
268macro_rules! replace_with_2_dots {
269    ( $( $input:tt )* ) => {
270        ..
271    };
272}
273
274macro_rules! enum_str {
275    (
276        $(
277            $variant:ident (
278                $( $tfield:ty ),*
279            )
280        )
281        ,
282        *
283    ) => {
284        enum Foo {
285            $(
286                $variant ( $( $tfield ),* ),
287            )*
288        }
289
290        impl Foo {
291            fn variant_name_as_str(&self) -> &str {
292                match self {
293                    $(
294                        Self::$variant ( replace_with_2_dots!( $( $tfield ),* ) )
295                          => "",
296                    )*
297                }
298            }
299        }
300    };
301}
302
303enum_str! {
304    TupleVariant1(i32),
305    TupleVariant2(),
306    TupleVariant3(i8,u8,i128)
307}
308"#,
309        );
310
311        check_diagnostics(
312            r#"
313#![allow(dead_code)]
314macro_rules! two_dots1 {
315    () => { .. };
316}
317
318macro_rules! two_dots2 {
319    () => { two_dots1!() };
320}
321
322fn test() {
323    let (_, _, two_dots1!()) = ((), 42);
324    let (_, two_dots2!(), _) = (1, true, 2, false, (), (), 3);
325}
326"#,
327        );
328    }
329
330    #[test]
331    fn varargs() {
332        check_diagnostics(
333            r#"
334extern "C" {
335    fn fixed(fixed: u8);
336    fn varargs(fixed: u8, ...);
337    fn varargs2(...);
338}
339
340fn f() {
341    unsafe {
342        fixed(0);
343        fixed(0, 1);
344               //^^ error: expected 1 argument, found 2
345        varargs(0);
346        varargs(0, 1);
347        varargs2();
348        varargs2(0);
349        varargs2(0, 1);
350    }
351}
352        "#,
353        )
354    }
355
356    #[test]
357    fn arg_count_lambda() {
358        check_diagnostics(
359            r#"
360fn main() {
361    let f = |()| ();
362    f();
363   //^^ error: expected 1 argument, found 0
364    f(());
365    f((), ());
366        //^^^ error: expected 1 argument, found 2
367}
368"#,
369        )
370    }
371
372    #[test]
373    fn cfgd_out_call_arguments() {
374        check_diagnostics(
375            r#"
376struct C(#[cfg(FALSE)] ());
377impl C {
378    fn new() -> Self {
379        Self(
380            #[cfg(FALSE)]
381            (),
382        )
383    }
384
385    fn method(&self) {}
386}
387
388fn main() {
389    C::new().method(#[cfg(FALSE)] 0);
390}
391            "#,
392        );
393    }
394
395    #[test]
396    fn cfgd_out_fn_params() {
397        check_diagnostics(
398            r#"
399fn foo(#[cfg(NEVER)] x: ()) {}
400
401struct S;
402
403impl S {
404    fn method(#[cfg(NEVER)] self) {}
405    fn method2(#[cfg(NEVER)] self, _arg: u8) {}
406    fn method3(self, #[cfg(NEVER)] _arg: u8) {}
407}
408
409extern "C" {
410    fn fixed(fixed: u8, #[cfg(NEVER)] ...);
411    fn varargs(#[cfg(not(NEVER))] ...);
412}
413
414fn main() {
415    foo();
416    S::method();
417    S::method2(0);
418    S::method3(S);
419    S.method3();
420    unsafe {
421        fixed(0);
422        varargs(1, 2, 3);
423    }
424}
425            "#,
426        )
427    }
428
429    #[test]
430    fn legacy_const_generics() {
431        check_diagnostics(
432            r#"
433#[rustc_legacy_const_generics(1, 3)]
434fn mixed<const N1: &'static str, const N2: bool>(
435    _a: u8,
436    _b: i8,
437) {}
438
439fn f() {
440    mixed(0, "", -1, true);
441    mixed::<"", true>(0, -1);
442}
443
444#[rustc_legacy_const_generics(1, 3)]
445fn b<const N1: u8, const N2: u8>(
446    _a: u8,
447    _b: u8,
448) {}
449
450fn g() {
451    b(0, 1, 2, 3);
452    b::<1, 3>(0, 2);
453
454    b(0, 1, 2);
455           //^ error: expected 4 arguments, found 3
456}
457            "#,
458        )
459    }
460
461    #[test]
462    fn tuple_struct_pat() {
463        check_diagnostics(
464            r#"
465struct S(u32, u32);
466fn f(
467    S(a, b, c): S,
468         // ^^ error: this pattern has 3 fields, but the corresponding tuple struct has 2 fields
469    S(): S,
470  // ^^ error: this pattern has 0 fields, but the corresponding tuple struct has 2 fields
471    S(e, f, .., g, d): S
472  //        ^^^^^^^^^ error: this pattern has 4 fields, but the corresponding tuple struct has 2 fields
473) { _ = (a, b, c, d, e, f, g); }
474"#,
475        )
476    }
477
478    #[test]
479    fn no_type_mismatches_when_arg_count_mismatch() {
480        check_diagnostics(
481            r#"
482fn foo((): (), (): ()) {
483    foo(1, 2, 3);
484           // ^^ error: expected 2 arguments, found 3
485    foo(1);
486      // ^ error: expected 2 arguments, found 1
487}
488"#,
489        );
490    }
491
492    #[test]
493    fn regression_17233() {
494        check_diagnostics(
495            r#"
496pub trait A {
497    type X: B;
498}
499pub trait B: A {
500    fn confused_name(self, _: i32);
501}
502
503pub struct Foo;
504impl Foo {
505    pub fn confused_name(&self) {}
506}
507
508pub fn repro<T: A>() {
509    Foo.confused_name();
510}
511"#,
512        );
513    }
514}