aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_hir_ty
diff options
context:
space:
mode:
authorJonas Schievink <[email protected]>2020-07-08 18:58:45 +0100
committerJonas Schievink <[email protected]>2020-07-09 11:16:29 +0100
commit63ce2c7b5fd96e6688796f2ddd1cd7316df8d11d (patch)
tree8e3ce783592c9c136efac6bbf78ffff177c077cc /crates/ra_hir_ty
parent91005ecc27427f46529b9372f91e5072dfe5e179 (diff)
Add argument count mismatch diagnostic
Diffstat (limited to 'crates/ra_hir_ty')
-rw-r--r--crates/ra_hir_ty/src/diagnostics.rs29
-rw-r--r--crates/ra_hir_ty/src/expr.rs68
2 files changed, 92 insertions, 5 deletions
diff --git a/crates/ra_hir_ty/src/diagnostics.rs b/crates/ra_hir_ty/src/diagnostics.rs
index 0289911de..daac669e6 100644
--- a/crates/ra_hir_ty/src/diagnostics.rs
+++ b/crates/ra_hir_ty/src/diagnostics.rs
@@ -197,3 +197,32 @@ impl AstDiagnostic for MissingUnsafe {
197 ast::Expr::cast(node).unwrap() 197 ast::Expr::cast(node).unwrap()
198 } 198 }
199} 199}
200
201#[derive(Debug)]
202pub struct MismatchedArgCount {
203 pub file: HirFileId,
204 pub call_expr: AstPtr<ast::Expr>,
205 pub expected: usize,
206 pub found: usize,
207}
208
209impl Diagnostic for MismatchedArgCount {
210 fn message(&self) -> String {
211 format!("Expected {} arguments, found {}", self.expected, self.found)
212 }
213 fn source(&self) -> InFile<SyntaxNodePtr> {
214 InFile { file_id: self.file, value: self.call_expr.clone().into() }
215 }
216 fn as_any(&self) -> &(dyn Any + Send + 'static) {
217 self
218 }
219}
220
221impl AstDiagnostic for MismatchedArgCount {
222 type AST = ast::CallExpr;
223 fn ast(&self, db: &dyn AstDatabase) -> Self::AST {
224 let root = db.parse_or_expand(self.source().file_id).unwrap();
225 let node = self.source().value.to_node(&root);
226 ast::CallExpr::cast(node).unwrap()
227 }
228}
diff --git a/crates/ra_hir_ty/src/expr.rs b/crates/ra_hir_ty/src/expr.rs
index 7db928dde..7c3cd7952 100644
--- a/crates/ra_hir_ty/src/expr.rs
+++ b/crates/ra_hir_ty/src/expr.rs
@@ -9,9 +9,11 @@ use rustc_hash::FxHashSet;
9 9
10use crate::{ 10use crate::{
11 db::HirDatabase, 11 db::HirDatabase,
12 diagnostics::{MissingFields, MissingMatchArms, MissingOkInTailExpr, MissingPatFields}, 12 diagnostics::{
13 MismatchedArgCount, MissingFields, MissingMatchArms, MissingOkInTailExpr, MissingPatFields,
14 },
13 utils::variant_data, 15 utils::variant_data,
14 ApplicationTy, InferenceResult, Ty, TypeCtor, 16 ApplicationTy, CallableDef, InferenceResult, Ty, TypeCtor,
15 _match::{is_useful, MatchCheckCtx, Matrix, PatStack, Usefulness}, 17 _match::{is_useful, MatchCheckCtx, Matrix, PatStack, Usefulness},
16}; 18};
17 19
@@ -24,7 +26,8 @@ pub use hir_def::{
24 ArithOp, Array, BinaryOp, BindingAnnotation, CmpOp, Expr, ExprId, Literal, LogicOp, 26 ArithOp, Array, BinaryOp, BindingAnnotation, CmpOp, Expr, ExprId, Literal, LogicOp,
25 MatchArm, Ordering, Pat, PatId, RecordFieldPat, RecordLitField, Statement, UnaryOp, 27 MatchArm, Ordering, Pat, PatId, RecordFieldPat, RecordLitField, Statement, UnaryOp,
26 }, 28 },
27 LocalFieldId, VariantId, 29 src::HasSource,
30 LocalFieldId, Lookup, VariantId,
28}; 31};
29 32
30pub struct ExprValidator<'a, 'b: 'a> { 33pub struct ExprValidator<'a, 'b: 'a> {
@@ -56,8 +59,15 @@ impl<'a, 'b> ExprValidator<'a, 'b> {
56 missed_fields, 59 missed_fields,
57 ); 60 );
58 } 61 }
59 if let Expr::Match { expr, arms } = expr { 62
60 self.validate_match(id, *expr, arms, db, self.infer.clone()); 63 match expr {
64 Expr::Match { expr, arms } => {
65 self.validate_match(id, *expr, arms, db, self.infer.clone());
66 }
67 Expr::Call { .. } | Expr::MethodCall { .. } => {
68 self.validate_call(db, id, expr);
69 }
70 _ => {}
61 } 71 }
62 } 72 }
63 for (id, pat) in body.pats.iter() { 73 for (id, pat) in body.pats.iter() {
@@ -138,6 +148,54 @@ impl<'a, 'b> ExprValidator<'a, 'b> {
138 } 148 }
139 } 149 }
140 150
151 fn validate_call(&mut self, db: &dyn HirDatabase, call_id: ExprId, expr: &Expr) -> Option<()> {
152 // Check that the number of arguments matches the number of parameters.
153 let (callee, args) = match expr {
154 Expr::Call { callee, args } => {
155 let callee = &self.infer.type_of_expr[*callee];
156 let (callable, _) = callee.as_callable()?;
157 let callee = match callable {
158 CallableDef::FunctionId(func) => func,
159 _ => return None,
160 };
161
162 (callee, args.clone())
163 }
164 Expr::MethodCall { receiver, args, .. } => {
165 let callee = self.infer.method_resolution(call_id)?;
166 let mut args = args.clone();
167 args.insert(0, *receiver);
168 (callee, args)
169 }
170 _ => return None,
171 };
172
173 let loc = callee.lookup(db.upcast());
174 let ast = loc.source(db.upcast());
175 let params = ast.value.param_list()?;
176
177 let mut param_count = params.params().count();
178 if params.self_param().is_some() {
179 param_count += 1;
180 }
181 let arg_count = args.len();
182
183 if arg_count != param_count {
184 let (_, source_map): (Arc<Body>, Arc<BodySourceMap>) =
185 db.body_with_source_map(self.func.into());
186 if let Ok(source_ptr) = source_map.expr_syntax(call_id) {
187 self.sink.push(MismatchedArgCount {
188 file: source_ptr.file_id,
189 call_expr: source_ptr.value,
190 expected: param_count,
191 found: arg_count,
192 });
193 }
194 }
195
196 None
197 }
198
141 fn validate_match( 199 fn validate_match(
142 &mut self, 200 &mut self,
143 id: ExprId, 201 id: ExprId,