From 63ce2c7b5fd96e6688796f2ddd1cd7316df8d11d Mon Sep 17 00:00:00 2001 From: Jonas Schievink Date: Wed, 8 Jul 2020 19:58:45 +0200 Subject: Add argument count mismatch diagnostic --- crates/ra_hir/src/diagnostics.rs | 4 ++- crates/ra_hir_ty/src/diagnostics.rs | 29 ++++++++++++++++ crates/ra_hir_ty/src/expr.rs | 68 ++++++++++++++++++++++++++++++++++--- crates/ra_ide/src/diagnostics.rs | 8 +++++ 4 files changed, 103 insertions(+), 6 deletions(-) diff --git a/crates/ra_hir/src/diagnostics.rs b/crates/ra_hir/src/diagnostics.rs index c82883d0c..11a0ecb8b 100644 --- a/crates/ra_hir/src/diagnostics.rs +++ b/crates/ra_hir/src/diagnostics.rs @@ -1,4 +1,6 @@ //! FIXME: write short doc here pub use hir_def::diagnostics::UnresolvedModule; pub use hir_expand::diagnostics::{AstDiagnostic, Diagnostic, DiagnosticSink}; -pub use hir_ty::diagnostics::{MissingFields, MissingMatchArms, MissingOkInTailExpr, NoSuchField}; +pub use hir_ty::diagnostics::{ + MismatchedArgCount, MissingFields, MissingMatchArms, MissingOkInTailExpr, NoSuchField, +}; 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 { ast::Expr::cast(node).unwrap() } } + +#[derive(Debug)] +pub struct MismatchedArgCount { + pub file: HirFileId, + pub call_expr: AstPtr, + pub expected: usize, + pub found: usize, +} + +impl Diagnostic for MismatchedArgCount { + fn message(&self) -> String { + format!("Expected {} arguments, found {}", self.expected, self.found) + } + fn source(&self) -> InFile { + InFile { file_id: self.file, value: self.call_expr.clone().into() } + } + fn as_any(&self) -> &(dyn Any + Send + 'static) { + self + } +} + +impl AstDiagnostic for MismatchedArgCount { + type AST = ast::CallExpr; + fn ast(&self, db: &dyn AstDatabase) -> Self::AST { + let root = db.parse_or_expand(self.source().file_id).unwrap(); + let node = self.source().value.to_node(&root); + ast::CallExpr::cast(node).unwrap() + } +} 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; use crate::{ db::HirDatabase, - diagnostics::{MissingFields, MissingMatchArms, MissingOkInTailExpr, MissingPatFields}, + diagnostics::{ + MismatchedArgCount, MissingFields, MissingMatchArms, MissingOkInTailExpr, MissingPatFields, + }, utils::variant_data, - ApplicationTy, InferenceResult, Ty, TypeCtor, + ApplicationTy, CallableDef, InferenceResult, Ty, TypeCtor, _match::{is_useful, MatchCheckCtx, Matrix, PatStack, Usefulness}, }; @@ -24,7 +26,8 @@ pub use hir_def::{ ArithOp, Array, BinaryOp, BindingAnnotation, CmpOp, Expr, ExprId, Literal, LogicOp, MatchArm, Ordering, Pat, PatId, RecordFieldPat, RecordLitField, Statement, UnaryOp, }, - LocalFieldId, VariantId, + src::HasSource, + LocalFieldId, Lookup, VariantId, }; pub struct ExprValidator<'a, 'b: 'a> { @@ -56,8 +59,15 @@ impl<'a, 'b> ExprValidator<'a, 'b> { missed_fields, ); } - if let Expr::Match { expr, arms } = expr { - self.validate_match(id, *expr, arms, db, self.infer.clone()); + + match expr { + Expr::Match { expr, arms } => { + self.validate_match(id, *expr, arms, db, self.infer.clone()); + } + Expr::Call { .. } | Expr::MethodCall { .. } => { + self.validate_call(db, id, expr); + } + _ => {} } } for (id, pat) in body.pats.iter() { @@ -138,6 +148,54 @@ impl<'a, 'b> ExprValidator<'a, 'b> { } } + fn validate_call(&mut self, db: &dyn HirDatabase, call_id: ExprId, expr: &Expr) -> Option<()> { + // Check that the number of arguments matches the number of parameters. + let (callee, args) = match expr { + Expr::Call { callee, args } => { + let callee = &self.infer.type_of_expr[*callee]; + let (callable, _) = callee.as_callable()?; + let callee = match callable { + CallableDef::FunctionId(func) => func, + _ => return None, + }; + + (callee, args.clone()) + } + Expr::MethodCall { receiver, args, .. } => { + let callee = self.infer.method_resolution(call_id)?; + let mut args = args.clone(); + args.insert(0, *receiver); + (callee, args) + } + _ => return None, + }; + + let loc = callee.lookup(db.upcast()); + let ast = loc.source(db.upcast()); + let params = ast.value.param_list()?; + + let mut param_count = params.params().count(); + if params.self_param().is_some() { + param_count += 1; + } + let arg_count = args.len(); + + if arg_count != param_count { + let (_, source_map): (Arc, Arc) = + db.body_with_source_map(self.func.into()); + if let Ok(source_ptr) = source_map.expr_syntax(call_id) { + self.sink.push(MismatchedArgCount { + file: source_ptr.file_id, + call_expr: source_ptr.value, + expected: param_count, + found: arg_count, + }); + } + } + + None + } + fn validate_match( &mut self, id: ExprId, diff --git a/crates/ra_ide/src/diagnostics.rs b/crates/ra_ide/src/diagnostics.rs index 46f8c31c7..d984f58ba 100644 --- a/crates/ra_ide/src/diagnostics.rs +++ b/crates/ra_ide/src/diagnostics.rs @@ -127,6 +127,14 @@ pub(crate) fn diagnostics(db: &RootDatabase, file_id: FileId) -> Vec severity: Severity::Error, fix: missing_struct_field_fix(&sema, file_id, d), }) + }) + .on::(|d| { + res.borrow_mut().push(Diagnostic { + range: sema.diagnostics_range(d).range, + message: d.message(), + severity: Severity::Error, + fix: None, + }) }); if let Some(m) = sema.to_module_def(file_id) { -- cgit v1.2.3