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 // ^ 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}