about summary refs log tree commit diff
diff options
context:
space:
mode:
authorAaron Hill <aa1ronham@gmail.com>2020-06-08 21:09:54 -0400
committerAaron Hill <aa1ronham@gmail.com>2020-06-08 21:09:54 -0400
commit0fcea2e4238dbb7ba3758064051f6330910aae4c (patch)
treea96aa0b360fa84cd7256cca18b69c06e4e8a20d0
parentfd4b177aabb9749dfb562c48e47379cea81dc277 (diff)
downloadrust-0fcea2e4238dbb7ba3758064051f6330910aae4c.tar.gz
rust-0fcea2e4238dbb7ba3758064051f6330910aae4c.zip
Don't lose empty `where` clause when pretty-printing
Previously, we would parse `struct Foo where;` and `struct Foo;`
identically, leading to an 'empty' `where` clause being omitted during
pretty printing. This will cause us to lose spans when proc-macros
involved, since we will have a collected `where` token that does not
appear in the pretty-printed item.

We now explicitly track the presence of a `where` token during parsing,
so that we can distinguish between `struct Foo where;` and `struct Foo;`
during pretty-printing
-rw-r--r--src/librustc_ast/ast.rs11
-rw-r--r--src/librustc_ast/mut_visit.rs2
-rw-r--r--src/librustc_ast_pretty/pprust.rs8
-rw-r--r--src/librustc_builtin_macros/deriving/generic/ty.rs6
-rw-r--r--src/librustc_parse/parser/generics.rs9
-rw-r--r--src/test/ui/proc-macro/empty-where-clause.rs18
-rw-r--r--src/test/ui/proc-macro/empty-where-clause.stderr21
7 files changed, 68 insertions, 7 deletions
diff --git a/src/librustc_ast/ast.rs b/src/librustc_ast/ast.rs
index efcf95ec706..672ef108969 100644
--- a/src/librustc_ast/ast.rs
+++ b/src/librustc_ast/ast.rs
@@ -362,7 +362,11 @@ impl Default for Generics {
     fn default() -> Generics {
         Generics {
             params: Vec::new(),
-            where_clause: WhereClause { predicates: Vec::new(), span: DUMMY_SP },
+            where_clause: WhereClause {
+                has_where_token: false,
+                predicates: Vec::new(),
+                span: DUMMY_SP,
+            },
             span: DUMMY_SP,
         }
     }
@@ -371,6 +375,11 @@ impl Default for Generics {
 /// A where-clause in a definition.
 #[derive(Clone, RustcEncodable, RustcDecodable, Debug)]
 pub struct WhereClause {
+    /// `true` if we ate a `where` token: this can happen
+    /// if we parsed no predicates (e.g. `struct Foo where {}
+    /// This allows us to accurately pretty-print
+    /// in `nt_to_tokenstream`
+    pub has_where_token: bool,
     pub predicates: Vec<WherePredicate>,
     pub span: Span,
 }
diff --git a/src/librustc_ast/mut_visit.rs b/src/librustc_ast/mut_visit.rs
index 7ececb814a6..66b8d7f97f0 100644
--- a/src/librustc_ast/mut_visit.rs
+++ b/src/librustc_ast/mut_visit.rs
@@ -786,7 +786,7 @@ pub fn noop_visit_generics<T: MutVisitor>(generics: &mut Generics, vis: &mut T)
 }
 
 pub fn noop_visit_where_clause<T: MutVisitor>(wc: &mut WhereClause, vis: &mut T) {
-    let WhereClause { predicates, span } = wc;
+    let WhereClause { has_where_token: _, predicates, span } = wc;
     visit_vec(predicates, |predicate| vis.visit_where_predicate(predicate));
     vis.visit_span(span);
 }
diff --git a/src/librustc_ast_pretty/pprust.rs b/src/librustc_ast_pretty/pprust.rs
index 872126646f3..8ff80df527c 100644
--- a/src/librustc_ast_pretty/pprust.rs
+++ b/src/librustc_ast_pretty/pprust.rs
@@ -2593,7 +2593,7 @@ impl<'a> State<'a> {
     }
 
     crate fn print_where_clause(&mut self, where_clause: &ast::WhereClause) {
-        if where_clause.predicates.is_empty() {
+        if where_clause.predicates.is_empty() && !where_clause.has_where_token {
             return;
         }
 
@@ -2739,7 +2739,11 @@ impl<'a> State<'a> {
         }
         let generics = ast::Generics {
             params: Vec::new(),
-            where_clause: ast::WhereClause { predicates: Vec::new(), span: rustc_span::DUMMY_SP },
+            where_clause: ast::WhereClause {
+                has_where_token: false,
+                predicates: Vec::new(),
+                span: rustc_span::DUMMY_SP,
+            },
             span: rustc_span::DUMMY_SP,
         };
         let header = ast::FnHeader { unsafety, ext, ..ast::FnHeader::default() };
diff --git a/src/librustc_builtin_macros/deriving/generic/ty.rs b/src/librustc_builtin_macros/deriving/generic/ty.rs
index 62cbdb19a88..609feb6f259 100644
--- a/src/librustc_builtin_macros/deriving/generic/ty.rs
+++ b/src/librustc_builtin_macros/deriving/generic/ty.rs
@@ -216,7 +216,11 @@ fn mk_ty_param(
 }
 
 fn mk_generics(params: Vec<ast::GenericParam>, span: Span) -> Generics {
-    Generics { params, where_clause: ast::WhereClause { predicates: Vec::new(), span }, span }
+    Generics {
+        params,
+        where_clause: ast::WhereClause { has_where_token: false, predicates: Vec::new(), span },
+        span,
+    }
 }
 
 /// Lifetimes and bounds on type parameters
diff --git a/src/librustc_parse/parser/generics.rs b/src/librustc_parse/parser/generics.rs
index 8e8f864728c..04b64d93c70 100644
--- a/src/librustc_parse/parser/generics.rs
+++ b/src/librustc_parse/parser/generics.rs
@@ -157,6 +157,7 @@ impl<'a> Parser<'a> {
         Ok(ast::Generics {
             params,
             where_clause: WhereClause {
+                has_where_token: false,
                 predicates: Vec::new(),
                 span: self.prev_token.span.shrink_to_hi(),
             },
@@ -170,12 +171,16 @@ impl<'a> Parser<'a> {
     /// where T : Trait<U, V> + 'b, 'a : 'b
     /// ```
     pub(super) fn parse_where_clause(&mut self) -> PResult<'a, WhereClause> {
-        let mut where_clause =
-            WhereClause { predicates: Vec::new(), span: self.prev_token.span.shrink_to_hi() };
+        let mut where_clause = WhereClause {
+            has_where_token: false,
+            predicates: Vec::new(),
+            span: self.prev_token.span.shrink_to_hi(),
+        };
 
         if !self.eat_keyword(kw::Where) {
             return Ok(where_clause);
         }
+        where_clause.has_where_token = true;
         let lo = self.prev_token.span;
 
         // We are considering adding generics to the `where` keyword as an alternative higher-rank
diff --git a/src/test/ui/proc-macro/empty-where-clause.rs b/src/test/ui/proc-macro/empty-where-clause.rs
new file mode 100644
index 00000000000..719555c092a
--- /dev/null
+++ b/src/test/ui/proc-macro/empty-where-clause.rs
@@ -0,0 +1,18 @@
+// aux-build:test-macros.rs
+
+extern crate test_macros;
+use test_macros::recollect_attr;
+
+#[recollect_attr]
+struct FieldStruct where {
+    field: MissingType1 //~ ERROR cannot find
+}
+
+#[recollect_attr]
+struct TupleStruct(MissingType2) where; //~ ERROR cannot find
+
+enum MyEnum where {
+    Variant(MissingType3) //~ ERROR cannot find
+}
+
+fn main() {}
diff --git a/src/test/ui/proc-macro/empty-where-clause.stderr b/src/test/ui/proc-macro/empty-where-clause.stderr
new file mode 100644
index 00000000000..192a2b30f0d
--- /dev/null
+++ b/src/test/ui/proc-macro/empty-where-clause.stderr
@@ -0,0 +1,21 @@
+error[E0412]: cannot find type `MissingType1` in this scope
+  --> $DIR/empty-where-clause.rs:8:12
+   |
+LL |     field: MissingType1
+   |            ^^^^^^^^^^^^ not found in this scope
+
+error[E0412]: cannot find type `MissingType2` in this scope
+  --> $DIR/empty-where-clause.rs:12:20
+   |
+LL | struct TupleStruct(MissingType2) where;
+   |                    ^^^^^^^^^^^^ not found in this scope
+
+error[E0412]: cannot find type `MissingType3` in this scope
+  --> $DIR/empty-where-clause.rs:15:13
+   |
+LL |     Variant(MissingType3)
+   |             ^^^^^^^^^^^^ not found in this scope
+
+error: aborting due to 3 previous errors
+
+For more information about this error, try `rustc --explain E0412`.