about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2023-09-08 13:08:10 +0000
committerbors <bors@rust-lang.org>2023-09-08 13:08:10 +0000
commitc31d418a31aab71459238ae9ecffba033de111ae (patch)
tree8ef5eed6004b43e13bb72a4a8c0e01cad0291bcf
parentea71a49c8ec33a1c14359e1972f1280297c2bbb6 (diff)
parent8654a098c76ba0cbfa31d7ea668c3de5a48571a1 (diff)
downloadrust-c31d418a31aab71459238ae9ecffba033de111ae.tar.gz
rust-c31d418a31aab71459238ae9ecffba033de111ae.zip
Auto merge of #15578 - Veykril:diag-tuple-struct-count, r=Veykril
Diagnose mismatched arg count for tuple struct patterns
-rw-r--r--crates/hir-ty/src/infer.rs5
-rw-r--r--crates/hir-ty/src/infer/pat.rs15
-rw-r--r--crates/hir/src/diagnostics.rs8
-rw-r--r--crates/hir/src/lib.rs24
-rw-r--r--crates/ide-diagnostics/src/handlers/mismatched_arg_count.rs108
-rw-r--r--crates/ide-diagnostics/src/handlers/missing_match_arms.rs3
-rw-r--r--crates/ide-diagnostics/src/lib.rs1
7 files changed, 144 insertions, 20 deletions
diff --git a/crates/hir-ty/src/infer.rs b/crates/hir-ty/src/infer.rs
index 0fb4934444b..794d99964e4 100644
--- a/crates/hir-ty/src/infer.rs
+++ b/crates/hir-ty/src/infer.rs
@@ -228,6 +228,11 @@ pub enum InferenceDiagnostic {
         expected: usize,
         found: usize,
     },
+    MismatchedTupleStructPatArgCount {
+        pat: ExprOrPatId,
+        expected: usize,
+        found: usize,
+    },
     ExpectedFunction {
         call_expr: ExprId,
         found: Ty,
diff --git a/crates/hir-ty/src/infer/pat.rs b/crates/hir-ty/src/infer/pat.rs
index 5da0ab76b88..7d88e42e52a 100644
--- a/crates/hir-ty/src/infer/pat.rs
+++ b/crates/hir-ty/src/infer/pat.rs
@@ -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 =
diff --git a/crates/hir/src/diagnostics.rs b/crates/hir/src/diagnostics.rs
index 80c3bcdca8b..b8b997cc506 100644
--- a/crates/hir/src/diagnostics.rs
+++ b/crates/hir/src/diagnostics.rs
@@ -43,6 +43,7 @@ diagnostics![
     MacroExpansionParseError,
     MalformedDerive,
     MismatchedArgCount,
+    MismatchedTupleStructPatArgCount,
     MissingFields,
     MissingMatchArms,
     MissingUnsafe,
@@ -183,6 +184,13 @@ pub struct PrivateAssocItem {
 }
 
 #[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>>,
     pub found: Type,
diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs
index 91443238c62..47a2a25b6b7 100644
--- a/crates/hir/src/lib.rs
+++ b/crates/hir/src/lib.rs
@@ -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() {
diff --git a/crates/ide-diagnostics/src/handlers/mismatched_arg_count.rs b/crates/ide-diagnostics/src/handlers/mismatched_arg_count.rs
index 6238c7e09e9..8265e0b1c11 100644
--- a/crates/ide-diagnostics/src/handlers/mismatched_arg_count.rs
+++ b/crates/ide-diagnostics/src/handlers/mismatched_arg_count.rs
@@ -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
+) {}
+"#,
+        )
+    }
 }
diff --git a/crates/ide-diagnostics/src/handlers/missing_match_arms.rs b/crates/ide-diagnostics/src/handlers/missing_match_arms.rs
index 82a9a3bd559..d5115c2192b 100644
--- a/crates/ide-diagnostics/src/handlers/missing_match_arms.rs
+++ b/crates/ide-diagnostics/src/handlers/missing_match_arms.rs
@@ -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
     }
 }
 "#,
diff --git a/crates/ide-diagnostics/src/lib.rs b/crates/ide-diagnostics/src/lib.rs
index b1b9b4b8ea5..16ddb2aaf38 100644
--- a/crates/ide-diagnostics/src/lib.rs
+++ b/crates/ide-diagnostics/src/lib.rs
@@ -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)
     }