be more robust to bogus items in struct patterns/constructors
Fixes #27815
This commit is contained in:
parent
7b7fc67dd4
commit
b87c292627
@ -526,41 +526,41 @@ pub fn check_pat_struct<'a, 'tcx>(pcx: &pat_ctxt<'a, 'tcx>, pat: &'tcx ast::Pat,
|
||||
etc: bool, expected: Ty<'tcx>) {
|
||||
let fcx = pcx.fcx;
|
||||
let tcx = pcx.fcx.ccx.tcx;
|
||||
let report_nonstruct = || {
|
||||
let name = pprust::path_to_string(path);
|
||||
span_err!(tcx.sess, pat.span, E0163,
|
||||
"`{}` does not name a struct or a struct variant", name);
|
||||
fcx.write_error(pat.id);
|
||||
|
||||
for field in fields {
|
||||
check_pat(pcx, &field.node.pat, tcx.types.err);
|
||||
}
|
||||
};
|
||||
|
||||
let def = tcx.def_map.borrow().get(&pat.id).unwrap().full_def();
|
||||
let (adt_def, variant) = match def {
|
||||
def::DefTrait(_) => {
|
||||
let name = pprust::path_to_string(path);
|
||||
span_err!(tcx.sess, pat.span, E0168,
|
||||
"use of trait `{}` in a struct pattern", name);
|
||||
fcx.write_error(pat.id);
|
||||
|
||||
for field in fields {
|
||||
check_pat(pcx, &*field.node.pat, tcx.types.err);
|
||||
}
|
||||
return;
|
||||
},
|
||||
_ => {
|
||||
let def_type = tcx.lookup_item_type(def.def_id());
|
||||
match def_type.ty.sty {
|
||||
def::DefTy(did, _) | def::DefStruct(did) => {
|
||||
match tcx.lookup_item_type(did).ty.sty {
|
||||
ty::TyStruct(struct_def, _) =>
|
||||
(struct_def, struct_def.struct_variant()),
|
||||
ty::TyEnum(enum_def, _)
|
||||
if def == def::DefVariant(enum_def.did, def.def_id(), true) =>
|
||||
(enum_def, enum_def.variant_of_def(def)),
|
||||
_ => {
|
||||
let name = pprust::path_to_string(path);
|
||||
span_err!(tcx.sess, pat.span, E0163,
|
||||
"`{}` does not name a struct or a struct variant", name);
|
||||
fcx.write_error(pat.id);
|
||||
|
||||
for field in fields {
|
||||
check_pat(pcx, &*field.node.pat, tcx.types.err);
|
||||
}
|
||||
report_nonstruct();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
def::DefVariant(eid, vid, true) => {
|
||||
match tcx.lookup_item_type(vid).ty.sty {
|
||||
ty::TyEnum(enum_def, _) if enum_def.did == eid => {
|
||||
(enum_def, enum_def.variant_with_id(vid))
|
||||
}
|
||||
_ => tcx.sess.span_bug(pat.span, "variant's type is not its enum")
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
report_nonstruct();
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
instantiate_path(pcx.fcx,
|
||||
|
@ -1421,14 +1421,11 @@ pub fn instantiate_type(&self,
|
||||
///
|
||||
/// This is used when checking the constructor in struct literals.
|
||||
fn instantiate_struct_literal_ty(&self,
|
||||
did: ast::DefId,
|
||||
struct_ty: ty::TypeScheme<'tcx>,
|
||||
path: &ast::Path)
|
||||
-> TypeAndSubsts<'tcx>
|
||||
{
|
||||
let tcx = self.tcx();
|
||||
|
||||
let ty::TypeScheme { generics, ty: decl_ty } =
|
||||
tcx.lookup_item_type(did);
|
||||
let ty::TypeScheme { generics, ty: decl_ty } = struct_ty;
|
||||
|
||||
let substs = astconv::ast_path_substs_for_ty(self, self,
|
||||
path.span,
|
||||
@ -3168,6 +3165,18 @@ fn check_struct_fields_on_error<'a,'tcx>(fcx: &FnCtxt<'a,'tcx>,
|
||||
}
|
||||
}
|
||||
|
||||
fn report_exprstruct_on_nondict<'a, 'tcx>(fcx: &FnCtxt<'a,'tcx>,
|
||||
id: ast::NodeId,
|
||||
fields: &'tcx [ast::Field],
|
||||
base_expr: &'tcx Option<P<ast::Expr>>,
|
||||
path: &ast::Path)
|
||||
{
|
||||
span_err!(fcx.tcx().sess, path.span, E0071,
|
||||
"`{}` does not name a structure",
|
||||
pprust::path_to_string(path));
|
||||
check_struct_fields_on_error(fcx, id, fields, base_expr)
|
||||
}
|
||||
|
||||
type ExprCheckerWithTy = fn(&FnCtxt, &ast::Expr, Ty);
|
||||
|
||||
let tcx = fcx.ccx.tcx;
|
||||
@ -3618,7 +3627,8 @@ fn check_struct_fields_on_error<'a,'tcx>(fcx: &FnCtxt<'a,'tcx>,
|
||||
ast::ExprStruct(ref path, ref fields, ref base_expr) => {
|
||||
// Resolve the path.
|
||||
let def = lookup_full_def(tcx, path.span, id);
|
||||
let struct_id = match def {
|
||||
|
||||
let struct_ty = match def {
|
||||
def::DefVariant(enum_id, variant_id, true) => {
|
||||
if let &Some(ref base_expr) = base_expr {
|
||||
span_err!(tcx.sess, base_expr.span, E0436,
|
||||
@ -3627,54 +3637,38 @@ fn check_struct_fields_on_error<'a,'tcx>(fcx: &FnCtxt<'a,'tcx>,
|
||||
}
|
||||
check_struct_enum_variant(fcx, id, expr.span, enum_id,
|
||||
variant_id, &fields[..]);
|
||||
enum_id
|
||||
Some(tcx.lookup_item_type(enum_id))
|
||||
}
|
||||
def::DefTrait(def_id) => {
|
||||
span_err!(tcx.sess, path.span, E0159,
|
||||
"use of trait `{}` as a struct constructor",
|
||||
pprust::path_to_string(path));
|
||||
check_struct_fields_on_error(fcx,
|
||||
id,
|
||||
&fields[..],
|
||||
base_expr);
|
||||
def_id
|
||||
},
|
||||
def => {
|
||||
def::DefTy(did, _) | def::DefStruct(did) => {
|
||||
// Verify that this was actually a struct.
|
||||
let typ = fcx.ccx.tcx.lookup_item_type(def.def_id());
|
||||
match typ.ty.sty {
|
||||
ty::TyStruct(struct_def, _) => {
|
||||
check_struct_constructor(fcx,
|
||||
id,
|
||||
expr.span,
|
||||
struct_def,
|
||||
&fields[..],
|
||||
base_expr.as_ref().map(|e| &**e));
|
||||
}
|
||||
_ => {
|
||||
span_err!(tcx.sess, path.span, E0071,
|
||||
"`{}` does not name a structure",
|
||||
pprust::path_to_string(path));
|
||||
check_struct_fields_on_error(fcx,
|
||||
id,
|
||||
&fields[..],
|
||||
base_expr);
|
||||
}
|
||||
let typ = tcx.lookup_item_type(did);
|
||||
if let ty::TyStruct(struct_def, _) = typ.ty.sty {
|
||||
check_struct_constructor(fcx,
|
||||
id,
|
||||
expr.span,
|
||||
struct_def,
|
||||
&fields,
|
||||
base_expr.as_ref().map(|e| &**e));
|
||||
} else {
|
||||
report_exprstruct_on_nondict(fcx, id, &fields, base_expr, path);
|
||||
}
|
||||
|
||||
def.def_id()
|
||||
Some(typ)
|
||||
}
|
||||
_ => {
|
||||
report_exprstruct_on_nondict(fcx, id, &fields, base_expr, path);
|
||||
None
|
||||
}
|
||||
};
|
||||
|
||||
// Turn the path into a type and verify that that type unifies with
|
||||
// the resulting structure type. This is needed to handle type
|
||||
// parameters correctly.
|
||||
let actual_structure_type = fcx.expr_ty(&*expr);
|
||||
if !actual_structure_type.references_error() {
|
||||
let type_and_substs = fcx.instantiate_struct_literal_ty(struct_id, path);
|
||||
if let Some(struct_ty) = struct_ty {
|
||||
let expr_ty = fcx.expr_ty(&expr);
|
||||
let type_and_substs = fcx.instantiate_struct_literal_ty(struct_ty, path);
|
||||
match fcx.mk_subty(false,
|
||||
infer::Misc(path.span),
|
||||
actual_structure_type,
|
||||
expr_ty,
|
||||
type_and_substs.ty) {
|
||||
Ok(()) => {}
|
||||
Err(type_error) => {
|
||||
@ -3685,8 +3679,7 @@ fn check_struct_fields_on_error<'a,'tcx>(fcx: &FnCtxt<'a,'tcx>,
|
||||
fcx.infcx()
|
||||
.ty_to_string(type_and_substs.ty),
|
||||
fcx.infcx()
|
||||
.ty_to_string(
|
||||
actual_structure_type),
|
||||
.ty_to_string(expr_ty),
|
||||
type_error);
|
||||
tcx.note_and_explain_type_err(&type_error, path.span);
|
||||
}
|
||||
|
@ -735,39 +735,33 @@ fn some_func(x: &mut i32) {
|
||||
"##,
|
||||
|
||||
E0071: r##"
|
||||
You tried to use a structure initialization with a non-structure type.
|
||||
You tried to use structure-literal syntax to create an item that is
|
||||
not a struct-style structure or enum variant.
|
||||
|
||||
Example of erroneous code:
|
||||
|
||||
```
|
||||
enum Foo { FirstValue };
|
||||
enum Foo { FirstValue(i32) };
|
||||
|
||||
let u = Foo::FirstValue { value: 0i32 }; // error: Foo::FirstValue
|
||||
// isn't a structure!
|
||||
// or even simpler, if the structure wasn't defined at all:
|
||||
let u = RandomName { random_field: 0i32 }; // error: RandomName
|
||||
// isn't a structure!
|
||||
```
|
||||
// or even simpler, if the name doesn't refer to a structure at all.
|
||||
let t = u32 { value: 4 }; // error: `u32` does not name a structure.```
|
||||
|
||||
To fix this, please check:
|
||||
* Did you spell it right?
|
||||
* Did you accidentaly used an enum as a struct?
|
||||
* Did you accidentaly make an enum when you intended to use a struct?
|
||||
To fix this, ensure that the name was correctly spelled, and that
|
||||
the correct form of initializer was used.
|
||||
|
||||
Here is the previous code with all missing information:
|
||||
For example, the code above can be fixed to:
|
||||
|
||||
```
|
||||
struct Inner {
|
||||
value: i32
|
||||
}
|
||||
|
||||
enum Foo {
|
||||
FirstValue(Inner)
|
||||
FirstValue(i32)
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let u = Foo::FirstValue(Inner { value: 0i32 });
|
||||
let u = Foo::FirstValue(0i32);
|
||||
|
||||
let t = Inner { value: 0i32 };
|
||||
let t = 4;
|
||||
}
|
||||
```
|
||||
"##,
|
||||
@ -1636,30 +1630,6 @@ struct SomeStruct {
|
||||
```
|
||||
"##,
|
||||
|
||||
E0159: r##"
|
||||
You tried to use a trait as a struct constructor. Erroneous code example:
|
||||
|
||||
```
|
||||
trait TraitNotAStruct {}
|
||||
|
||||
TraitNotAStruct{ value: 0 }; // error: use of trait `TraitNotAStruct` as a
|
||||
// struct constructor
|
||||
```
|
||||
|
||||
Please verify you used the correct type name or please implement the trait
|
||||
on a struct and use this struct constructor. Example:
|
||||
|
||||
```
|
||||
trait TraitNotAStruct {}
|
||||
|
||||
struct Foo {
|
||||
value: i32
|
||||
}
|
||||
|
||||
Foo{ value: 0 }; // ok!
|
||||
```
|
||||
"##,
|
||||
|
||||
E0166: r##"
|
||||
This error means that the compiler found a return expression in a function
|
||||
marked as diverging. A function diverges if it has `!` in the place of the
|
||||
@ -2673,10 +2643,11 @@ struct Foo<'a, T: 'a> {
|
||||
E0127,
|
||||
E0129,
|
||||
E0141,
|
||||
// E0159, // use of trait `{}` as struct constructor
|
||||
E0163,
|
||||
E0164,
|
||||
E0167,
|
||||
E0168,
|
||||
// E0168,
|
||||
E0173, // manual implementations of unboxed closure traits are experimental
|
||||
E0174, // explicit use of unboxed closure methods are experimental
|
||||
E0182,
|
||||
|
@ -15,6 +15,6 @@
|
||||
|
||||
fn main() {
|
||||
match () {
|
||||
Trait { x: 42 } => () //~ ERROR use of trait `Trait` in a struct pattern
|
||||
Trait { x: 42 } => () //~ ERROR `Trait` does not name a struct
|
||||
}
|
||||
}
|
||||
|
20
src/test/compile-fail/issue-27815.rs
Normal file
20
src/test/compile-fail/issue-27815.rs
Normal file
@ -0,0 +1,20 @@
|
||||
// Copyright 2015 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 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
mod A {}
|
||||
|
||||
fn main() {
|
||||
let u = A { x: 1 }; //~ ERROR `A` does not name a structure
|
||||
let v = u32 { x: 1 }; //~ ERROR `u32` does not name a structure
|
||||
match () {
|
||||
A { x: 1 } => {} //~ ERROR `A` does not name a struct
|
||||
u32 { x: 1 } => {} //~ ERROR `u32` does not name a struct
|
||||
}
|
||||
}
|
@ -12,5 +12,5 @@ trait TraitNotAStruct {}
|
||||
|
||||
fn main() {
|
||||
TraitNotAStruct{ value: 0 };
|
||||
//~^ ERROR: use of trait `TraitNotAStruct` as a struct constructor [E0159]
|
||||
//~^ ERROR: `TraitNotAStruct` does not name a structure [E0071]
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user