1 use ide_db::base_db::{FileRange, SourceDatabase};
2 use syntax::{
3     algo::find_node_at_range,
4     ast::{self, HasArgList},
5     AstNode,
6 };
7 
8 use crate::{Diagnostic, DiagnosticsContext};
9 
10 // Diagnostic: mismatched-arg-count
11 //
12 // This diagnostic is triggered if a function is invoked with an incorrect amount of arguments.
mismatched_arg_count( ctx: &DiagnosticsContext<'_>, d: &hir::MismatchedArgCount, ) -> Diagnostic13 pub(crate) fn mismatched_arg_count(
14     ctx: &DiagnosticsContext<'_>,
15     d: &hir::MismatchedArgCount,
16 ) -> Diagnostic {
17     let s = if d.expected == 1 { "" } else { "s" };
18     let message = format!("expected {} argument{}, found {}", d.expected, s, d.found);
19     Diagnostic::new(
20         "mismatched-arg-count",
21         message,
22         invalid_args_range(ctx, d).unwrap_or_else(|it| it).range,
23     )
24 }
25 
invalid_args_range( ctx: &DiagnosticsContext<'_>, d: &hir::MismatchedArgCount, ) -> Result<FileRange, FileRange>26 fn invalid_args_range(
27     ctx: &DiagnosticsContext<'_>,
28     d: &hir::MismatchedArgCount,
29 ) -> Result<FileRange, FileRange> {
30     let full_range = ctx.sema.diagnostics_display_range(d.call_expr.clone().map(|it| it.into()));
31 
32     let source_file = ctx.sema.db.parse(full_range.file_id);
33     let expr = find_node_at_range::<ast::Expr>(&source_file.syntax_node(), full_range.range)
34         .filter(|it| it.syntax().text_range() == full_range.range);
35     let arg_list = match expr {
36         Some(ast::Expr::CallExpr(call)) => call.arg_list(),
37         Some(ast::Expr::MethodCallExpr(call)) => call.arg_list(),
38         _ => None,
39     };
40     let arg_list = match arg_list {
41         Some(it) => it,
42         None => return Err(full_range),
43     };
44     let arg_list_range =
45         FileRange { file_id: full_range.file_id, range: arg_list.syntax().text_range() };
46     if d.found < d.expected {
47         if d.found == 0 {
48             return Ok(arg_list_range);
49         }
50         if let Some(r_paren) = arg_list.r_paren_token() {
51             return Ok(FileRange { file_id: full_range.file_id, range: r_paren.text_range() });
52         }
53     }
54     if d.expected < d.found {
55         return Ok(arg_list_range);
56     }
57 
58     Err(full_range)
59 }
60 
61 #[cfg(test)]
62 mod tests {
63     use crate::tests::check_diagnostics;
64 
65     #[test]
simple_free_fn_zero()66     fn simple_free_fn_zero() {
67         check_diagnostics(
68             r#"
69 fn zero() {}
70 fn f() { zero(1); }
71            //^^^ error: expected 0 arguments, found 1
72 "#,
73         );
74 
75         check_diagnostics(
76             r#"
77 fn zero() {}
78 fn f() { zero(); }
79 "#,
80         );
81     }
82 
83     #[test]
simple_free_fn_one()84     fn simple_free_fn_one() {
85         check_diagnostics(
86             r#"
87 fn one(arg: u8) {}
88 fn f() { one(); }
89           //^^ error: expected 1 argument, found 0
90 "#,
91         );
92 
93         check_diagnostics(
94             r#"
95 fn one(arg: u8) {}
96 fn f() { one(1); }
97 "#,
98         );
99     }
100 
101     #[test]
method_as_fn()102     fn method_as_fn() {
103         check_diagnostics(
104             r#"
105 struct S;
106 impl S { fn method(&self) {} }
107 
108 fn f() {
109     S::method();
110 }          //^^ error: expected 1 argument, found 0
111 "#,
112         );
113 
114         check_diagnostics(
115             r#"
116 struct S;
117 impl S { fn method(&self) {} }
118 
119 fn f() {
120     S::method(&S);
121     S.method();
122 }
123 "#,
124         );
125     }
126 
127     #[test]
method_with_arg()128     fn method_with_arg() {
129         check_diagnostics(
130             r#"
131 struct S;
132 impl S { fn method(&self, arg: u8) {} }
133 
134             fn f() {
135                 S.method();
136             }         //^^ error: expected 1 argument, found 0
137             "#,
138         );
139 
140         check_diagnostics(
141             r#"
142 struct S;
143 impl S { fn method(&self, arg: u8) {} }
144 
145 fn f() {
146     S::method(&S, 0);
147     S.method(1);
148 }
149 "#,
150         );
151     }
152 
153     #[test]
method_unknown_receiver()154     fn method_unknown_receiver() {
155         // note: this is incorrect code, so there might be errors on this in the
156         // future, but we shouldn't emit an argument count diagnostic here
157         check_diagnostics(
158             r#"
159 trait Foo { fn method(&self, arg: usize) {} }
160 
161 fn f() {
162     let x;
163     x.method();
164 }
165 "#,
166         );
167     }
168 
169     #[test]
tuple_struct()170     fn tuple_struct() {
171         check_diagnostics(
172             r#"
173 struct Tup(u8, u16);
174 fn f() {
175     Tup(0);
176 }      //^ error: expected 2 arguments, found 1
177 "#,
178         )
179     }
180 
181     #[test]
enum_variant()182     fn enum_variant() {
183         check_diagnostics(
184             r#"
185 enum En { Variant(u8, u16), }
186 fn f() {
187     En::Variant(0);
188 }              //^ error: expected 2 arguments, found 1
189 "#,
190         )
191     }
192 
193     #[test]
enum_variant_type_macro()194     fn enum_variant_type_macro() {
195         check_diagnostics(
196             r#"
197 macro_rules! Type {
198     () => { u32 };
199 }
200 enum Foo {
201     Bar(Type![])
202 }
203 impl Foo {
204     fn new() {
205         Foo::Bar(0);
206         Foo::Bar(0, 1);
207               //^^^^^^ error: expected 1 argument, found 2
208         Foo::Bar();
209               //^^ error: expected 1 argument, found 0
210     }
211 }
212         "#,
213         );
214     }
215 
216     #[test]
varargs()217     fn varargs() {
218         check_diagnostics(
219             r#"
220 extern "C" {
221     fn fixed(fixed: u8);
222     fn varargs(fixed: u8, ...);
223     fn varargs2(...);
224 }
225 
226 fn f() {
227     unsafe {
228         fixed(0);
229         fixed(0, 1);
230            //^^^^^^ error: expected 1 argument, found 2
231         varargs(0);
232         varargs(0, 1);
233         varargs2();
234         varargs2(0);
235         varargs2(0, 1);
236     }
237 }
238         "#,
239         )
240     }
241 
242     #[test]
arg_count_lambda()243     fn arg_count_lambda() {
244         check_diagnostics(
245             r#"
246 fn main() {
247     let f = |()| ();
248     f();
249    //^^ error: expected 1 argument, found 0
250     f(());
251     f((), ());
252    //^^^^^^^^ error: expected 1 argument, found 2
253 }
254 "#,
255         )
256     }
257 
258     #[test]
cfgd_out_call_arguments()259     fn cfgd_out_call_arguments() {
260         check_diagnostics(
261             r#"
262 struct C(#[cfg(FALSE)] ());
263 impl C {
264     fn new() -> Self {
265         Self(
266             #[cfg(FALSE)]
267             (),
268         )
269     }
270 
271     fn method(&self) {}
272 }
273 
274 fn main() {
275     C::new().method(#[cfg(FALSE)] 0);
276 }
277             "#,
278         );
279     }
280 
281     #[test]
cfgd_out_fn_params()282     fn cfgd_out_fn_params() {
283         check_diagnostics(
284             r#"
285 fn foo(#[cfg(NEVER)] x: ()) {}
286 
287 struct S;
288 
289 impl S {
290     fn method(#[cfg(NEVER)] self) {}
291     fn method2(#[cfg(NEVER)] self, arg: u8) {}
292     fn method3(self, #[cfg(NEVER)] arg: u8) {}
293 }
294 
295 extern "C" {
296     fn fixed(fixed: u8, #[cfg(NEVER)] ...);
297     fn varargs(#[cfg(not(NEVER))] ...);
298 }
299 
300 fn main() {
301     foo();
302     S::method();
303     S::method2(0);
304     S::method3(S);
305     S.method3();
306     unsafe {
307         fixed(0);
308         varargs(1, 2, 3);
309     }
310 }
311             "#,
312         )
313     }
314 }
315