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