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
@ -34,6 +34,7 @@ use rustc::util::nodemap::NodeSet;
|
||||
use syntax::ast::{self, CRATE_NODE_ID, Ident};
|
||||
use syntax::symbol::keywords;
|
||||
use syntax_pos::Span;
|
||||
use syntax_pos::hygiene::SyntaxContext;
|
||||
|
||||
use std::cmp;
|
||||
use std::mem::replace;
|
||||
@ -491,9 +492,13 @@ struct NamePrivacyVisitor<'a, 'tcx: 'a> {
|
||||
}
|
||||
|
||||
impl<'a, 'tcx> NamePrivacyVisitor<'a, 'tcx> {
|
||||
// Checks that a field is accessible.
|
||||
fn check_field(&mut self, span: Span, def: &'tcx ty::AdtDef, field: &'tcx ty::FieldDef) {
|
||||
let ident = Ident { ctxt: span.ctxt().modern(), ..keywords::Invalid.ident() };
|
||||
// Checks that a field in a struct constructor (expression or pattern) is accessible.
|
||||
fn check_field(&mut self,
|
||||
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;
|
||||
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",
|
||||
@ -566,12 +571,17 @@ impl<'a, 'tcx> Visitor<'tcx> for NamePrivacyVisitor<'a, 'tcx> {
|
||||
// unmentioned fields, just check them all.
|
||||
for variant_field in &variant.fields {
|
||||
let field = fields.iter().find(|f| f.name.node == variant_field.name);
|
||||
let span = if let Some(f) = field { f.span } else { base.span };
|
||||
self.check_field(span, adt, variant_field);
|
||||
let (use_ctxt, span) = match 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 {
|
||||
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 variant = adt.variant_of_def(def);
|
||||
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.
|
||||
let (fieldname, expr, is_shorthand) = if self.look_ahead(1, |t| t == &token::Colon) {
|
||||
let fieldname = self.parse_field_name()?;
|
||||
self.bump();
|
||||
hi = self.prev_span;
|
||||
self.bump();
|
||||
(fieldname, self.parse_expr()?, false)
|
||||
} else {
|
||||
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
|
||||
|
|
||||
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`
|
||||
--> $DIR/issue-42599_available_fields_note.rs:31:39
|
||||
|
|
||||
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
|
||||
|
||||
|
@ -4,7 +4,7 @@ error[E0062]: field `x` specified more than once
|
||||
17 | x: 0,
|
||||
| ---- first use of `x`
|
||||
18 | x: 0,
|
||||
| ^^ used more than once
|
||||
| ^ used more than once
|
||||
|
||||
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
|
||||
|
|
||||
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`
|
||||
|
||||
|
@ -2,7 +2,7 @@ error[E0560]: struct `Simba` has no field named `father`
|
||||
--> $DIR/E0560.rs:16:32
|
||||
|
|
||||
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`
|
||||
|
||||
|
@ -2,7 +2,7 @@ error[E0559]: variant `Homura::Akemi` has no field named `kaname`
|
||||
--> $DIR/issue-19922.rs:16:34
|
||||
|
|
||||
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`
|
||||
|
||||
|
@ -2,7 +2,7 @@ error[E0560]: struct `S` has no field named `0b1`
|
||||
--> $DIR/numeric-fields.rs:14:15
|
||||
|
|
||||
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`
|
||||
|
||||
|
@ -2,7 +2,7 @@ error[E0560]: struct `A` has no field named `bar`
|
||||
--> $DIR/struct-fields-hints-no-dupe.rs:20:9
|
||||
|
|
||||
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
|
||||
|
||||
|
@ -2,7 +2,7 @@ error[E0560]: struct `A` has no field named `bar`
|
||||
--> $DIR/struct-fields-hints.rs:20:9
|
||||
|
|
||||
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
|
||||
|
||||
|
@ -2,7 +2,7 @@ error[E0560]: struct `BuildData` has no field named `bar`
|
||||
--> $DIR/struct-fields-too-many.rs:18:9
|
||||
|
|
||||
18 | bar: 0
|
||||
| ^^^^ `BuildData` does not have this field
|
||||
| ^^^ `BuildData` does not have this field
|
||||
|
|
||||
= 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
|
||||
|
|
||||
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`
|
||||
--> $DIR/suggest-private-fields.rs:27:9
|
||||
|
|
||||
27 | bb: 20,
|
||||
| ^^^ `xc::B` does not have this field
|
||||
| ^^ `xc::B` does not have this field
|
||||
|
|
||||
= 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
|
||||
|
|
||||
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`
|
||||
--> $DIR/suggest-private-fields.rs:34:9
|
||||
|
|
||||
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
|
||||
|
||||
|
@ -14,7 +14,7 @@ error[E0560]: union `U` has no field named `c`
|
||||
--> $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
|
||||
| ^^ `U` does not have this field
|
||||
| ^ `U` does not have this field
|
||||
|
|
||||
= 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
|
||||
|
|
||||
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`
|
||||
--> $DIR/union-suggest-field.rs:22:15
|
||||
|
Loading…
Reference in New Issue
Block a user