ide_diagnostics/handlers/
mismatched_arg_count.rs1use 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
11pub(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
32pub(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 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}