Auto merge of #48082 - jseyfried:improve_struct_field_hygiene, r=petrochenkov
macros: improve struct constructor field hygiene, fix span bug Fixes #47311. r? @nrc
This commit is contained in:
commit
4a70e27219
15 changed files with 62 additions and 24 deletions
|
@ -34,6 +34,7 @@ use rustc::util::nodemap::NodeSet;
|
||||||
use syntax::ast::{self, CRATE_NODE_ID, Ident};
|
use syntax::ast::{self, CRATE_NODE_ID, Ident};
|
||||||
use syntax::symbol::keywords;
|
use syntax::symbol::keywords;
|
||||||
use syntax_pos::Span;
|
use syntax_pos::Span;
|
||||||
|
use syntax_pos::hygiene::SyntaxContext;
|
||||||
|
|
||||||
use std::cmp;
|
use std::cmp;
|
||||||
use std::mem::replace;
|
use std::mem::replace;
|
||||||
|
@ -491,9 +492,13 @@ struct NamePrivacyVisitor<'a, 'tcx: 'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, 'tcx> NamePrivacyVisitor<'a, 'tcx> {
|
impl<'a, 'tcx> NamePrivacyVisitor<'a, 'tcx> {
|
||||||
// Checks that a field is accessible.
|
// Checks that a field in a struct constructor (expression or pattern) is accessible.
|
||||||
fn check_field(&mut self, span: Span, def: &'tcx ty::AdtDef, field: &'tcx ty::FieldDef) {
|
fn check_field(&mut self,
|
||||||
let ident = Ident { ctxt: span.ctxt().modern(), ..keywords::Invalid.ident() };
|
use_ctxt: SyntaxContext, // Syntax context of the field name at the use site
|
||||||
|
span: Span, // Span of the field pattern, e.g. `x: 0`
|
||||||
|
def: &'tcx ty::AdtDef, // Definition of the struct or enum
|
||||||
|
field: &'tcx ty::FieldDef) { // Definition of the field
|
||||||
|
let ident = Ident { ctxt: use_ctxt.modern(), ..keywords::Invalid.ident() };
|
||||||
let def_id = self.tcx.adjust_ident(ident, def.did, self.current_item).1;
|
let def_id = self.tcx.adjust_ident(ident, def.did, self.current_item).1;
|
||||||
if !def.is_enum() && !field.vis.is_accessible_from(def_id, self.tcx) {
|
if !def.is_enum() && !field.vis.is_accessible_from(def_id, self.tcx) {
|
||||||
struct_span_err!(self.tcx.sess, span, E0451, "field `{}` of {} `{}` is private",
|
struct_span_err!(self.tcx.sess, span, E0451, "field `{}` of {} `{}` is private",
|
||||||
|
@ -566,12 +571,17 @@ impl<'a, 'tcx> Visitor<'tcx> for NamePrivacyVisitor<'a, 'tcx> {
|
||||||
// unmentioned fields, just check them all.
|
// unmentioned fields, just check them all.
|
||||||
for variant_field in &variant.fields {
|
for variant_field in &variant.fields {
|
||||||
let field = fields.iter().find(|f| f.name.node == variant_field.name);
|
let field = fields.iter().find(|f| f.name.node == variant_field.name);
|
||||||
let span = if let Some(f) = field { f.span } else { base.span };
|
let (use_ctxt, span) = match field {
|
||||||
self.check_field(span, adt, variant_field);
|
Some(field) => (field.name.node.to_ident().ctxt, field.span),
|
||||||
|
None => (base.span.ctxt(), base.span),
|
||||||
|
};
|
||||||
|
self.check_field(use_ctxt, span, adt, variant_field);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
for field in fields {
|
for field in fields {
|
||||||
self.check_field(field.span, adt, variant.field_named(field.name.node));
|
let use_ctxt = field.name.node.to_ident().ctxt;
|
||||||
|
let field_def = variant.field_named(field.name.node);
|
||||||
|
self.check_field(use_ctxt, field.span, adt, field_def);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -588,7 +598,9 @@ impl<'a, 'tcx> Visitor<'tcx> for NamePrivacyVisitor<'a, 'tcx> {
|
||||||
let adt = self.tables.pat_ty(pat).ty_adt_def().unwrap();
|
let adt = self.tables.pat_ty(pat).ty_adt_def().unwrap();
|
||||||
let variant = adt.variant_of_def(def);
|
let variant = adt.variant_of_def(def);
|
||||||
for field in fields {
|
for field in fields {
|
||||||
self.check_field(field.span, adt, variant.field_named(field.node.name));
|
let use_ctxt = field.node.name.to_ident().ctxt;
|
||||||
|
let field_def = variant.field_named(field.node.name);
|
||||||
|
self.check_field(use_ctxt, field.span, adt, field_def);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
|
|
|
@ -2125,8 +2125,8 @@ impl<'a> Parser<'a> {
|
||||||
// Check if a colon exists one ahead. This means we're parsing a fieldname.
|
// Check if a colon exists one ahead. This means we're parsing a fieldname.
|
||||||
let (fieldname, expr, is_shorthand) = if self.look_ahead(1, |t| t == &token::Colon) {
|
let (fieldname, expr, is_shorthand) = if self.look_ahead(1, |t| t == &token::Colon) {
|
||||||
let fieldname = self.parse_field_name()?;
|
let fieldname = self.parse_field_name()?;
|
||||||
self.bump();
|
|
||||||
hi = self.prev_span;
|
hi = self.prev_span;
|
||||||
|
self.bump();
|
||||||
(fieldname, self.parse_expr()?, false)
|
(fieldname, self.parse_expr()?, false)
|
||||||
} else {
|
} else {
|
||||||
let fieldname = self.parse_ident_common(false)?;
|
let fieldname = self.parse_ident_common(false)?;
|
||||||
|
|
26
src/test/run-pass/hygiene/issue-47311.rs
Normal file
26
src/test/run-pass/hygiene/issue-47311.rs
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
// Copyright 2018 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.
|
||||||
|
|
||||||
|
// ignore-pretty pretty-printing is unhygienic
|
||||||
|
|
||||||
|
#![feature(decl_macro)]
|
||||||
|
#![allow(unused)]
|
||||||
|
|
||||||
|
macro m($S:ident, $x:ident) {
|
||||||
|
$S { $x: 0 }
|
||||||
|
}
|
||||||
|
|
||||||
|
mod foo {
|
||||||
|
struct S { x: i32 }
|
||||||
|
|
||||||
|
fn f() { ::m!(S, x); }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {}
|
|
@ -2,13 +2,13 @@ error[E0560]: struct `submodule::Demo` has no field named `inocently_mispellable
|
||||||
--> $DIR/issue-42599_available_fields_note.rs:26:39
|
--> $DIR/issue-42599_available_fields_note.rs:26:39
|
||||||
|
|
|
|
||||||
26 | Self { secret_integer: 2, inocently_mispellable: () }
|
26 | Self { secret_integer: 2, inocently_mispellable: () }
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^ field does not exist - did you mean `innocently_misspellable`?
|
| ^^^^^^^^^^^^^^^^^^^^^ field does not exist - did you mean `innocently_misspellable`?
|
||||||
|
|
||||||
error[E0560]: struct `submodule::Demo` has no field named `egregiously_nonexistent_field`
|
error[E0560]: struct `submodule::Demo` has no field named `egregiously_nonexistent_field`
|
||||||
--> $DIR/issue-42599_available_fields_note.rs:31:39
|
--> $DIR/issue-42599_available_fields_note.rs:31:39
|
||||||
|
|
|
|
||||||
31 | Self { secret_integer: 3, egregiously_nonexistent_field: () }
|
31 | Self { secret_integer: 3, egregiously_nonexistent_field: () }
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `submodule::Demo` does not have this field
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `submodule::Demo` does not have this field
|
||||||
|
|
|
|
||||||
= note: available fields are: `favorite_integer`, `secret_integer`, `innocently_misspellable`, `another_field`, `yet_another_field` ... and 2 others
|
= note: available fields are: `favorite_integer`, `secret_integer`, `innocently_misspellable`, `another_field`, `yet_another_field` ... and 2 others
|
||||||
|
|
||||||
|
|
|
@ -4,7 +4,7 @@ error[E0062]: field `x` specified more than once
|
||||||
17 | x: 0,
|
17 | x: 0,
|
||||||
| ---- first use of `x`
|
| ---- first use of `x`
|
||||||
18 | x: 0,
|
18 | x: 0,
|
||||||
| ^^ used more than once
|
| ^ used more than once
|
||||||
|
|
||||||
error: aborting due to previous error
|
error: aborting due to previous error
|
||||||
|
|
||||||
|
|
|
@ -2,7 +2,7 @@ error[E0559]: variant `Field::Fool` has no field named `joke`
|
||||||
--> $DIR/E0559.rs:16:27
|
--> $DIR/E0559.rs:16:27
|
||||||
|
|
|
|
||||||
16 | let s = Field::Fool { joke: 0 };
|
16 | let s = Field::Fool { joke: 0 };
|
||||||
| ^^^^^ `Field::Fool` does not have this field
|
| ^^^^ `Field::Fool` does not have this field
|
||||||
|
|
|
|
||||||
= note: available fields are: `x`
|
= note: available fields are: `x`
|
||||||
|
|
||||||
|
|
|
@ -2,7 +2,7 @@ error[E0560]: struct `Simba` has no field named `father`
|
||||||
--> $DIR/E0560.rs:16:32
|
--> $DIR/E0560.rs:16:32
|
||||||
|
|
|
|
||||||
16 | let s = Simba { mother: 1, father: 0 };
|
16 | let s = Simba { mother: 1, father: 0 };
|
||||||
| ^^^^^^^ `Simba` does not have this field
|
| ^^^^^^ `Simba` does not have this field
|
||||||
|
|
|
|
||||||
= note: available fields are: `mother`
|
= note: available fields are: `mother`
|
||||||
|
|
||||||
|
|
|
@ -2,7 +2,7 @@ error[E0559]: variant `Homura::Akemi` has no field named `kaname`
|
||||||
--> $DIR/issue-19922.rs:16:34
|
--> $DIR/issue-19922.rs:16:34
|
||||||
|
|
|
|
||||||
16 | let homura = Homura::Akemi { kaname: () };
|
16 | let homura = Homura::Akemi { kaname: () };
|
||||||
| ^^^^^^^ `Homura::Akemi` does not have this field
|
| ^^^^^^ `Homura::Akemi` does not have this field
|
||||||
|
|
|
|
||||||
= note: available fields are: `madoka`
|
= note: available fields are: `madoka`
|
||||||
|
|
||||||
|
|
|
@ -2,7 +2,7 @@ error[E0560]: struct `S` has no field named `0b1`
|
||||||
--> $DIR/numeric-fields.rs:14:15
|
--> $DIR/numeric-fields.rs:14:15
|
||||||
|
|
|
|
||||||
14 | let s = S{0b1: 10, 0: 11};
|
14 | let s = S{0b1: 10, 0: 11};
|
||||||
| ^^^^ `S` does not have this field
|
| ^^^ `S` does not have this field
|
||||||
|
|
|
|
||||||
= note: available fields are: `0`, `1`
|
= note: available fields are: `0`, `1`
|
||||||
|
|
||||||
|
|
|
@ -2,7 +2,7 @@ error[E0560]: struct `A` has no field named `bar`
|
||||||
--> $DIR/struct-fields-hints-no-dupe.rs:20:9
|
--> $DIR/struct-fields-hints-no-dupe.rs:20:9
|
||||||
|
|
|
|
||||||
20 | bar : 42,
|
20 | bar : 42,
|
||||||
| ^^^^^ field does not exist - did you mean `barr`?
|
| ^^^ field does not exist - did you mean `barr`?
|
||||||
|
|
||||||
error: aborting due to previous error
|
error: aborting due to previous error
|
||||||
|
|
||||||
|
|
|
@ -2,7 +2,7 @@ error[E0560]: struct `A` has no field named `bar`
|
||||||
--> $DIR/struct-fields-hints.rs:20:9
|
--> $DIR/struct-fields-hints.rs:20:9
|
||||||
|
|
|
|
||||||
20 | bar : 42,
|
20 | bar : 42,
|
||||||
| ^^^^^ field does not exist - did you mean `car`?
|
| ^^^ field does not exist - did you mean `car`?
|
||||||
|
|
||||||
error: aborting due to previous error
|
error: aborting due to previous error
|
||||||
|
|
||||||
|
|
|
@ -2,7 +2,7 @@ error[E0560]: struct `BuildData` has no field named `bar`
|
||||||
--> $DIR/struct-fields-too-many.rs:18:9
|
--> $DIR/struct-fields-too-many.rs:18:9
|
||||||
|
|
|
|
||||||
18 | bar: 0
|
18 | bar: 0
|
||||||
| ^^^^ `BuildData` does not have this field
|
| ^^^ `BuildData` does not have this field
|
||||||
|
|
|
|
||||||
= note: available fields are: `foo`
|
= note: available fields are: `foo`
|
||||||
|
|
||||||
|
|
|
@ -2,13 +2,13 @@ error[E0560]: struct `xc::B` has no field named `aa`
|
||||||
--> $DIR/suggest-private-fields.rs:25:9
|
--> $DIR/suggest-private-fields.rs:25:9
|
||||||
|
|
|
|
||||||
25 | aa: 20,
|
25 | aa: 20,
|
||||||
| ^^^ field does not exist - did you mean `a`?
|
| ^^ field does not exist - did you mean `a`?
|
||||||
|
|
||||||
error[E0560]: struct `xc::B` has no field named `bb`
|
error[E0560]: struct `xc::B` has no field named `bb`
|
||||||
--> $DIR/suggest-private-fields.rs:27:9
|
--> $DIR/suggest-private-fields.rs:27:9
|
||||||
|
|
|
|
||||||
27 | bb: 20,
|
27 | bb: 20,
|
||||||
| ^^^ `xc::B` does not have this field
|
| ^^ `xc::B` does not have this field
|
||||||
|
|
|
|
||||||
= note: available fields are: `a`
|
= note: available fields are: `a`
|
||||||
|
|
||||||
|
@ -16,13 +16,13 @@ error[E0560]: struct `A` has no field named `aa`
|
||||||
--> $DIR/suggest-private-fields.rs:32:9
|
--> $DIR/suggest-private-fields.rs:32:9
|
||||||
|
|
|
|
||||||
32 | aa: 20,
|
32 | aa: 20,
|
||||||
| ^^^ field does not exist - did you mean `a`?
|
| ^^ field does not exist - did you mean `a`?
|
||||||
|
|
||||||
error[E0560]: struct `A` has no field named `bb`
|
error[E0560]: struct `A` has no field named `bb`
|
||||||
--> $DIR/suggest-private-fields.rs:34:9
|
--> $DIR/suggest-private-fields.rs:34:9
|
||||||
|
|
|
|
||||||
34 | bb: 20,
|
34 | bb: 20,
|
||||||
| ^^^ field does not exist - did you mean `b`?
|
| ^^ field does not exist - did you mean `b`?
|
||||||
|
|
||||||
error: aborting due to 4 previous errors
|
error: aborting due to 4 previous errors
|
||||||
|
|
||||||
|
|
|
@ -14,7 +14,7 @@ error[E0560]: union `U` has no field named `c`
|
||||||
--> $DIR/union-fields-2.rs:20:29
|
--> $DIR/union-fields-2.rs:20:29
|
||||||
|
|
|
|
||||||
20 | let u = U { a: 0, b: 1, c: 2 }; //~ ERROR union expressions should have exactly one field
|
20 | let u = U { a: 0, b: 1, c: 2 }; //~ ERROR union expressions should have exactly one field
|
||||||
| ^^ `U` does not have this field
|
| ^ `U` does not have this field
|
||||||
|
|
|
|
||||||
= note: available fields are: `a`, `b`
|
= note: available fields are: `a`, `b`
|
||||||
|
|
||||||
|
|
|
@ -2,7 +2,7 @@ error[E0560]: union `U` has no field named `principle`
|
||||||
--> $DIR/union-suggest-field.rs:20:17
|
--> $DIR/union-suggest-field.rs:20:17
|
||||||
|
|
|
|
||||||
20 | let u = U { principle: 0 };
|
20 | let u = U { principle: 0 };
|
||||||
| ^^^^^^^^^^ field does not exist - did you mean `principal`?
|
| ^^^^^^^^^ field does not exist - did you mean `principal`?
|
||||||
|
|
||||||
error[E0609]: no field `principial` on type `U`
|
error[E0609]: no field `principial` on type `U`
|
||||||
--> $DIR/union-suggest-field.rs:22:15
|
--> $DIR/union-suggest-field.rs:22:15
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue