Diagnose mismatched arg count for tuple struct patterns
This commit is contained in:
parent
297ed70a23
commit
8654a098c7
@ -228,6 +228,11 @@ pub enum InferenceDiagnostic {
|
||||
expected: usize,
|
||||
found: usize,
|
||||
},
|
||||
MismatchedTupleStructPatArgCount {
|
||||
pat: ExprOrPatId,
|
||||
expected: usize,
|
||||
found: usize,
|
||||
},
|
||||
ExpectedFunction {
|
||||
call_expr: ExprId,
|
||||
found: Ty,
|
||||
|
@ -15,7 +15,8 @@ use crate::{
|
||||
infer::{BindingMode, Expectation, InferenceContext, TypeMismatch},
|
||||
lower::lower_to_chalk_mutability,
|
||||
primitive::UintTy,
|
||||
static_lifetime, Interner, Scalar, Substitution, Ty, TyBuilder, TyExt, TyKind,
|
||||
static_lifetime, InferenceDiagnostic, Interner, Scalar, Substitution, Ty, TyBuilder, TyExt,
|
||||
TyKind,
|
||||
};
|
||||
|
||||
/// Used to generalize patterns and assignee expressions.
|
||||
@ -74,6 +75,18 @@ impl InferenceContext<'_> {
|
||||
if let Some(variant) = def {
|
||||
self.write_variant_resolution(id.into(), variant);
|
||||
}
|
||||
if let Some(var) = &var_data {
|
||||
let cmp = if ellipsis.is_some() { usize::gt } else { usize::ne };
|
||||
|
||||
if cmp(&subs.len(), &var.fields().len()) {
|
||||
self.push_diagnostic(InferenceDiagnostic::MismatchedTupleStructPatArgCount {
|
||||
pat: id.into(),
|
||||
expected: var.fields().len(),
|
||||
found: subs.len(),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
self.unify(&ty, expected);
|
||||
|
||||
let substs =
|
||||
|
@ -43,6 +43,7 @@ diagnostics![
|
||||
MacroExpansionParseError,
|
||||
MalformedDerive,
|
||||
MismatchedArgCount,
|
||||
MismatchedTupleStructPatArgCount,
|
||||
MissingFields,
|
||||
MissingMatchArms,
|
||||
MissingUnsafe,
|
||||
@ -182,6 +183,13 @@ pub struct PrivateAssocItem {
|
||||
pub item: AssocItem,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct MismatchedTupleStructPatArgCount {
|
||||
pub expr_or_pat: InFile<Either<AstPtr<ast::Expr>, AstPtr<ast::Pat>>>,
|
||||
pub expected: usize,
|
||||
pub found: usize,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct ExpectedFunction {
|
||||
pub call: InFile<AstPtr<ast::Expr>>,
|
||||
|
@ -92,9 +92,10 @@ pub use crate::{
|
||||
diagnostics::{
|
||||
AnyDiagnostic, BreakOutsideOfLoop, CaseType, ExpectedFunction, InactiveCode,
|
||||
IncoherentImpl, IncorrectCase, InvalidDeriveTarget, MacroDefError, MacroError,
|
||||
MacroExpansionParseError, MalformedDerive, MismatchedArgCount, MissingFields,
|
||||
MissingMatchArms, MissingUnsafe, MovedOutOfRef, NeedMut, NoSuchField, PrivateAssocItem,
|
||||
PrivateField, ReplaceFilterMapNextWithFindMap, TypeMismatch, TypedHole, UndeclaredLabel,
|
||||
MacroExpansionParseError, MalformedDerive, MismatchedArgCount,
|
||||
MismatchedTupleStructPatArgCount, MissingFields, MissingMatchArms, MissingUnsafe,
|
||||
MovedOutOfRef, NeedMut, NoSuchField, PrivateAssocItem, PrivateField,
|
||||
ReplaceFilterMapNextWithFindMap, TypeMismatch, TypedHole, UndeclaredLabel,
|
||||
UnimplementedBuiltinMacro, UnreachableLabel, UnresolvedExternCrate, UnresolvedField,
|
||||
UnresolvedImport, UnresolvedMacroCall, UnresolvedMethodCall, UnresolvedModule,
|
||||
UnresolvedProcMacro, UnusedMut,
|
||||
@ -1597,6 +1598,23 @@ impl DefWithBody {
|
||||
.into(),
|
||||
)
|
||||
}
|
||||
&hir_ty::InferenceDiagnostic::MismatchedTupleStructPatArgCount {
|
||||
pat,
|
||||
expected,
|
||||
found,
|
||||
} => {
|
||||
let expr_or_pat = match pat {
|
||||
ExprOrPatId::ExprId(expr) => expr_syntax(expr).map(Either::Left),
|
||||
ExprOrPatId::PatId(pat) => source_map
|
||||
.pat_syntax(pat)
|
||||
.expect("unexpected synthetic")
|
||||
.map(|it| it.unwrap_left())
|
||||
.map(Either::Right),
|
||||
};
|
||||
acc.push(
|
||||
MismatchedTupleStructPatArgCount { expr_or_pat, expected, found }.into(),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
for (pat_or_expr, mismatch) in infer.type_mismatches() {
|
||||
|
@ -1,10 +1,37 @@
|
||||
use either::Either;
|
||||
use hir::InFile;
|
||||
use syntax::{
|
||||
ast::{self, HasArgList},
|
||||
AstNode, TextRange,
|
||||
AstNode, SyntaxNodePtr, TextRange,
|
||||
};
|
||||
|
||||
use crate::{adjusted_display_range, Diagnostic, DiagnosticCode, DiagnosticsContext};
|
||||
|
||||
// Diagnostic: mismatched-tuple-struct-pat-arg-count
|
||||
//
|
||||
// This diagnostic is triggered if a function is invoked with an incorrect amount of arguments.
|
||||
pub(crate) fn mismatched_tuple_struct_pat_arg_count(
|
||||
ctx: &DiagnosticsContext<'_>,
|
||||
d: &hir::MismatchedTupleStructPatArgCount,
|
||||
) -> Diagnostic {
|
||||
let s = if d.found == 1 { "" } else { "s" };
|
||||
let s2 = if d.expected == 1 { "" } else { "s" };
|
||||
let message = format!(
|
||||
"this pattern has {} field{s}, but the corresponding tuple struct has {} field{s2}",
|
||||
d.found, d.expected
|
||||
);
|
||||
Diagnostic::new(
|
||||
DiagnosticCode::RustcHardError("E0023"),
|
||||
message,
|
||||
invalid_args_range(
|
||||
ctx,
|
||||
d.expr_or_pat.clone().map(|it| it.either(Into::into, Into::into)),
|
||||
d.expected,
|
||||
d.found,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
// Diagnostic: mismatched-arg-count
|
||||
//
|
||||
// This diagnostic is triggered if a function is invoked with an incorrect amount of arguments.
|
||||
@ -14,31 +41,63 @@ pub(crate) fn mismatched_arg_count(
|
||||
) -> Diagnostic {
|
||||
let s = if d.expected == 1 { "" } else { "s" };
|
||||
let message = format!("expected {} argument{s}, found {}", d.expected, d.found);
|
||||
Diagnostic::new(DiagnosticCode::RustcHardError("E0107"), message, invalid_args_range(ctx, d))
|
||||
Diagnostic::new(
|
||||
DiagnosticCode::RustcHardError("E0107"),
|
||||
message,
|
||||
invalid_args_range(ctx, d.call_expr.clone().map(Into::into), d.expected, d.found),
|
||||
)
|
||||
}
|
||||
|
||||
fn invalid_args_range(ctx: &DiagnosticsContext<'_>, d: &hir::MismatchedArgCount) -> TextRange {
|
||||
adjusted_display_range::<ast::Expr>(ctx, d.call_expr.clone().map(|it| it.into()), &|expr| {
|
||||
let arg_list = match expr {
|
||||
ast::Expr::CallExpr(call) => call.arg_list()?,
|
||||
ast::Expr::MethodCallExpr(call) => call.arg_list()?,
|
||||
fn invalid_args_range(
|
||||
ctx: &DiagnosticsContext<'_>,
|
||||
source: InFile<SyntaxNodePtr>,
|
||||
expected: usize,
|
||||
found: usize,
|
||||
) -> TextRange {
|
||||
adjusted_display_range::<Either<ast::Expr, ast::TupleStructPat>>(ctx, source, &|expr| {
|
||||
let (text_range, r_paren_token, expected_arg) = match expr {
|
||||
Either::Left(ast::Expr::CallExpr(call)) => {
|
||||
let arg_list = call.arg_list()?;
|
||||
(
|
||||
arg_list.syntax().text_range(),
|
||||
arg_list.r_paren_token(),
|
||||
arg_list.args().nth(expected).map(|it| it.syntax().text_range()),
|
||||
)
|
||||
}
|
||||
Either::Left(ast::Expr::MethodCallExpr(call)) => {
|
||||
let arg_list = call.arg_list()?;
|
||||
(
|
||||
arg_list.syntax().text_range(),
|
||||
arg_list.r_paren_token(),
|
||||
arg_list.args().nth(expected).map(|it| it.syntax().text_range()),
|
||||
)
|
||||
}
|
||||
Either::Right(pat) => {
|
||||
let r_paren = pat.r_paren_token()?;
|
||||
let l_paren = pat.l_paren_token()?;
|
||||
(
|
||||
l_paren.text_range().cover(r_paren.text_range()),
|
||||
Some(r_paren),
|
||||
pat.fields().nth(expected).map(|it| it.syntax().text_range()),
|
||||
)
|
||||
}
|
||||
_ => return None,
|
||||
};
|
||||
if d.found < d.expected {
|
||||
if d.found == 0 {
|
||||
return Some(arg_list.syntax().text_range());
|
||||
if found < expected {
|
||||
if found == 0 {
|
||||
return Some(text_range);
|
||||
}
|
||||
if let Some(r_paren) = arg_list.r_paren_token() {
|
||||
if let Some(r_paren) = r_paren_token {
|
||||
return Some(r_paren.text_range());
|
||||
}
|
||||
}
|
||||
if d.expected < d.found {
|
||||
if d.expected == 0 {
|
||||
return Some(arg_list.syntax().text_range());
|
||||
if expected < found {
|
||||
if expected == 0 {
|
||||
return Some(text_range);
|
||||
}
|
||||
let zip = arg_list.args().nth(d.expected).zip(arg_list.r_paren_token());
|
||||
let zip = expected_arg.zip(r_paren_token);
|
||||
if let Some((arg, r_paren)) = zip {
|
||||
return Some(arg.syntax().text_range().cover(r_paren.text_range()));
|
||||
return Some(arg.cover(r_paren.text_range()));
|
||||
}
|
||||
}
|
||||
|
||||
@ -331,4 +390,21 @@ fn g() {
|
||||
"#,
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn tuple_struct_pat() {
|
||||
check_diagnostics(
|
||||
r#"
|
||||
struct S(u32, u32);
|
||||
fn f(
|
||||
S(a, b, c): S,
|
||||
// ^^ error: this pattern has 3 fields, but the corresponding tuple struct has 2 fields
|
||||
S(): S,
|
||||
// ^^ error: this pattern has 0 fields, but the corresponding tuple struct has 2 fields
|
||||
S(e, f, .., g, d): S
|
||||
// ^^^^^^^^^ error: this pattern has 4 fields, but the corresponding tuple struct has 2 fields
|
||||
) {}
|
||||
"#,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -319,6 +319,7 @@ fn main() {
|
||||
match Either::A {
|
||||
Either::A => (),
|
||||
Either::B() => (),
|
||||
// ^^ error: this pattern has 0 fields, but the corresponding tuple struct has 1 field
|
||||
}
|
||||
}
|
||||
"#,
|
||||
@ -334,9 +335,11 @@ enum A { B(isize, isize), C }
|
||||
fn main() {
|
||||
match A::B(1, 2) {
|
||||
A::B(_, _, _) => (),
|
||||
// ^^ error: this pattern has 3 fields, but the corresponding tuple struct has 2 fields
|
||||
}
|
||||
match A::B(1, 2) {
|
||||
A::C(_) => (),
|
||||
// ^^^ error: this pattern has 1 field, but the corresponding tuple struct has 0 fields
|
||||
}
|
||||
}
|
||||
"#,
|
||||
|
@ -369,6 +369,7 @@ pub fn diagnostics(
|
||||
AnyDiagnostic::UnresolvedProcMacro(d) => handlers::unresolved_proc_macro::unresolved_proc_macro(&ctx, &d, config.proc_macros_enabled, config.proc_attr_macros_enabled),
|
||||
AnyDiagnostic::UnusedMut(d) => handlers::mutability_errors::unused_mut(&ctx, &d),
|
||||
AnyDiagnostic::BreakOutsideOfLoop(d) => handlers::break_outside_of_loop::break_outside_of_loop(&ctx, &d),
|
||||
AnyDiagnostic::MismatchedTupleStructPatArgCount(d) => handlers::mismatched_arg_count::mismatched_tuple_struct_pat_arg_count(&ctx, &d),
|
||||
};
|
||||
res.push(d)
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user