From 6db688e893f69632624fb8d2e3b747a3b81000b0 Mon Sep 17 00:00:00 2001
From: Tim Chevalier <chevalier@alum.wellesley.edu>
Date: Mon, 23 Jan 2012 15:46:12 -0800
Subject: [PATCH] Check that the names mentioned in tag exports are actually
 types (or variants)

Check that in export foo{}, foo is an enum type, and that in export
foo{bar, quux}, foo is an enum type and bar and quux are variants belonging
to foo.
---
 src/comp/middle/resolve.rs                | 60 ++++++++++++++++++++++-
 src/test/compile-fail/bad-tag-export-2.rs | 11 +++++
 src/test/compile-fail/bad-tag-export-3.rs | 13 +++++
 src/test/compile-fail/bad-tag-export-4.rs | 12 +++++
 src/test/compile-fail/bad-tag-export.rs   | 14 ++++++
 5 files changed, 108 insertions(+), 2 deletions(-)
 create mode 100644 src/test/compile-fail/bad-tag-export-2.rs
 create mode 100644 src/test/compile-fail/bad-tag-export-3.rs
 create mode 100644 src/test/compile-fail/bad-tag-export-4.rs
 create mode 100644 src/test/compile-fail/bad-tag-export.rs

diff --git a/src/comp/middle/resolve.rs b/src/comp/middle/resolve.rs
index 9cbf6bf74a1..3b49f8c33ad 100644
--- a/src/comp/middle/resolve.rs
+++ b/src/comp/middle/resolve.rs
@@ -1766,6 +1766,32 @@ fn check_exports(e: @env) {
         }
     }
 
+    fn check_enum_ok(e: @env, sp:span, id: ident, val: @indexed_mod)
+        -> node_id {
+        alt val.index.find(id) {
+           none { e.sess.span_fatal(sp, #fmt("error: undefined id %s\
+                         in an export", id)); }
+           some(ms) {
+             let maybe_id = list::find(ms) {|m|
+                  alt m {
+                     mie_item(an_item) {
+                      alt an_item.node {
+                          item_tag(_,_) { /* OK */ some(an_item.id) }
+                          _ { none }
+                      }
+                     }
+                     _ { none }
+               }
+             };
+             alt maybe_id {
+                some(an_id) { ret an_id; }
+                _ { e.sess.span_fatal(sp, #fmt("error: %s does not refer \
+                          to an enumeration", id)); }
+             }
+         }
+      }
+    }
+
     e.mod_map.values {|val|
         alt val.m {
           some(m) {
@@ -1776,14 +1802,44 @@ fn check_exports(e: @env) {
                         check_export(e, ident, val, vi);
                     }
                   }
+                  ast::view_item_export_tag_none(id, _) {
+                      let _ = check_enum_ok(e, vi.span, id, val);
+                  }
+                  ast::view_item_export_tag_some(id, ids, _) {
+                      // Check that it's an enum and all the given variants
+                      // belong to it
+                      let parent_id = check_enum_ok(e, vi.span, id, val);
+                      for variant_id in ids {
+                         alt val.index.find(variant_id.node.name) {
+                            some(ms) {
+                                list::iter(ms) {|m|
+                                   alt m {
+                                     mie_tag_variant(parent_item,_) {
+                                       if parent_item.id != parent_id {
+                                          e.sess.span_err(vi.span,
+                                           #fmt("variant %s \
+                                           doesn't belong to enum %s",
+                                                variant_id.node.name,
+                                                id));
+                                       }
+                                     }
+                                     _ { e.sess.span_err(vi.span,
+                                         #fmt("%s is not a \
+                                         variant", variant_id.node.name));  }
+                                       }}
+                            }
+                            _ { e.sess.span_err(vi.span, #fmt("%s is not a\
+                                         variant", variant_id.node.name));  }
+                      }
+                     }
+                  }
                   _ { }
                 }
             }
           }
           none { }
         }
-    };
-}
+    }}
 
 // Impl resolution
 
diff --git a/src/test/compile-fail/bad-tag-export-2.rs b/src/test/compile-fail/bad-tag-export-2.rs
new file mode 100644
index 00000000000..09018c2167f
--- /dev/null
+++ b/src/test/compile-fail/bad-tag-export-2.rs
@@ -0,0 +1,11 @@
+// error-pattern:b does not refer to an enumeration
+import bad::*;
+
+mod bad {
+  export b::{};
+
+  fn b() { fail; }
+}
+
+fn main() {
+}
\ No newline at end of file
diff --git a/src/test/compile-fail/bad-tag-export-3.rs b/src/test/compile-fail/bad-tag-export-3.rs
new file mode 100644
index 00000000000..e6934688e21
--- /dev/null
+++ b/src/test/compile-fail/bad-tag-export-3.rs
@@ -0,0 +1,13 @@
+// error-pattern:b does not refer to an enumeration
+import bad::*;
+
+mod bad {
+  export b::{f, z};
+
+  fn b() { fail; }
+  fn f() { fail; }
+  fn z() { fail; }
+}
+
+fn main() {
+}
\ No newline at end of file
diff --git a/src/test/compile-fail/bad-tag-export-4.rs b/src/test/compile-fail/bad-tag-export-4.rs
new file mode 100644
index 00000000000..fadf0c353a3
--- /dev/null
+++ b/src/test/compile-fail/bad-tag-export-4.rs
@@ -0,0 +1,12 @@
+// error-pattern:f is not a variant
+import bad::*;
+
+mod bad {
+  export b::{f, z};
+
+  enum b { z, k }
+  fn f() { fail; }
+}
+
+fn main() {
+}
\ No newline at end of file
diff --git a/src/test/compile-fail/bad-tag-export.rs b/src/test/compile-fail/bad-tag-export.rs
new file mode 100644
index 00000000000..fb9d9b8682c
--- /dev/null
+++ b/src/test/compile-fail/bad-tag-export.rs
@@ -0,0 +1,14 @@
+// error-pattern:variant e doesn't belong to enum floop
+import bad::*;
+
+mod bad {
+
+  export floop::{a, e};
+
+  enum floop {a, b, c}
+  enum bloop {d, e, f}
+
+}
+
+fn main() {
+}
\ No newline at end of file