about summary refs log tree commit diff
diff options
context:
space:
mode:
authorVadim Petrochenkov <vadim.petrochenkov@gmail.com>2019-11-23 16:54:24 +0300
committerVadim Petrochenkov <vadim.petrochenkov@gmail.com>2019-11-24 11:47:46 +0300
commitf1359c61d302057d82c5276aba86fec1fe326bb8 (patch)
tree7060c8ea19e8577ed82934d83db668d7de93cf69
parent37bb0c7fa68700b7637c137c8b31cdc2e6b49b5e (diff)
downloadrust-f1359c61d302057d82c5276aba86fec1fe326bb8.tar.gz
rust-f1359c61d302057d82c5276aba86fec1fe326bb8.zip
expand: Fully preserve visibilities on unnamed fields with attributes
-rw-r--r--src/librustc_resolve/build_reduced_graph.rs3
-rw-r--r--src/libsyntax_expand/expand.rs22
-rw-r--r--src/libsyntax_expand/placeholders.rs5
-rw-r--r--src/test/ui/attributes/unnamed-field-attributes-vis.rs11
4 files changed, 37 insertions, 4 deletions
diff --git a/src/librustc_resolve/build_reduced_graph.rs b/src/librustc_resolve/build_reduced_graph.rs
index a178c603a46..6694ddc53d4 100644
--- a/src/librustc_resolve/build_reduced_graph.rs
+++ b/src/librustc_resolve/build_reduced_graph.rs
@@ -746,6 +746,9 @@ impl<'a, 'b> BuildReducedGraphVisitor<'a, 'b> {
 
                 // Record field names for error reporting.
                 let field_names = struct_def.fields().iter().map(|field| {
+                    // NOTE: The field may be an expansion placeholder, but expansion sets correct
+                    // visibilities for unnamed field placeholders specifically, so the constructor
+                    // visibility should still be determined correctly.
                     let field_vis = self.resolve_visibility(&field.vis);
                     if ctor_vis.is_at_least(field_vis, &*self.r) {
                         ctor_vis = field_vis;
diff --git a/src/libsyntax_expand/expand.rs b/src/libsyntax_expand/expand.rs
index 2532bbc0fe2..4f05b0147bf 100644
--- a/src/libsyntax_expand/expand.rs
+++ b/src/libsyntax_expand/expand.rs
@@ -86,7 +86,7 @@ macro_rules! ast_fragments {
                         // mention some macro variable from those arguments even if it's not used.
                         #[cfg_attr(bootstrap, allow(unused_macros))]
                         macro _repeating($flat_map_ast_elt) {}
-                        placeholder(AstFragmentKind::$Kind, *id).$make_ast()
+                        placeholder(AstFragmentKind::$Kind, *id, None).$make_ast()
                     })),)?)*
                     _ => panic!("unexpected AST fragment kind")
                 }
@@ -275,6 +275,23 @@ pub enum InvocationKind {
     },
 }
 
+impl InvocationKind {
+    fn placeholder_visibility(&self) -> Option<ast::Visibility> {
+        // HACK: For unnamed fields placeholders should have the same visibility as the actual
+        // fields because for tuple structs/variants resolve determines visibilities of their
+        // constructor using these field visibilities before attributes on them are are expanded.
+        // The assumption is that the attribute expansion cannot change field visibilities,
+        // and it holds because only inert attributes are supported in this position.
+        match self {
+            InvocationKind::Attr { item: Annotatable::StructField(field), .. } |
+            InvocationKind::Derive { item: Annotatable::StructField(field), .. } |
+            InvocationKind::DeriveContainer { item: Annotatable::StructField(field), .. }
+                if field.ident.is_none() => Some(field.vis.clone()),
+            _ => None,
+        }
+    }
+}
+
 impl Invocation {
     pub fn span(&self) -> Span {
         match &self.kind {
@@ -931,6 +948,7 @@ impl<'a, 'b> InvocationCollector<'a, 'b> {
             _ => None,
         };
         let expn_id = ExpnId::fresh(expn_data);
+        let vis = kind.placeholder_visibility();
         self.invocations.push(Invocation {
             kind,
             fragment_kind,
@@ -940,7 +958,7 @@ impl<'a, 'b> InvocationCollector<'a, 'b> {
                 ..self.cx.current_expansion.clone()
             },
         });
-        placeholder(fragment_kind, NodeId::placeholder_from_expn_id(expn_id))
+        placeholder(fragment_kind, NodeId::placeholder_from_expn_id(expn_id), vis)
     }
 
     fn collect_bang(&mut self, mac: ast::Mac, span: Span, kind: AstFragmentKind) -> AstFragment {
diff --git a/src/libsyntax_expand/placeholders.rs b/src/libsyntax_expand/placeholders.rs
index 36a09700076..6cbe8c13245 100644
--- a/src/libsyntax_expand/placeholders.rs
+++ b/src/libsyntax_expand/placeholders.rs
@@ -12,7 +12,8 @@ use smallvec::{smallvec, SmallVec};
 
 use rustc_data_structures::fx::FxHashMap;
 
-pub fn placeholder(kind: AstFragmentKind, id: ast::NodeId) -> AstFragment {
+pub fn placeholder(kind: AstFragmentKind, id: ast::NodeId, vis: Option<ast::Visibility>)
+                   -> AstFragment {
     fn mac_placeholder() -> ast::Mac {
         ast::Mac {
             path: ast::Path { span: DUMMY_SP, segments: Vec::new() },
@@ -26,7 +27,7 @@ pub fn placeholder(kind: AstFragmentKind, id: ast::NodeId) -> AstFragment {
     let ident = ast::Ident::invalid();
     let attrs = Vec::new();
     let generics = ast::Generics::default();
-    let vis = dummy_spanned(ast::VisibilityKind::Inherited);
+    let vis = vis.unwrap_or_else(|| dummy_spanned(ast::VisibilityKind::Inherited));
     let span = DUMMY_SP;
     let expr_placeholder = || P(ast::Expr {
         id, span,
diff --git a/src/test/ui/attributes/unnamed-field-attributes-vis.rs b/src/test/ui/attributes/unnamed-field-attributes-vis.rs
new file mode 100644
index 00000000000..d12155f6d81
--- /dev/null
+++ b/src/test/ui/attributes/unnamed-field-attributes-vis.rs
@@ -0,0 +1,11 @@
+// Unnamed fields don't lose their visibility due to non-builtin attributes on them.
+
+// check-pass
+
+mod m {
+    pub struct S(#[rustfmt::skip] pub u8);
+}
+
+fn main() {
+    m::S(0);
+}