diff --git a/src/librustc/middle/typeck/check/_match.rs b/src/librustc/middle/typeck/check/_match.rs index d8a9350e695d7..b7114e602830f 100644 --- a/src/librustc/middle/typeck/check/_match.rs +++ b/src/librustc/middle/typeck/check/_match.rs @@ -156,6 +156,7 @@ pub fn check_pat_variant(pcx: &pat_ctxt, pat: @ast::pat, path: &ast::Path, kind_name = "variant"; } None => { + // See [Note-Type-error-reporting] in middle/typeck/infer/mod.rs fcx.infcx().type_error_message_str_with_expected(pat.span, |expected, actual| { expected.map_move_default(~"", |e| { @@ -199,6 +200,7 @@ pub fn check_pat_variant(pcx: &pat_ctxt, pat: @ast::pat, path: &ast::Path, kind_name = "structure"; } _ => { + // See [Note-Type-error-reporting] in middle/typeck/infer/mod.rs fcx.infcx().type_error_message_str_with_expected(pat.span, |expected, actual| { expected.map_move_default(~"", |e| { @@ -302,10 +304,13 @@ pub fn check_struct_pat_fields(pcx: &pat_ctxt, } None => { let name = pprust::path_to_str(path, tcx.sess.intr()); + // Check the pattern anyway, so that attempts to look + // up its type won't fail + check_pat(pcx, field.pat, ty::mk_err()); tcx.sess.span_err(span, - fmt!("struct `%s` does not have a field - named `%s`", name, - tcx.sess.str_of(field.ident))); + fmt!("struct `%s` does not have a field named `%s`", + name, + tcx.sess.str_of(field.ident))); } } } @@ -326,16 +331,17 @@ pub fn check_struct_pat_fields(pcx: &pat_ctxt, pub fn check_struct_pat(pcx: &pat_ctxt, pat_id: ast::NodeId, span: span, expected: ty::t, path: &ast::Path, fields: &[ast::field_pat], etc: bool, - class_id: ast::def_id, substitutions: &ty::substs) { + struct_id: ast::def_id, + substitutions: &ty::substs) { let fcx = pcx.fcx; let tcx = pcx.fcx.ccx.tcx; - let class_fields = ty::lookup_struct_fields(tcx, class_id); + let class_fields = ty::lookup_struct_fields(tcx, struct_id); // Check to ensure that the struct is the one specified. match tcx.def_map.find(&pat_id) { Some(&ast::def_struct(supplied_def_id)) - if supplied_def_id == class_id => { + if supplied_def_id == struct_id => { // OK. } Some(&ast::def_struct(*)) | Some(&ast::def_variant(*)) => { @@ -346,11 +352,11 @@ pub fn check_struct_pat(pcx: &pat_ctxt, pat_id: ast::NodeId, span: span, name)); } _ => { - tcx.sess.span_bug(span, "resolve didn't write in class"); + tcx.sess.span_bug(span, "resolve didn't write in struct ID"); } } - check_struct_pat_fields(pcx, span, path, fields, class_fields, class_id, + check_struct_pat_fields(pcx, span, path, fields, class_fields, struct_id, substitutions, etc); } @@ -499,9 +505,22 @@ pub fn check_pat(pcx: &pat_ctxt, pat: @ast::pat, expected: ty::t) { substs); } _ => { - tcx.sess.span_err(pat.span, - fmt!("mismatched types: expected `%s` but found struct", - fcx.infcx().ty_to_str(expected))); + // See [Note-Type-error-reporting] in middle/typeck/infer/mod.rs + fcx.infcx().type_error_message_str_with_expected(pat.span, + |expected, actual| { + expected.map_move_default(~"", |e| { + fmt!("mismatched types: expected `%s` but found %s", + e, actual)})}, + Some(expected), ~"a structure pattern", + None); + match tcx.def_map.find(&pat.id) { + Some(&ast::def_struct(supplied_def_id)) => { + check_struct_pat(pcx, pat.id, pat.span, ty::mk_err(), path, *fields, etc, + supplied_def_id, + &ty::substs { self_ty: None, tps: ~[], regions: ty::ErasedRegions} ); + } + _ => () // Error, but we're already in an error case + } error_happened = true; } } @@ -534,6 +553,7 @@ pub fn check_pat(pcx: &pat_ctxt, pat: @ast::pat, expected: ty::t) { found: e_count}), _ => ty::terr_mismatch }; + // See [Note-Type-error-reporting] in middle/typeck/infer/mod.rs fcx.infcx().type_error_message_str_with_expected(pat.span, |expected, actual| { expected.map_move_default(~"", |e| { fmt!("mismatched types: expected `%s` but found %s", @@ -581,6 +601,7 @@ pub fn check_pat(pcx: &pat_ctxt, pat: @ast::pat, expected: ty::t) { for &elt in after.iter() { check_pat(pcx, elt, ty::mk_err()); } + // See [Note-Type-error-reporting] in middle/typeck/infer/mod.rs fcx.infcx().type_error_message_str_with_expected( pat.span, |expected, actual| { @@ -639,6 +660,7 @@ pub fn check_pointer_pat(pcx: &pat_ctxt, } _ => { check_pat(pcx, inner, ty::mk_err()); + // See [Note-Type-error-reporting] in middle/typeck/infer/mod.rs fcx.infcx().type_error_message_str_with_expected( span, |expected, actual| { diff --git a/src/librustc/middle/typeck/infer/mod.rs b/src/librustc/middle/typeck/infer/mod.rs index 7fa7daf614901..a11abda8ec529 100644 --- a/src/librustc/middle/typeck/infer/mod.rs +++ b/src/librustc/middle/typeck/infer/mod.rs @@ -698,6 +698,17 @@ impl InferCtxt { } } + // [Note-Type-error-reporting] + // An invariant is that anytime the expected or actual type is ty_err (the special + // error type, meaning that an error occurred when typechecking this expression), + // this is a derived error. The error cascaded from another error (that was already + // reported), so it's not useful to display it to the user. + // The following four methods -- type_error_message_str, type_error_message_str_with_expected, + // type_error_message, and report_mismatched_types -- implement this logic. + // They check if either the actual or expected type is ty_err, and don't print the error + // in this case. The typechecker should only ever report type errors involving mismatched + // types using one of these four methods, and should not call span_err directly for such + // errors. pub fn type_error_message_str(@mut self, sp: span, mk_msg: &fn(Option<~str>, ~str) -> ~str, diff --git a/src/test/compile-fail/pattern-error-continue.rs b/src/test/compile-fail/pattern-error-continue.rs index 14d8b04ade4eb..09e4380f34679 100644 --- a/src/test/compile-fail/pattern-error-continue.rs +++ b/src/test/compile-fail/pattern-error-continue.rs @@ -29,8 +29,9 @@ fn main() { _ => () } match 'c' { - S { _ } => (), //~ ERROR mismatched types: expected `char` but found struct + S { _ } => (), //~ ERROR mismatched types: expected `char` but found a structure pattern + _ => () } f(true); //~ ERROR mismatched types: expected `char` but found `bool` -} \ No newline at end of file +} diff --git a/src/test/compile-fail/struct-pat-derived-error.rs b/src/test/compile-fail/struct-pat-derived-error.rs new file mode 100644 index 0000000000000..cafead3af0e66 --- /dev/null +++ b/src/test/compile-fail/struct-pat-derived-error.rs @@ -0,0 +1,26 @@ +// Copyright 2013 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +struct a { + b: uint, + c: uint +} + +impl a { + fn foo(&self) { + let a { x, y } = self.d; //~ ERROR attempted access of field `d` + //~^ ERROR struct `a` does not have a field named `x` + //~^^ ERROR struct `a` does not have a field named `y` + //~^^^ ERROR pattern does not mention field `b` + //~^^^^ ERROR pattern does not mention field `c` + } +} + +fn main() {}