Skip to content

Commit b87c292

Browse files
committed
be more robust to bogus items in struct patterns/constructors
Fixes #27815
1 parent 7b7fc67 commit b87c292

File tree

6 files changed

+99
-115
lines changed

6 files changed

+99
-115
lines changed

src/librustc_typeck/check/_match.rs

+25-25
Original file line numberDiff line numberDiff line change
@@ -526,41 +526,41 @@ pub fn check_pat_struct<'a, 'tcx>(pcx: &pat_ctxt<'a, 'tcx>, pat: &'tcx ast::Pat,
526526
etc: bool, expected: Ty<'tcx>) {
527527
let fcx = pcx.fcx;
528528
let tcx = pcx.fcx.ccx.tcx;
529+
let report_nonstruct = || {
530+
let name = pprust::path_to_string(path);
531+
span_err!(tcx.sess, pat.span, E0163,
532+
"`{}` does not name a struct or a struct variant", name);
533+
fcx.write_error(pat.id);
534+
535+
for field in fields {
536+
check_pat(pcx, &field.node.pat, tcx.types.err);
537+
}
538+
};
529539

530540
let def = tcx.def_map.borrow().get(&pat.id).unwrap().full_def();
531541
let (adt_def, variant) = match def {
532-
def::DefTrait(_) => {
533-
let name = pprust::path_to_string(path);
534-
span_err!(tcx.sess, pat.span, E0168,
535-
"use of trait `{}` in a struct pattern", name);
536-
fcx.write_error(pat.id);
537-
538-
for field in fields {
539-
check_pat(pcx, &*field.node.pat, tcx.types.err);
540-
}
541-
return;
542-
},
543-
_ => {
544-
let def_type = tcx.lookup_item_type(def.def_id());
545-
match def_type.ty.sty {
542+
def::DefTy(did, _) | def::DefStruct(did) => {
543+
match tcx.lookup_item_type(did).ty.sty {
546544
ty::TyStruct(struct_def, _) =>
547545
(struct_def, struct_def.struct_variant()),
548-
ty::TyEnum(enum_def, _)
549-
if def == def::DefVariant(enum_def.did, def.def_id(), true) =>
550-
(enum_def, enum_def.variant_of_def(def)),
551546
_ => {
552-
let name = pprust::path_to_string(path);
553-
span_err!(tcx.sess, pat.span, E0163,
554-
"`{}` does not name a struct or a struct variant", name);
555-
fcx.write_error(pat.id);
556-
557-
for field in fields {
558-
check_pat(pcx, &*field.node.pat, tcx.types.err);
559-
}
547+
report_nonstruct();
560548
return;
561549
}
562550
}
563551
}
552+
def::DefVariant(eid, vid, true) => {
553+
match tcx.lookup_item_type(vid).ty.sty {
554+
ty::TyEnum(enum_def, _) if enum_def.did == eid => {
555+
(enum_def, enum_def.variant_with_id(vid))
556+
}
557+
_ => tcx.sess.span_bug(pat.span, "variant's type is not its enum")
558+
}
559+
}
560+
_ => {
561+
report_nonstruct();
562+
return;
563+
}
564564
};
565565

566566
instantiate_path(pcx.fcx,

src/librustc_typeck/check/mod.rs

+38-45
Original file line numberDiff line numberDiff line change
@@ -1421,14 +1421,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
14211421
///
14221422
/// This is used when checking the constructor in struct literals.
14231423
fn instantiate_struct_literal_ty(&self,
1424-
did: ast::DefId,
1424+
struct_ty: ty::TypeScheme<'tcx>,
14251425
path: &ast::Path)
14261426
-> TypeAndSubsts<'tcx>
14271427
{
1428-
let tcx = self.tcx();
1429-
1430-
let ty::TypeScheme { generics, ty: decl_ty } =
1431-
tcx.lookup_item_type(did);
1428+
let ty::TypeScheme { generics, ty: decl_ty } = struct_ty;
14321429

14331430
let substs = astconv::ast_path_substs_for_ty(self, self,
14341431
path.span,
@@ -3168,6 +3165,18 @@ fn check_expr_with_unifier<'a, 'tcx, F>(fcx: &FnCtxt<'a, 'tcx>,
31683165
}
31693166
}
31703167

3168+
fn report_exprstruct_on_nondict<'a, 'tcx>(fcx: &FnCtxt<'a,'tcx>,
3169+
id: ast::NodeId,
3170+
fields: &'tcx [ast::Field],
3171+
base_expr: &'tcx Option<P<ast::Expr>>,
3172+
path: &ast::Path)
3173+
{
3174+
span_err!(fcx.tcx().sess, path.span, E0071,
3175+
"`{}` does not name a structure",
3176+
pprust::path_to_string(path));
3177+
check_struct_fields_on_error(fcx, id, fields, base_expr)
3178+
}
3179+
31713180
type ExprCheckerWithTy = fn(&FnCtxt, &ast::Expr, Ty);
31723181

31733182
let tcx = fcx.ccx.tcx;
@@ -3618,7 +3627,8 @@ fn check_expr_with_unifier<'a, 'tcx, F>(fcx: &FnCtxt<'a, 'tcx>,
36183627
ast::ExprStruct(ref path, ref fields, ref base_expr) => {
36193628
// Resolve the path.
36203629
let def = lookup_full_def(tcx, path.span, id);
3621-
let struct_id = match def {
3630+
3631+
let struct_ty = match def {
36223632
def::DefVariant(enum_id, variant_id, true) => {
36233633
if let &Some(ref base_expr) = base_expr {
36243634
span_err!(tcx.sess, base_expr.span, E0436,
@@ -3627,54 +3637,38 @@ fn check_expr_with_unifier<'a, 'tcx, F>(fcx: &FnCtxt<'a, 'tcx>,
36273637
}
36283638
check_struct_enum_variant(fcx, id, expr.span, enum_id,
36293639
variant_id, &fields[..]);
3630-
enum_id
3640+
Some(tcx.lookup_item_type(enum_id))
36313641
}
3632-
def::DefTrait(def_id) => {
3633-
span_err!(tcx.sess, path.span, E0159,
3634-
"use of trait `{}` as a struct constructor",
3635-
pprust::path_to_string(path));
3636-
check_struct_fields_on_error(fcx,
3637-
id,
3638-
&fields[..],
3639-
base_expr);
3640-
def_id
3641-
},
3642-
def => {
3642+
def::DefTy(did, _) | def::DefStruct(did) => {
36433643
// Verify that this was actually a struct.
3644-
let typ = fcx.ccx.tcx.lookup_item_type(def.def_id());
3645-
match typ.ty.sty {
3646-
ty::TyStruct(struct_def, _) => {
3647-
check_struct_constructor(fcx,
3648-
id,
3649-
expr.span,
3650-
struct_def,
3651-
&fields[..],
3652-
base_expr.as_ref().map(|e| &**e));
3653-
}
3654-
_ => {
3655-
span_err!(tcx.sess, path.span, E0071,
3656-
"`{}` does not name a structure",
3657-
pprust::path_to_string(path));
3658-
check_struct_fields_on_error(fcx,
3659-
id,
3660-
&fields[..],
3661-
base_expr);
3662-
}
3644+
let typ = tcx.lookup_item_type(did);
3645+
if let ty::TyStruct(struct_def, _) = typ.ty.sty {
3646+
check_struct_constructor(fcx,
3647+
id,
3648+
expr.span,
3649+
struct_def,
3650+
&fields,
3651+
base_expr.as_ref().map(|e| &**e));
3652+
} else {
3653+
report_exprstruct_on_nondict(fcx, id, &fields, base_expr, path);
36633654
}
3664-
3665-
def.def_id()
3655+
Some(typ)
3656+
}
3657+
_ => {
3658+
report_exprstruct_on_nondict(fcx, id, &fields, base_expr, path);
3659+
None
36663660
}
36673661
};
36683662

36693663
// Turn the path into a type and verify that that type unifies with
36703664
// the resulting structure type. This is needed to handle type
36713665
// parameters correctly.
3672-
let actual_structure_type = fcx.expr_ty(&*expr);
3673-
if !actual_structure_type.references_error() {
3674-
let type_and_substs = fcx.instantiate_struct_literal_ty(struct_id, path);
3666+
if let Some(struct_ty) = struct_ty {
3667+
let expr_ty = fcx.expr_ty(&expr);
3668+
let type_and_substs = fcx.instantiate_struct_literal_ty(struct_ty, path);
36753669
match fcx.mk_subty(false,
36763670
infer::Misc(path.span),
3677-
actual_structure_type,
3671+
expr_ty,
36783672
type_and_substs.ty) {
36793673
Ok(()) => {}
36803674
Err(type_error) => {
@@ -3685,8 +3679,7 @@ fn check_expr_with_unifier<'a, 'tcx, F>(fcx: &FnCtxt<'a, 'tcx>,
36853679
fcx.infcx()
36863680
.ty_to_string(type_and_substs.ty),
36873681
fcx.infcx()
3688-
.ty_to_string(
3689-
actual_structure_type),
3682+
.ty_to_string(expr_ty),
36903683
type_error);
36913684
tcx.note_and_explain_type_err(&type_error, path.span);
36923685
}

src/librustc_typeck/diagnostics.rs

+14-43
Original file line numberDiff line numberDiff line change
@@ -735,39 +735,33 @@ fn some_func(x: &mut i32) {
735735
"##,
736736

737737
E0071: r##"
738-
You tried to use a structure initialization with a non-structure type.
738+
You tried to use structure-literal syntax to create an item that is
739+
not a struct-style structure or enum variant.
740+
739741
Example of erroneous code:
740742
741743
```
742-
enum Foo { FirstValue };
744+
enum Foo { FirstValue(i32) };
743745
744746
let u = Foo::FirstValue { value: 0i32 }; // error: Foo::FirstValue
745747
// isn't a structure!
746-
// or even simpler, if the structure wasn't defined at all:
747-
let u = RandomName { random_field: 0i32 }; // error: RandomName
748-
// isn't a structure!
749-
```
748+
// or even simpler, if the name doesn't refer to a structure at all.
749+
let t = u32 { value: 4 }; // error: `u32` does not name a structure.```
750750
751-
To fix this, please check:
752-
* Did you spell it right?
753-
* Did you accidentaly used an enum as a struct?
754-
* Did you accidentaly make an enum when you intended to use a struct?
751+
To fix this, ensure that the name was correctly spelled, and that
752+
the correct form of initializer was used.
755753
756-
Here is the previous code with all missing information:
754+
For example, the code above can be fixed to:
757755
758756
```
759-
struct Inner {
760-
value: i32
761-
}
762-
763757
enum Foo {
764-
FirstValue(Inner)
758+
FirstValue(i32)
765759
}
766760
767761
fn main() {
768-
let u = Foo::FirstValue(Inner { value: 0i32 });
762+
let u = Foo::FirstValue(0i32);
769763
770-
let t = Inner { value: 0i32 };
764+
let t = 4;
771765
}
772766
```
773767
"##,
@@ -1636,30 +1630,6 @@ fn(isize, *const *const u8) -> isize
16361630
```
16371631
"##,
16381632

1639-
E0159: r##"
1640-
You tried to use a trait as a struct constructor. Erroneous code example:
1641-
1642-
```
1643-
trait TraitNotAStruct {}
1644-
1645-
TraitNotAStruct{ value: 0 }; // error: use of trait `TraitNotAStruct` as a
1646-
// struct constructor
1647-
```
1648-
1649-
Please verify you used the correct type name or please implement the trait
1650-
on a struct and use this struct constructor. Example:
1651-
1652-
```
1653-
trait TraitNotAStruct {}
1654-
1655-
struct Foo {
1656-
value: i32
1657-
}
1658-
1659-
Foo{ value: 0 }; // ok!
1660-
```
1661-
"##,
1662-
16631633
E0166: r##"
16641634
This error means that the compiler found a return expression in a function
16651635
marked as diverging. A function diverges if it has `!` in the place of the
@@ -2673,10 +2643,11 @@ register_diagnostics! {
26732643
E0127,
26742644
E0129,
26752645
E0141,
2646+
// E0159, // use of trait `{}` as struct constructor
26762647
E0163,
26772648
E0164,
26782649
E0167,
2679-
E0168,
2650+
// E0168,
26802651
E0173, // manual implementations of unboxed closure traits are experimental
26812652
E0174, // explicit use of unboxed closure methods are experimental
26822653
E0182,

src/test/compile-fail-fulldeps/issue-18986.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,6 @@ pub use use_from_trait_xc::Trait;
1515

1616
fn main() {
1717
match () {
18-
Trait { x: 42 } => () //~ ERROR use of trait `Trait` in a struct pattern
18+
Trait { x: 42 } => () //~ ERROR `Trait` does not name a struct
1919
}
2020
}

src/test/compile-fail/issue-27815.rs

+20
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
mod A {}
12+
13+
fn main() {
14+
let u = A { x: 1 }; //~ ERROR `A` does not name a structure
15+
let v = u32 { x: 1 }; //~ ERROR `u32` does not name a structure
16+
match () {
17+
A { x: 1 } => {} //~ ERROR `A` does not name a struct
18+
u32 { x: 1 } => {} //~ ERROR `u32` does not name a struct
19+
}
20+
}

src/test/compile-fail/trait-as-struct-constructor.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -12,5 +12,5 @@ trait TraitNotAStruct {}
1212

1313
fn main() {
1414
TraitNotAStruct{ value: 0 };
15-
//~^ ERROR: use of trait `TraitNotAStruct` as a struct constructor [E0159]
15+
//~^ ERROR: `TraitNotAStruct` does not name a structure [E0071]
1616
}

0 commit comments

Comments
 (0)