Skip to content

Commit c31d418

Browse files
committed
Auto merge of rust-lang#15578 - Veykril:diag-tuple-struct-count, r=Veykril
Diagnose mismatched arg count for tuple struct patterns
2 parents ea71a49 + 8654a09 commit c31d418

File tree

7 files changed

+144
-20
lines changed

7 files changed

+144
-20
lines changed

crates/hir-ty/src/infer.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -228,6 +228,11 @@ pub enum InferenceDiagnostic {
228228
expected: usize,
229229
found: usize,
230230
},
231+
MismatchedTupleStructPatArgCount {
232+
pat: ExprOrPatId,
233+
expected: usize,
234+
found: usize,
235+
},
231236
ExpectedFunction {
232237
call_expr: ExprId,
233238
found: Ty,

crates/hir-ty/src/infer/pat.rs

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,8 @@ use crate::{
1515
infer::{BindingMode, Expectation, InferenceContext, TypeMismatch},
1616
lower::lower_to_chalk_mutability,
1717
primitive::UintTy,
18-
static_lifetime, Interner, Scalar, Substitution, Ty, TyBuilder, TyExt, TyKind,
18+
static_lifetime, InferenceDiagnostic, Interner, Scalar, Substitution, Ty, TyBuilder, TyExt,
19+
TyKind,
1920
};
2021

2122
/// Used to generalize patterns and assignee expressions.
@@ -74,6 +75,18 @@ impl InferenceContext<'_> {
7475
if let Some(variant) = def {
7576
self.write_variant_resolution(id.into(), variant);
7677
}
78+
if let Some(var) = &var_data {
79+
let cmp = if ellipsis.is_some() { usize::gt } else { usize::ne };
80+
81+
if cmp(&subs.len(), &var.fields().len()) {
82+
self.push_diagnostic(InferenceDiagnostic::MismatchedTupleStructPatArgCount {
83+
pat: id.into(),
84+
expected: var.fields().len(),
85+
found: subs.len(),
86+
});
87+
}
88+
}
89+
7790
self.unify(&ty, expected);
7891

7992
let substs =

crates/hir/src/diagnostics.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ diagnostics![
4343
MacroExpansionParseError,
4444
MalformedDerive,
4545
MismatchedArgCount,
46+
MismatchedTupleStructPatArgCount,
4647
MissingFields,
4748
MissingMatchArms,
4849
MissingUnsafe,
@@ -182,6 +183,13 @@ pub struct PrivateAssocItem {
182183
pub item: AssocItem,
183184
}
184185

186+
#[derive(Debug)]
187+
pub struct MismatchedTupleStructPatArgCount {
188+
pub expr_or_pat: InFile<Either<AstPtr<ast::Expr>, AstPtr<ast::Pat>>>,
189+
pub expected: usize,
190+
pub found: usize,
191+
}
192+
185193
#[derive(Debug)]
186194
pub struct ExpectedFunction {
187195
pub call: InFile<AstPtr<ast::Expr>>,

crates/hir/src/lib.rs

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -92,9 +92,10 @@ pub use crate::{
9292
diagnostics::{
9393
AnyDiagnostic, BreakOutsideOfLoop, CaseType, ExpectedFunction, InactiveCode,
9494
IncoherentImpl, IncorrectCase, InvalidDeriveTarget, MacroDefError, MacroError,
95-
MacroExpansionParseError, MalformedDerive, MismatchedArgCount, MissingFields,
96-
MissingMatchArms, MissingUnsafe, MovedOutOfRef, NeedMut, NoSuchField, PrivateAssocItem,
97-
PrivateField, ReplaceFilterMapNextWithFindMap, TypeMismatch, TypedHole, UndeclaredLabel,
95+
MacroExpansionParseError, MalformedDerive, MismatchedArgCount,
96+
MismatchedTupleStructPatArgCount, MissingFields, MissingMatchArms, MissingUnsafe,
97+
MovedOutOfRef, NeedMut, NoSuchField, PrivateAssocItem, PrivateField,
98+
ReplaceFilterMapNextWithFindMap, TypeMismatch, TypedHole, UndeclaredLabel,
9899
UnimplementedBuiltinMacro, UnreachableLabel, UnresolvedExternCrate, UnresolvedField,
99100
UnresolvedImport, UnresolvedMacroCall, UnresolvedMethodCall, UnresolvedModule,
100101
UnresolvedProcMacro, UnusedMut,
@@ -1597,6 +1598,23 @@ impl DefWithBody {
15971598
.into(),
15981599
)
15991600
}
1601+
&hir_ty::InferenceDiagnostic::MismatchedTupleStructPatArgCount {
1602+
pat,
1603+
expected,
1604+
found,
1605+
} => {
1606+
let expr_or_pat = match pat {
1607+
ExprOrPatId::ExprId(expr) => expr_syntax(expr).map(Either::Left),
1608+
ExprOrPatId::PatId(pat) => source_map
1609+
.pat_syntax(pat)
1610+
.expect("unexpected synthetic")
1611+
.map(|it| it.unwrap_left())
1612+
.map(Either::Right),
1613+
};
1614+
acc.push(
1615+
MismatchedTupleStructPatArgCount { expr_or_pat, expected, found }.into(),
1616+
)
1617+
}
16001618
}
16011619
}
16021620
for (pat_or_expr, mismatch) in infer.type_mismatches() {

crates/ide-diagnostics/src/handlers/mismatched_arg_count.rs

Lines changed: 92 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,37 @@
1+
use either::Either;
2+
use hir::InFile;
13
use syntax::{
24
ast::{self, HasArgList},
3-
AstNode, TextRange,
5+
AstNode, SyntaxNodePtr, TextRange,
46
};
57

68
use crate::{adjusted_display_range, Diagnostic, DiagnosticCode, DiagnosticsContext};
79

10+
// Diagnostic: mismatched-tuple-struct-pat-arg-count
11+
//
12+
// This diagnostic is triggered if a function is invoked with an incorrect amount of arguments.
13+
pub(crate) fn mismatched_tuple_struct_pat_arg_count(
14+
ctx: &DiagnosticsContext<'_>,
15+
d: &hir::MismatchedTupleStructPatArgCount,
16+
) -> Diagnostic {
17+
let s = if d.found == 1 { "" } else { "s" };
18+
let s2 = if d.expected == 1 { "" } else { "s" };
19+
let message = format!(
20+
"this pattern has {} field{s}, but the corresponding tuple struct has {} field{s2}",
21+
d.found, d.expected
22+
);
23+
Diagnostic::new(
24+
DiagnosticCode::RustcHardError("E0023"),
25+
message,
26+
invalid_args_range(
27+
ctx,
28+
d.expr_or_pat.clone().map(|it| it.either(Into::into, Into::into)),
29+
d.expected,
30+
d.found,
31+
),
32+
)
33+
}
34+
835
// Diagnostic: mismatched-arg-count
936
//
1037
// 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(
1441
) -> Diagnostic {
1542
let s = if d.expected == 1 { "" } else { "s" };
1643
let message = format!("expected {} argument{s}, found {}", d.expected, d.found);
17-
Diagnostic::new(DiagnosticCode::RustcHardError("E0107"), message, invalid_args_range(ctx, d))
44+
Diagnostic::new(
45+
DiagnosticCode::RustcHardError("E0107"),
46+
message,
47+
invalid_args_range(ctx, d.call_expr.clone().map(Into::into), d.expected, d.found),
48+
)
1849
}
1950

20-
fn invalid_args_range(ctx: &DiagnosticsContext<'_>, d: &hir::MismatchedArgCount) -> TextRange {
21-
adjusted_display_range::<ast::Expr>(ctx, d.call_expr.clone().map(|it| it.into()), &|expr| {
22-
let arg_list = match expr {
23-
ast::Expr::CallExpr(call) => call.arg_list()?,
24-
ast::Expr::MethodCallExpr(call) => call.arg_list()?,
51+
fn invalid_args_range(
52+
ctx: &DiagnosticsContext<'_>,
53+
source: InFile<SyntaxNodePtr>,
54+
expected: usize,
55+
found: usize,
56+
) -> TextRange {
57+
adjusted_display_range::<Either<ast::Expr, ast::TupleStructPat>>(ctx, source, &|expr| {
58+
let (text_range, r_paren_token, expected_arg) = match expr {
59+
Either::Left(ast::Expr::CallExpr(call)) => {
60+
let arg_list = call.arg_list()?;
61+
(
62+
arg_list.syntax().text_range(),
63+
arg_list.r_paren_token(),
64+
arg_list.args().nth(expected).map(|it| it.syntax().text_range()),
65+
)
66+
}
67+
Either::Left(ast::Expr::MethodCallExpr(call)) => {
68+
let arg_list = call.arg_list()?;
69+
(
70+
arg_list.syntax().text_range(),
71+
arg_list.r_paren_token(),
72+
arg_list.args().nth(expected).map(|it| it.syntax().text_range()),
73+
)
74+
}
75+
Either::Right(pat) => {
76+
let r_paren = pat.r_paren_token()?;
77+
let l_paren = pat.l_paren_token()?;
78+
(
79+
l_paren.text_range().cover(r_paren.text_range()),
80+
Some(r_paren),
81+
pat.fields().nth(expected).map(|it| it.syntax().text_range()),
82+
)
83+
}
2584
_ => return None,
2685
};
27-
if d.found < d.expected {
28-
if d.found == 0 {
29-
return Some(arg_list.syntax().text_range());
86+
if found < expected {
87+
if found == 0 {
88+
return Some(text_range);
3089
}
31-
if let Some(r_paren) = arg_list.r_paren_token() {
90+
if let Some(r_paren) = r_paren_token {
3291
return Some(r_paren.text_range());
3392
}
3493
}
35-
if d.expected < d.found {
36-
if d.expected == 0 {
37-
return Some(arg_list.syntax().text_range());
94+
if expected < found {
95+
if expected == 0 {
96+
return Some(text_range);
3897
}
39-
let zip = arg_list.args().nth(d.expected).zip(arg_list.r_paren_token());
98+
let zip = expected_arg.zip(r_paren_token);
4099
if let Some((arg, r_paren)) = zip {
41-
return Some(arg.syntax().text_range().cover(r_paren.text_range()));
100+
return Some(arg.cover(r_paren.text_range()));
42101
}
43102
}
44103

@@ -331,4 +390,21 @@ fn g() {
331390
"#,
332391
)
333392
}
393+
394+
#[test]
395+
fn tuple_struct_pat() {
396+
check_diagnostics(
397+
r#"
398+
struct S(u32, u32);
399+
fn f(
400+
S(a, b, c): S,
401+
// ^^ error: this pattern has 3 fields, but the corresponding tuple struct has 2 fields
402+
S(): S,
403+
// ^^ error: this pattern has 0 fields, but the corresponding tuple struct has 2 fields
404+
S(e, f, .., g, d): S
405+
// ^^^^^^^^^ error: this pattern has 4 fields, but the corresponding tuple struct has 2 fields
406+
) {}
407+
"#,
408+
)
409+
}
334410
}

crates/ide-diagnostics/src/handlers/missing_match_arms.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -319,6 +319,7 @@ fn main() {
319319
match Either::A {
320320
Either::A => (),
321321
Either::B() => (),
322+
// ^^ error: this pattern has 0 fields, but the corresponding tuple struct has 1 field
322323
}
323324
}
324325
"#,
@@ -334,9 +335,11 @@ enum A { B(isize, isize), C }
334335
fn main() {
335336
match A::B(1, 2) {
336337
A::B(_, _, _) => (),
338+
// ^^ error: this pattern has 3 fields, but the corresponding tuple struct has 2 fields
337339
}
338340
match A::B(1, 2) {
339341
A::C(_) => (),
342+
// ^^^ error: this pattern has 1 field, but the corresponding tuple struct has 0 fields
340343
}
341344
}
342345
"#,

crates/ide-diagnostics/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -369,6 +369,7 @@ pub fn diagnostics(
369369
AnyDiagnostic::UnresolvedProcMacro(d) => handlers::unresolved_proc_macro::unresolved_proc_macro(&ctx, &d, config.proc_macros_enabled, config.proc_attr_macros_enabled),
370370
AnyDiagnostic::UnusedMut(d) => handlers::mutability_errors::unused_mut(&ctx, &d),
371371
AnyDiagnostic::BreakOutsideOfLoop(d) => handlers::break_outside_of_loop::break_outside_of_loop(&ctx, &d),
372+
AnyDiagnostic::MismatchedTupleStructPatArgCount(d) => handlers::mismatched_arg_count::mismatched_tuple_struct_pat_arg_count(&ctx, &d),
372373
};
373374
res.push(d)
374375
}

0 commit comments

Comments
 (0)