Add argument count mismatch diagnostic

This commit is contained in:
Jonas Schievink 2020-07-08 19:58:45 +02:00
parent 91005ecc27
commit 63ce2c7b5f
4 changed files with 103 additions and 6 deletions

View file

@ -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,
};

View file

@ -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<ast::Expr>,
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<SyntaxNodePtr> {
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()
}
}

View file

@ -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<Body>, Arc<BodySourceMap>) =
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,

View file

@ -127,6 +127,14 @@ pub(crate) fn diagnostics(db: &RootDatabase, file_id: FileId) -> Vec<Diagnostic>
severity: Severity::Error,
fix: missing_struct_field_fix(&sema, file_id, d),
})
})
.on::<hir::diagnostics::MismatchedArgCount, _>(|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) {