Skip to main content

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     // ^ error: type annotations needed
209    x.method();
210}
211"#,
212        );
213    }
214
215    #[test]
216    fn tuple_struct() {
217        check_diagnostics(
218            r#"
219struct Tup(u8, u16);
220fn f() {
221    Tup(0);
222}      //^ error: expected 2 arguments, found 1
223"#,
224        )
225    }
226
227    #[test]
228    fn enum_variant() {
229        check_diagnostics(
230            r#"
231enum En { Variant(u8, u16), }
232fn f() {
233    En::Variant(0);
234}              //^ error: expected 2 arguments, found 1
235"#,
236        )
237    }
238
239    #[test]
240    fn enum_variant_type_macro() {
241        check_diagnostics(
242            r#"
243macro_rules! Type {
244    () => { u32 };
245}
246enum Foo {
247    Bar(Type![])
248}
249impl Foo {
250    fn new() {
251        Foo::Bar(0);
252        Foo::Bar(0, 1);
253                  //^^ error: expected 1 argument, found 2
254        Foo::Bar();
255              //^^ error: expected 1 argument, found 0
256    }
257}
258        "#,
259        );
260    }
261
262    #[test]
263    fn rest_pat_in_macro_expansion() {
264        check_diagnostics(
265            r#"
266// issue #17292
267#![allow(dead_code)]
268
269macro_rules! replace_with_2_dots {
270    ( $( $input:tt )* ) => {
271        ..
272    };
273}
274
275macro_rules! enum_str {
276    (
277        $(
278            $variant:ident (
279                $( $tfield:ty ),*
280            )
281        )
282        ,
283        *
284    ) => {
285        enum Foo {
286            $(
287                $variant ( $( $tfield ),* ),
288            )*
289        }
290
291        impl Foo {
292            fn variant_name_as_str(&self) -> &str {
293                match self {
294                    $(
295                        Self::$variant ( replace_with_2_dots!( $( $tfield ),* ) )
296                          => "",
297                    )*
298                }
299            }
300        }
301    };
302}
303
304enum_str! {
305    TupleVariant1(i32),
306    TupleVariant2(),
307    TupleVariant3(i8,u8,i128)
308}
309"#,
310        );
311
312        check_diagnostics(
313            r#"
314#![allow(dead_code)]
315macro_rules! two_dots1 {
316    () => { .. };
317}
318
319macro_rules! two_dots2 {
320    () => { two_dots1!() };
321}
322
323fn test() {
324    let (_, _, two_dots1!()) = ((), 42);
325    let (_, two_dots2!(), _) = (1, true, 2, false, (), (), 3);
326}
327"#,
328        );
329    }
330
331    #[test]
332    fn varargs() {
333        check_diagnostics(
334            r#"
335extern "C" {
336    fn fixed(fixed: u8);
337    fn varargs(fixed: u8, ...);
338    fn varargs2(...);
339}
340
341fn f() {
342    unsafe {
343        fixed(0);
344        fixed(0, 1);
345               //^^ error: expected 1 argument, found 2
346        varargs(0);
347        varargs(0, 1);
348        varargs2();
349        varargs2(0);
350        varargs2(0, 1);
351    }
352}
353        "#,
354        )
355    }
356
357    #[test]
358    fn arg_count_lambda() {
359        check_diagnostics(
360            r#"
361//- minicore: fn
362fn main() {
363    let f = |()| ();
364    f();
365   //^^ error: expected 1 argument, found 0
366    f(());
367    f((), ());
368        //^^^ error: expected 1 argument, found 2
369}
370"#,
371        )
372    }
373
374    #[test]
375    fn cfgd_out_call_arguments() {
376        check_diagnostics(
377            r#"
378struct C(#[cfg(FALSE)] ());
379impl C {
380    fn new() -> Self {
381        Self(
382            #[cfg(FALSE)]
383            (),
384        )
385    }
386
387    fn method(&self) {}
388}
389
390fn main() {
391    C::new().method(#[cfg(FALSE)] 0);
392}
393            "#,
394        );
395    }
396
397    #[test]
398    fn cfgd_out_fn_params() {
399        check_diagnostics(
400            r#"
401fn foo(#[cfg(NEVER)] x: ()) {}
402
403struct S;
404
405impl S {
406    fn method(#[cfg(NEVER)] self) {}
407    fn method2(#[cfg(NEVER)] self, _arg: u8) {}
408    fn method3(self, #[cfg(NEVER)] _arg: u8) {}
409}
410
411extern "C" {
412    fn fixed(fixed: u8, #[cfg(NEVER)] ...);
413    fn varargs(#[cfg(not(NEVER))] ...);
414}
415
416fn main() {
417    foo();
418    S::method();
419    S::method2(0);
420    S::method3(S);
421    S.method3();
422    unsafe {
423        fixed(0);
424        varargs(1, 2, 3);
425    }
426}
427            "#,
428        )
429    }
430
431    #[test]
432    fn legacy_const_generics() {
433        check_diagnostics(
434            r#"
435#[rustc_legacy_const_generics(1, 3)]
436fn mixed<const N1: &'static str, const N2: bool>(
437    _a: u8,
438    _b: i8,
439) {}
440
441fn f() {
442    mixed(0, "", -1, true);
443    mixed::<"", true>(0, -1);
444}
445
446#[rustc_legacy_const_generics(1, 3)]
447fn b<const N1: u8, const N2: u8>(
448    _a: u8,
449    _b: u8,
450) {}
451
452fn g() {
453    b(0, 1, 2, 3);
454    b::<1, 3>(0, 2);
455
456    b(0, 1, 2);
457 // ^ error: type annotations needed
458 // | full type: `fn b<_, _>(u8, u8)`
459           //^ error: expected 4 arguments, found 3
460}
461            "#,
462        )
463    }
464
465    #[test]
466    fn tuple_struct_pat() {
467        check_diagnostics(
468            r#"
469struct S(u32, u32);
470fn f(
471    S(a, b, c): S,
472         // ^^ error: this pattern has 3 fields, but the corresponding tuple struct has 2 fields
473    S(): S,
474  // ^^ error: this pattern has 0 fields, but the corresponding tuple struct has 2 fields
475    S(e, f, .., g, d): S
476  //        ^^^^^^^^^ error: this pattern has 4 fields, but the corresponding tuple struct has 2 fields
477) { _ = (a, b, c, d, e, f, g); }
478"#,
479        )
480    }
481
482    #[test]
483    fn no_type_mismatches_when_arg_count_mismatch() {
484        check_diagnostics(
485            r#"
486fn foo((): (), (): ()) {
487    foo(1, 2, 3);
488           // ^^ error: expected 2 arguments, found 3
489    foo(1);
490      // ^ error: expected 2 arguments, found 1
491}
492"#,
493        );
494    }
495
496    #[test]
497    fn regression_17233() {
498        check_diagnostics(
499            r#"
500pub trait A {
501    type X: B;
502}
503pub trait B: A {
504    fn confused_name(self, _: i32);
505}
506
507pub struct Foo;
508impl Foo {
509    pub fn confused_name(&self) {}
510}
511
512pub fn repro<T: A>() {
513    Foo.confused_name();
514}
515"#,
516        );
517    }
518}