about summary refs log tree commit diff
path: root/src/test/ui
diff options
context:
space:
mode:
authorMark Rousskov <mark.simulacrum@gmail.com>2019-07-18 11:29:43 -0400
committerGitHub <noreply@github.com>2019-07-18 11:29:43 -0400
commitaa31ca71283f783b20ee3b460620e0907f4a4a31 (patch)
tree13ea0f69c0dc60a4edb69aacee289bf802e58513 /src/test/ui
parent831d33b8b0108c808d5148d434295c4c94f7102b (diff)
parenta4a7bb9a3f72505efb6ca9856af45287c8685d5e (diff)
Rollup merge of #62730 - matthewjasper:hygiene-tests, r=petrochenkov
Consolidate hygiene tests

Some general clean-up extracted out from another PR
Diffstat (limited to 'src/test/ui')
-rw-r--r--src/test/ui/hygiene/auxiliary/legacy_interaction.rs9
-rw-r--r--src/test/ui/hygiene/auxiliary/my_crate.rs1
-rw-r--r--src/test/ui/hygiene/auxiliary/unhygienic_example.rs27
-rw-r--r--src/test/ui/hygiene/auxiliary/xcrate.rs28
-rw-r--r--src/test/ui/hygiene/dollar-crate-modern.rs2
-rw-r--r--src/test/ui/hygiene/expansion-info-reset.rs3
-rw-r--r--src/test/ui/hygiene/expansion-info-reset.stderr2
-rw-r--r--src/test/ui/hygiene/hygiene-dodging-1.rs14
-rw-r--r--src/test/ui/hygiene/hygiene.rs114
-rw-r--r--src/test/ui/hygiene/hygienic-labels-in-let.rs75
-rw-r--r--src/test/ui/hygiene/hygienic-labels-in-let.stderr300
-rw-r--r--src/test/ui/hygiene/hygienic-labels.rs53
-rw-r--r--src/test/ui/hygiene/hygienic-labels.stderr299
-rw-r--r--src/test/ui/hygiene/issue-44128.rs17
-rw-r--r--src/test/ui/hygiene/issue-47311.rs17
-rw-r--r--src/test/ui/hygiene/issue-47312.rs21
-rw-r--r--src/test/ui/hygiene/items.rs27
-rw-r--r--src/test/ui/hygiene/legacy_interaction.rs42
-rw-r--r--src/test/ui/hygiene/lexical.rs24
-rw-r--r--src/test/ui/hygiene/local_inner_macros.rs2
-rw-r--r--src/test/ui/hygiene/specialization.rs25
-rw-r--r--src/test/ui/hygiene/trait_items-2.rs20
-rw-r--r--src/test/ui/hygiene/transparent-basic.rs2
-rw-r--r--src/test/ui/hygiene/ty_params.rs14
-rw-r--r--src/test/ui/hygiene/unpretty-debug.rs20
-rw-r--r--src/test/ui/hygiene/unpretty-debug.stdout15
-rw-r--r--src/test/ui/hygiene/wrap_unhygienic_example.rs34
-rw-r--r--src/test/ui/hygiene/xcrate.rs12
28 files changed, 1212 insertions, 7 deletions
diff --git a/src/test/ui/hygiene/auxiliary/legacy_interaction.rs b/src/test/ui/hygiene/auxiliary/legacy_interaction.rs
new file mode 100644
index 00000000000..90d5243b74b
--- /dev/null
+++ b/src/test/ui/hygiene/auxiliary/legacy_interaction.rs
@@ -0,0 +1,9 @@
+// ignore-pretty pretty-printing is unhygienic
+
+#[macro_export]
+macro_rules! m {
+    () => {
+        fn f() {} // (2)
+        g(); // (1)
+    }
+}
diff --git a/src/test/ui/hygiene/auxiliary/my_crate.rs b/src/test/ui/hygiene/auxiliary/my_crate.rs
new file mode 100644
index 00000000000..cdc6c27d800
--- /dev/null
+++ b/src/test/ui/hygiene/auxiliary/my_crate.rs
@@ -0,0 +1 @@
+pub fn f() {}
diff --git a/src/test/ui/hygiene/auxiliary/unhygienic_example.rs b/src/test/ui/hygiene/auxiliary/unhygienic_example.rs
new file mode 100644
index 00000000000..8e6e8f9b32f
--- /dev/null
+++ b/src/test/ui/hygiene/auxiliary/unhygienic_example.rs
@@ -0,0 +1,27 @@
+#![crate_type = "lib"]
+
+extern crate my_crate;
+
+pub fn g() {} // (a)
+
+#[macro_export]
+macro_rules! unhygienic_macro {
+    () => {
+        // (1) unhygienic: depends on `my_crate` in the crate root at the invocation site.
+        ::my_crate::f();
+
+        // (2) unhygienic: defines `f` at the invocation site (in addition to the above point).
+        use my_crate::f;
+        f();
+
+        g(); // (3) unhygienic: `g` needs to be in scope at use site.
+
+        $crate::g(); // (4) hygienic: this always resolves to (a)
+    }
+}
+
+#[allow(unused)]
+fn test_unhygienic() {
+    unhygienic_macro!();
+    f(); // `f` was defined at the use site
+}
diff --git a/src/test/ui/hygiene/auxiliary/xcrate.rs b/src/test/ui/hygiene/auxiliary/xcrate.rs
new file mode 100644
index 00000000000..f5a911f57df
--- /dev/null
+++ b/src/test/ui/hygiene/auxiliary/xcrate.rs
@@ -0,0 +1,28 @@
+#![feature(decl_macro)]
+#![allow(unused)]
+
+pub use bar::test;
+
+extern crate std as foo;
+
+pub fn f() {}
+use f as f2;
+
+mod bar {
+    pub fn g() {}
+    use baz::h;
+
+    pub macro test() {
+        use std::mem;
+        use foo::cell;
+        ::f();
+        ::f2();
+        g();
+        h();
+        ::bar::h();
+    }
+}
+
+mod baz {
+    pub fn h() {}
+}
diff --git a/src/test/ui/hygiene/dollar-crate-modern.rs b/src/test/ui/hygiene/dollar-crate-modern.rs
index 9c25e138d2c..a432fb6eee1 100644
--- a/src/test/ui/hygiene/dollar-crate-modern.rs
+++ b/src/test/ui/hygiene/dollar-crate-modern.rs
@@ -1,6 +1,6 @@
 // Make sure `$crate` and `crate` work in for basic cases of nested macros.
 
-// build-pass (FIXME(62277): could be check-pass?)
+// check-pass
 // aux-build:intercrate.rs
 
 #![feature(decl_macro, crate_in_paths)]
diff --git a/src/test/ui/hygiene/expansion-info-reset.rs b/src/test/ui/hygiene/expansion-info-reset.rs
index 5c384c4ada9..fa5f712121d 100644
--- a/src/test/ui/hygiene/expansion-info-reset.rs
+++ b/src/test/ui/hygiene/expansion-info-reset.rs
@@ -1,6 +1,3 @@
-// FIXME: Investigate why expansion info for a single expansion id is reset from
-// `MacroBang(format_args)` to `MacroAttribute(derive(Clone))` (issue #52363).
-
 fn main() {
     format_args!({ #[derive(Clone)] struct S; });
     //~^ ERROR format argument must be a string literal
diff --git a/src/test/ui/hygiene/expansion-info-reset.stderr b/src/test/ui/hygiene/expansion-info-reset.stderr
index 9dd954b16cd..d8b602ce1c6 100644
--- a/src/test/ui/hygiene/expansion-info-reset.stderr
+++ b/src/test/ui/hygiene/expansion-info-reset.stderr
@@ -1,5 +1,5 @@
 error: format argument must be a string literal
-  --> $DIR/expansion-info-reset.rs:5:18
+  --> $DIR/expansion-info-reset.rs:2:18
    |
 LL |     format_args!({ #[derive(Clone)] struct S; });
    |                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
diff --git a/src/test/ui/hygiene/hygiene-dodging-1.rs b/src/test/ui/hygiene/hygiene-dodging-1.rs
new file mode 100644
index 00000000000..69e47e82ba5
--- /dev/null
+++ b/src/test/ui/hygiene/hygiene-dodging-1.rs
@@ -0,0 +1,14 @@
+// run-pass
+#![allow(unused_must_use)]
+
+mod x {
+    pub fn g() -> usize {14}
+}
+
+pub fn main(){
+    // should *not* shadow the module x:
+    let x = 9;
+    // use it to avoid warnings:
+    x+3;
+    assert_eq!(x::g(),14);
+}
diff --git a/src/test/ui/hygiene/hygiene.rs b/src/test/ui/hygiene/hygiene.rs
new file mode 100644
index 00000000000..fb351cf0faf
--- /dev/null
+++ b/src/test/ui/hygiene/hygiene.rs
@@ -0,0 +1,114 @@
+// run-pass
+#![allow(unused)]
+
+fn f() {
+    let x = 0;
+    macro_rules! foo { () => {
+        assert_eq!(x, 0);
+    } }
+
+    let x = 1;
+    foo!();
+}
+
+fn g() {
+    let x = 0;
+    macro_rules! m { ($m1:ident, $m2:ident, $x:ident) => {
+        macro_rules! $m1 { () => { ($x, x) } }
+        let x = 1;
+        macro_rules! $m2 { () => { ($x, x) } }
+    } }
+
+    let x = 2;
+    m!(m2, m3, x);
+
+    let x = 3;
+    assert_eq!(m2!(), (2, 0));
+    assert_eq!(m3!(), (2, 1));
+
+    let x = 4;
+    m!(m4, m5, x);
+    assert_eq!(m4!(), (4, 0));
+    assert_eq!(m5!(), (4, 1));
+}
+
+mod foo {
+    macro_rules! m {
+        ($f:ident : |$x:ident| $e:expr) => {
+            pub fn $f() -> (i32, i32) {
+                let x = 0;
+                let $x = 1;
+                (x, $e)
+            }
+        }
+    }
+
+    m!(f: |x| x + 10);
+}
+
+fn interpolated_pattern() {
+    let x = 0;
+    macro_rules! m {
+        ($p:pat, $e:expr) => {
+            let $p = 1;
+            assert_eq!((x, $e), (0, 1));
+        }
+    }
+
+    m!(x, x);
+}
+
+fn patterns_in_macro_generated_macros() {
+    let x = 0;
+    macro_rules! m {
+        ($a:expr, $b:expr) => {
+            assert_eq!(x, 0);
+            let x = $a;
+            macro_rules! n {
+                () => {
+                    (x, $b)
+                }
+            }
+        }
+    }
+
+    let x = 1;
+    m!(2, x);
+
+    let x = 3;
+    assert_eq!(n!(), (2, 1));
+}
+
+fn match_hygiene() {
+    let x = 0;
+
+    macro_rules! m {
+        ($p:pat, $e:expr) => {
+            for result in &[Ok(1), Err(1)] {
+                match *result {
+                    $p => { assert_eq!(($e, x), (1, 0)); }
+                    Err(x) => { assert_eq!(($e, x), (2, 1)); }
+                }
+            }
+        }
+    }
+
+    let x = 2;
+    m!(Ok(x), x);
+}
+
+fn label_hygiene() {
+    'a: loop {
+        macro_rules! m { () => { break 'a; } }
+        m!();
+    }
+}
+
+fn main() {
+    f();
+    g();
+    assert_eq!(foo::f(), (0, 11));
+    interpolated_pattern();
+    patterns_in_macro_generated_macros();
+    match_hygiene();
+}
diff --git a/src/test/ui/hygiene/hygienic-labels-in-let.rs b/src/test/ui/hygiene/hygienic-labels-in-let.rs
new file mode 100644
index 00000000000..fb9523b547d
--- /dev/null
+++ b/src/test/ui/hygiene/hygienic-labels-in-let.rs
@@ -0,0 +1,75 @@
+// run-pass
+#![allow(unreachable_code)]
+
+// Test that labels injected by macros do not break hygiene.  This
+// checks cases where the macros invocations are under the rhs of a
+// let statement.
+
+// Issue #24278: The label/lifetime shadowing checker from #24162
+// conservatively ignores hygiene, and thus issues warnings that are
+// both true- and false-positives for this test.
+
+macro_rules! loop_x {
+    ($e: expr) => {
+        // $e shouldn't be able to interact with this 'x
+        'x: loop { $e }
+    }
+}
+
+macro_rules! while_true {
+    ($e: expr) => {
+        // $e shouldn't be able to interact with this 'x
+        'x: while 1 + 1 == 2 { $e }
+    }
+}
+
+macro_rules! run_once {
+    ($e: expr) => {
+        // ditto
+        'x: for _ in 0..1 { $e }
+    }
+}
+
+pub fn main() {
+    let mut i = 0;
+
+    let j: isize = {
+        'x: loop {
+            // this 'x should refer to the outer loop, lexically
+            loop_x!(break 'x);
+            i += 1;
+        }
+        i + 1
+    };
+    assert_eq!(j, 1);
+
+    let k: isize = {
+        'x: for _ in 0..1 {
+            // ditto
+            loop_x!(break 'x);
+            i += 1;
+        }
+        i + 1
+    };
+    assert_eq!(k, 1);
+
+    let l: isize = {
+        'x: for _ in 0..1 {
+            // ditto
+            while_true!(break 'x);
+            i += 1;
+        }
+        i + 1
+    };
+    assert_eq!(l, 1);
+
+    let n: isize = {
+        'x: for _ in 0..1 {
+            // ditto
+            run_once!(continue 'x);
+            i += 1;
+        }
+        i + 1
+    };
+    assert_eq!(n, 1);
+}
diff --git a/src/test/ui/hygiene/hygienic-labels-in-let.stderr b/src/test/ui/hygiene/hygienic-labels-in-let.stderr
new file mode 100644
index 00000000000..9c906749f8c
--- /dev/null
+++ b/src/test/ui/hygiene/hygienic-labels-in-let.stderr
@@ -0,0 +1,300 @@
+warning: label name `'x` shadows a label name that is already in scope
+  --> $DIR/hygienic-labels-in-let.rs:15:9
+   |
+LL |         'x: loop { $e }
+   |         ^^ lifetime 'x already in scope
+...
+LL |         'x: loop {
+   |         -- first declared here
+LL |             // this 'x should refer to the outer loop, lexically
+LL |             loop_x!(break 'x);
+   |             ------------------ in this macro invocation
+
+warning: label name `'x` shadows a label name that is already in scope
+  --> $DIR/hygienic-labels-in-let.rs:47:9
+   |
+LL |         'x: loop {
+   |         -- first declared here
+...
+LL |         'x: for _ in 0..1 {
+   |         ^^ lifetime 'x already in scope
+
+warning: label name `'x` shadows a label name that is already in scope
+  --> $DIR/hygienic-labels-in-let.rs:47:9
+   |
+LL |         'x: loop { $e }
+   |         -- first declared here
+...
+LL |         'x: for _ in 0..1 {
+   |         ^^ lifetime 'x already in scope
+
+warning: label name `'x` shadows a label name that is already in scope
+  --> $DIR/hygienic-labels-in-let.rs:15:9
+   |
+LL |         'x: loop { $e }
+   |         ^^ lifetime 'x already in scope
+...
+LL |         'x: loop {
+   |         -- first declared here
+...
+LL |             loop_x!(break 'x);
+   |             ------------------ in this macro invocation
+
+warning: label name `'x` shadows a label name that is already in scope
+  --> $DIR/hygienic-labels-in-let.rs:15:9
+   |
+LL |         'x: loop { $e }
+   |         ^^
+   |         |
+   |         first declared here
+   |         lifetime 'x already in scope
+...
+LL |             loop_x!(break 'x);
+   |             ------------------ in this macro invocation
+
+warning: label name `'x` shadows a label name that is already in scope
+  --> $DIR/hygienic-labels-in-let.rs:15:9
+   |
+LL |         'x: loop { $e }
+   |         ^^ lifetime 'x already in scope
+...
+LL |         'x: for _ in 0..1 {
+   |         -- first declared here
+LL |             // ditto
+LL |             loop_x!(break 'x);
+   |             ------------------ in this macro invocation
+
+warning: label name `'x` shadows a label name that is already in scope
+  --> $DIR/hygienic-labels-in-let.rs:57:9
+   |
+LL |         'x: loop {
+   |         -- first declared here
+...
+LL |         'x: for _ in 0..1 {
+   |         ^^ lifetime 'x already in scope
+
+warning: label name `'x` shadows a label name that is already in scope
+  --> $DIR/hygienic-labels-in-let.rs:57:9
+   |
+LL |         'x: loop { $e }
+   |         -- first declared here
+...
+LL |         'x: for _ in 0..1 {
+   |         ^^ lifetime 'x already in scope
+
+warning: label name `'x` shadows a label name that is already in scope
+  --> $DIR/hygienic-labels-in-let.rs:57:9
+   |
+LL |         'x: for _ in 0..1 {
+   |         -- first declared here
+...
+LL |         'x: for _ in 0..1 {
+   |         ^^ lifetime 'x already in scope
+
+warning: label name `'x` shadows a label name that is already in scope
+  --> $DIR/hygienic-labels-in-let.rs:57:9
+   |
+LL |         'x: loop { $e }
+   |         -- first declared here
+...
+LL |         'x: for _ in 0..1 {
+   |         ^^ lifetime 'x already in scope
+
+warning: label name `'x` shadows a label name that is already in scope
+  --> $DIR/hygienic-labels-in-let.rs:22:9
+   |
+LL |         'x: while 1 + 1 == 2 { $e }
+   |         ^^ lifetime 'x already in scope
+...
+LL |         'x: loop {
+   |         -- first declared here
+...
+LL |             while_true!(break 'x);
+   |             ---------------------- in this macro invocation
+
+warning: label name `'x` shadows a label name that is already in scope
+  --> $DIR/hygienic-labels-in-let.rs:22:9
+   |
+LL |         'x: loop { $e }
+   |         -- first declared here
+...
+LL |         'x: while 1 + 1 == 2 { $e }
+   |         ^^ lifetime 'x already in scope
+...
+LL |             while_true!(break 'x);
+   |             ---------------------- in this macro invocation
+
+warning: label name `'x` shadows a label name that is already in scope
+  --> $DIR/hygienic-labels-in-let.rs:22:9
+   |
+LL |         'x: while 1 + 1 == 2 { $e }
+   |         ^^ lifetime 'x already in scope
+...
+LL |         'x: for _ in 0..1 {
+   |         -- first declared here
+...
+LL |             while_true!(break 'x);
+   |             ---------------------- in this macro invocation
+
+warning: label name `'x` shadows a label name that is already in scope
+  --> $DIR/hygienic-labels-in-let.rs:22:9
+   |
+LL |         'x: loop { $e }
+   |         -- first declared here
+...
+LL |         'x: while 1 + 1 == 2 { $e }
+   |         ^^ lifetime 'x already in scope
+...
+LL |             while_true!(break 'x);
+   |             ---------------------- in this macro invocation
+
+warning: label name `'x` shadows a label name that is already in scope
+  --> $DIR/hygienic-labels-in-let.rs:22:9
+   |
+LL |         'x: while 1 + 1 == 2 { $e }
+   |         ^^ lifetime 'x already in scope
+...
+LL |         'x: for _ in 0..1 {
+   |         -- first declared here
+LL |             // ditto
+LL |             while_true!(break 'x);
+   |             ---------------------- in this macro invocation
+
+warning: label name `'x` shadows a label name that is already in scope
+  --> $DIR/hygienic-labels-in-let.rs:67:9
+   |
+LL |         'x: loop {
+   |         -- first declared here
+...
+LL |         'x: for _ in 0..1 {
+   |         ^^ lifetime 'x already in scope
+
+warning: label name `'x` shadows a label name that is already in scope
+  --> $DIR/hygienic-labels-in-let.rs:67:9
+   |
+LL |         'x: loop { $e }
+   |         -- first declared here
+...
+LL |         'x: for _ in 0..1 {
+   |         ^^ lifetime 'x already in scope
+
+warning: label name `'x` shadows a label name that is already in scope
+  --> $DIR/hygienic-labels-in-let.rs:67:9
+   |
+LL |         'x: for _ in 0..1 {
+   |         -- first declared here
+...
+LL |         'x: for _ in 0..1 {
+   |         ^^ lifetime 'x already in scope
+
+warning: label name `'x` shadows a label name that is already in scope
+  --> $DIR/hygienic-labels-in-let.rs:67:9
+   |
+LL |         'x: loop { $e }
+   |         -- first declared here
+...
+LL |         'x: for _ in 0..1 {
+   |         ^^ lifetime 'x already in scope
+
+warning: label name `'x` shadows a label name that is already in scope
+  --> $DIR/hygienic-labels-in-let.rs:67:9
+   |
+LL |         'x: for _ in 0..1 {
+   |         -- first declared here
+...
+LL |         'x: for _ in 0..1 {
+   |         ^^ lifetime 'x already in scope
+
+warning: label name `'x` shadows a label name that is already in scope
+  --> $DIR/hygienic-labels-in-let.rs:67:9
+   |
+LL |         'x: while 1 + 1 == 2 { $e }
+   |         -- first declared here
+...
+LL |         'x: for _ in 0..1 {
+   |         ^^ lifetime 'x already in scope
+
+warning: label name `'x` shadows a label name that is already in scope
+  --> $DIR/hygienic-labels-in-let.rs:29:9
+   |
+LL |         'x: for _ in 0..1 { $e }
+   |         ^^ lifetime 'x already in scope
+...
+LL |         'x: loop {
+   |         -- first declared here
+...
+LL |             run_once!(continue 'x);
+   |             ----------------------- in this macro invocation
+
+warning: label name `'x` shadows a label name that is already in scope
+  --> $DIR/hygienic-labels-in-let.rs:29:9
+   |
+LL |         'x: loop { $e }
+   |         -- first declared here
+...
+LL |         'x: for _ in 0..1 { $e }
+   |         ^^ lifetime 'x already in scope
+...
+LL |             run_once!(continue 'x);
+   |             ----------------------- in this macro invocation
+
+warning: label name `'x` shadows a label name that is already in scope
+  --> $DIR/hygienic-labels-in-let.rs:29:9
+   |
+LL |         'x: for _ in 0..1 { $e }
+   |         ^^ lifetime 'x already in scope
+...
+LL |         'x: for _ in 0..1 {
+   |         -- first declared here
+...
+LL |             run_once!(continue 'x);
+   |             ----------------------- in this macro invocation
+
+warning: label name `'x` shadows a label name that is already in scope
+  --> $DIR/hygienic-labels-in-let.rs:29:9
+   |
+LL |         'x: loop { $e }
+   |         -- first declared here
+...
+LL |         'x: for _ in 0..1 { $e }
+   |         ^^ lifetime 'x already in scope
+...
+LL |             run_once!(continue 'x);
+   |             ----------------------- in this macro invocation
+
+warning: label name `'x` shadows a label name that is already in scope
+  --> $DIR/hygienic-labels-in-let.rs:29:9
+   |
+LL |         'x: for _ in 0..1 { $e }
+   |         ^^ lifetime 'x already in scope
+...
+LL |         'x: for _ in 0..1 {
+   |         -- first declared here
+...
+LL |             run_once!(continue 'x);
+   |             ----------------------- in this macro invocation
+
+warning: label name `'x` shadows a label name that is already in scope
+  --> $DIR/hygienic-labels-in-let.rs:29:9
+   |
+LL |         'x: while 1 + 1 == 2 { $e }
+   |         -- first declared here
+...
+LL |         'x: for _ in 0..1 { $e }
+   |         ^^ lifetime 'x already in scope
+...
+LL |             run_once!(continue 'x);
+   |             ----------------------- in this macro invocation
+
+warning: label name `'x` shadows a label name that is already in scope
+  --> $DIR/hygienic-labels-in-let.rs:29:9
+   |
+LL |         'x: for _ in 0..1 { $e }
+   |         ^^ lifetime 'x already in scope
+...
+LL |         'x: for _ in 0..1 {
+   |         -- first declared here
+LL |             // ditto
+LL |             run_once!(continue 'x);
+   |             ----------------------- in this macro invocation
+
diff --git a/src/test/ui/hygiene/hygienic-labels.rs b/src/test/ui/hygiene/hygienic-labels.rs
new file mode 100644
index 00000000000..a364dd9c887
--- /dev/null
+++ b/src/test/ui/hygiene/hygienic-labels.rs
@@ -0,0 +1,53 @@
+// run-pass
+#![allow(unreachable_code)]
+// Test that labels injected by macros do not break hygiene.
+
+// Issue #24278: The label/lifetime shadowing checker from #24162
+// conservatively ignores hygiene, and thus issues warnings that are
+// both true- and false-positives for this test.
+
+macro_rules! loop_x {
+    ($e: expr) => {
+        // $e shouldn't be able to interact with this 'x
+        'x: loop { $e }
+    }
+}
+
+macro_rules! run_once {
+    ($e: expr) => {
+        // ditto
+        'x: for _ in 0..1 { $e }
+    }
+}
+
+macro_rules! while_x {
+    ($e: expr) => {
+        // ditto
+        'x: while 1 + 1 == 2 { $e }
+    }
+}
+
+pub fn main() {
+    'x: for _ in 0..1 {
+        // this 'x should refer to the outer loop, lexically
+        loop_x!(break 'x);
+        panic!("break doesn't act hygienically inside for loop");
+    }
+
+    'x: loop {
+        // ditto
+        loop_x!(break 'x);
+        panic!("break doesn't act hygienically inside infinite loop");
+    }
+
+    'x: while 1 + 1 == 2 {
+        while_x!(break 'x);
+        panic!("break doesn't act hygienically inside infinite while loop");
+    }
+
+    'x: for _ in 0..1 {
+        // ditto
+        run_once!(continue 'x);
+        panic!("continue doesn't act hygienically inside for loop");
+    }
+}
diff --git a/src/test/ui/hygiene/hygienic-labels.stderr b/src/test/ui/hygiene/hygienic-labels.stderr
new file mode 100644
index 00000000000..d88dfce540e
--- /dev/null
+++ b/src/test/ui/hygiene/hygienic-labels.stderr
@@ -0,0 +1,299 @@
+warning: label name `'x` shadows a label name that is already in scope
+  --> $DIR/hygienic-labels.rs:12:9
+   |
+LL |         'x: loop { $e }
+   |         ^^ lifetime 'x already in scope
+...
+LL |     'x: for _ in 0..1 {
+   |     -- first declared here
+LL |         // this 'x should refer to the outer loop, lexically
+LL |         loop_x!(break 'x);
+   |         ------------------ in this macro invocation
+
+warning: label name `'x` shadows a label name that is already in scope
+  --> $DIR/hygienic-labels.rs:37:5
+   |
+LL |     'x: for _ in 0..1 {
+   |     -- first declared here
+...
+LL |     'x: loop {
+   |     ^^ lifetime 'x already in scope
+
+warning: label name `'x` shadows a label name that is already in scope
+  --> $DIR/hygienic-labels.rs:37:5
+   |
+LL |         'x: loop { $e }
+   |         -- first declared here
+...
+LL |     'x: loop {
+   |     ^^ lifetime 'x already in scope
+
+warning: label name `'x` shadows a label name that is already in scope
+  --> $DIR/hygienic-labels.rs:12:9
+   |
+LL |         'x: loop { $e }
+   |         ^^ lifetime 'x already in scope
+...
+LL |     'x: for _ in 0..1 {
+   |     -- first declared here
+...
+LL |         loop_x!(break 'x);
+   |         ------------------ in this macro invocation
+
+warning: label name `'x` shadows a label name that is already in scope
+  --> $DIR/hygienic-labels.rs:12:9
+   |
+LL |         'x: loop { $e }
+   |         ^^
+   |         |
+   |         first declared here
+   |         lifetime 'x already in scope
+...
+LL |         loop_x!(break 'x);
+   |         ------------------ in this macro invocation
+
+warning: label name `'x` shadows a label name that is already in scope
+  --> $DIR/hygienic-labels.rs:12:9
+   |
+LL |         'x: loop { $e }
+   |         ^^ lifetime 'x already in scope
+...
+LL |     'x: loop {
+   |     -- first declared here
+LL |         // ditto
+LL |         loop_x!(break 'x);
+   |         ------------------ in this macro invocation
+
+warning: label name `'x` shadows a label name that is already in scope
+  --> $DIR/hygienic-labels.rs:43:5
+   |
+LL |     'x: for _ in 0..1 {
+   |     -- first declared here
+...
+LL |     'x: while 1 + 1 == 2 {
+   |     ^^ lifetime 'x already in scope
+
+warning: label name `'x` shadows a label name that is already in scope
+  --> $DIR/hygienic-labels.rs:43:5
+   |
+LL |         'x: loop { $e }
+   |         -- first declared here
+...
+LL |     'x: while 1 + 1 == 2 {
+   |     ^^ lifetime 'x already in scope
+
+warning: label name `'x` shadows a label name that is already in scope
+  --> $DIR/hygienic-labels.rs:43:5
+   |
+LL |     'x: loop {
+   |     -- first declared here
+...
+LL |     'x: while 1 + 1 == 2 {
+   |     ^^ lifetime 'x already in scope
+
+warning: label name `'x` shadows a label name that is already in scope
+  --> $DIR/hygienic-labels.rs:43:5
+   |
+LL |         'x: loop { $e }
+   |         -- first declared here
+...
+LL |     'x: while 1 + 1 == 2 {
+   |     ^^ lifetime 'x already in scope
+
+warning: label name `'x` shadows a label name that is already in scope
+  --> $DIR/hygienic-labels.rs:26:9
+   |
+LL |         'x: while 1 + 1 == 2 { $e }
+   |         ^^ lifetime 'x already in scope
+...
+LL |     'x: for _ in 0..1 {
+   |     -- first declared here
+...
+LL |         while_x!(break 'x);
+   |         ------------------- in this macro invocation
+
+warning: label name `'x` shadows a label name that is already in scope
+  --> $DIR/hygienic-labels.rs:26:9
+   |
+LL |         'x: loop { $e }
+   |         -- first declared here
+...
+LL |         'x: while 1 + 1 == 2 { $e }
+   |         ^^ lifetime 'x already in scope
+...
+LL |         while_x!(break 'x);
+   |         ------------------- in this macro invocation
+
+warning: label name `'x` shadows a label name that is already in scope
+  --> $DIR/hygienic-labels.rs:26:9
+   |
+LL |         'x: while 1 + 1 == 2 { $e }
+   |         ^^ lifetime 'x already in scope
+...
+LL |     'x: loop {
+   |     -- first declared here
+...
+LL |         while_x!(break 'x);
+   |         ------------------- in this macro invocation
+
+warning: label name `'x` shadows a label name that is already in scope
+  --> $DIR/hygienic-labels.rs:26:9
+   |
+LL |         'x: loop { $e }
+   |         -- first declared here
+...
+LL |         'x: while 1 + 1 == 2 { $e }
+   |         ^^ lifetime 'x already in scope
+...
+LL |         while_x!(break 'x);
+   |         ------------------- in this macro invocation
+
+warning: label name `'x` shadows a label name that is already in scope
+  --> $DIR/hygienic-labels.rs:26:9
+   |
+LL |         'x: while 1 + 1 == 2 { $e }
+   |         ^^ lifetime 'x already in scope
+...
+LL |     'x: while 1 + 1 == 2 {
+   |     -- first declared here
+LL |         while_x!(break 'x);
+   |         ------------------- in this macro invocation
+
+warning: label name `'x` shadows a label name that is already in scope
+  --> $DIR/hygienic-labels.rs:48:5
+   |
+LL |     'x: for _ in 0..1 {
+   |     -- first declared here
+...
+LL |     'x: for _ in 0..1 {
+   |     ^^ lifetime 'x already in scope
+
+warning: label name `'x` shadows a label name that is already in scope
+  --> $DIR/hygienic-labels.rs:48:5
+   |
+LL |         'x: loop { $e }
+   |         -- first declared here
+...
+LL |     'x: for _ in 0..1 {
+   |     ^^ lifetime 'x already in scope
+
+warning: label name `'x` shadows a label name that is already in scope
+  --> $DIR/hygienic-labels.rs:48:5
+   |
+LL |     'x: loop {
+   |     -- first declared here
+...
+LL |     'x: for _ in 0..1 {
+   |     ^^ lifetime 'x already in scope
+
+warning: label name `'x` shadows a label name that is already in scope
+  --> $DIR/hygienic-labels.rs:48:5
+   |
+LL |         'x: loop { $e }
+   |         -- first declared here
+...
+LL |     'x: for _ in 0..1 {
+   |     ^^ lifetime 'x already in scope
+
+warning: label name `'x` shadows a label name that is already in scope
+  --> $DIR/hygienic-labels.rs:48:5
+   |
+LL |     'x: while 1 + 1 == 2 {
+   |     -- first declared here
+...
+LL |     'x: for _ in 0..1 {
+   |     ^^ lifetime 'x already in scope
+
+warning: label name `'x` shadows a label name that is already in scope
+  --> $DIR/hygienic-labels.rs:48:5
+   |
+LL |         'x: while 1 + 1 == 2 { $e }
+   |         -- first declared here
+...
+LL |     'x: for _ in 0..1 {
+   |     ^^ lifetime 'x already in scope
+
+warning: label name `'x` shadows a label name that is already in scope
+  --> $DIR/hygienic-labels.rs:19:9
+   |
+LL |         'x: for _ in 0..1 { $e }
+   |         ^^ lifetime 'x already in scope
+...
+LL |     'x: for _ in 0..1 {
+   |     -- first declared here
+...
+LL |         run_once!(continue 'x);
+   |         ----------------------- in this macro invocation
+
+warning: label name `'x` shadows a label name that is already in scope
+  --> $DIR/hygienic-labels.rs:19:9
+   |
+LL |         'x: loop { $e }
+   |         -- first declared here
+...
+LL |         'x: for _ in 0..1 { $e }
+   |         ^^ lifetime 'x already in scope
+...
+LL |         run_once!(continue 'x);
+   |         ----------------------- in this macro invocation
+
+warning: label name `'x` shadows a label name that is already in scope
+  --> $DIR/hygienic-labels.rs:19:9
+   |
+LL |         'x: for _ in 0..1 { $e }
+   |         ^^ lifetime 'x already in scope
+...
+LL |     'x: loop {
+   |     -- first declared here
+...
+LL |         run_once!(continue 'x);
+   |         ----------------------- in this macro invocation
+
+warning: label name `'x` shadows a label name that is already in scope
+  --> $DIR/hygienic-labels.rs:19:9
+   |
+LL |         'x: loop { $e }
+   |         -- first declared here
+...
+LL |         'x: for _ in 0..1 { $e }
+   |         ^^ lifetime 'x already in scope
+...
+LL |         run_once!(continue 'x);
+   |         ----------------------- in this macro invocation
+
+warning: label name `'x` shadows a label name that is already in scope
+  --> $DIR/hygienic-labels.rs:19:9
+   |
+LL |         'x: for _ in 0..1 { $e }
+   |         ^^ lifetime 'x already in scope
+...
+LL |     'x: while 1 + 1 == 2 {
+   |     -- first declared here
+...
+LL |         run_once!(continue 'x);
+   |         ----------------------- in this macro invocation
+
+warning: label name `'x` shadows a label name that is already in scope
+  --> $DIR/hygienic-labels.rs:19:9
+   |
+LL |         'x: for _ in 0..1 { $e }
+   |         ^^ lifetime 'x already in scope
+...
+LL |         'x: while 1 + 1 == 2 { $e }
+   |         -- first declared here
+...
+LL |         run_once!(continue 'x);
+   |         ----------------------- in this macro invocation
+
+warning: label name `'x` shadows a label name that is already in scope
+  --> $DIR/hygienic-labels.rs:19:9
+   |
+LL |         'x: for _ in 0..1 { $e }
+   |         ^^ lifetime 'x already in scope
+...
+LL |     'x: for _ in 0..1 {
+   |     -- first declared here
+LL |         // ditto
+LL |         run_once!(continue 'x);
+   |         ----------------------- in this macro invocation
+
diff --git a/src/test/ui/hygiene/issue-44128.rs b/src/test/ui/hygiene/issue-44128.rs
new file mode 100644
index 00000000000..5e03bdb8c5b
--- /dev/null
+++ b/src/test/ui/hygiene/issue-44128.rs
@@ -0,0 +1,17 @@
+// check-pass
+#![allow(unused_must_use)]
+#![feature(decl_macro)]
+
+pub macro create_struct($a:ident) {
+    struct $a;
+    impl Clone for $a {
+        fn clone(&self) -> Self {
+            $a
+        }
+    }
+}
+
+fn main() {
+    create_struct!(Test);
+    Test.clone();
+}
diff --git a/src/test/ui/hygiene/issue-47311.rs b/src/test/ui/hygiene/issue-47311.rs
new file mode 100644
index 00000000000..5c2379a8aba
--- /dev/null
+++ b/src/test/ui/hygiene/issue-47311.rs
@@ -0,0 +1,17 @@
+// check-pass
+// 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() {}
diff --git a/src/test/ui/hygiene/issue-47312.rs b/src/test/ui/hygiene/issue-47312.rs
new file mode 100644
index 00000000000..bbcb3a7f393
--- /dev/null
+++ b/src/test/ui/hygiene/issue-47312.rs
@@ -0,0 +1,21 @@
+// check-pass
+// ignore-pretty pretty-printing is unhygienic
+
+#![feature(decl_macro)]
+#![allow(unused)]
+
+mod foo {
+    pub macro m($s:tt, $i:tt) {
+        $s.$i
+    }
+}
+
+mod bar {
+    struct S(i32);
+    fn f() {
+        let s = S(0);
+        ::foo::m!(s, 0);
+    }
+}
+
+fn main() {}
diff --git a/src/test/ui/hygiene/items.rs b/src/test/ui/hygiene/items.rs
new file mode 100644
index 00000000000..1c625a9728c
--- /dev/null
+++ b/src/test/ui/hygiene/items.rs
@@ -0,0 +1,27 @@
+// check-pass
+// ignore-pretty pretty-printing is unhygienic
+
+#![feature(decl_macro)]
+
+pub macro m($foo:ident, $f:ident, $e:expr) {
+    mod foo {
+        pub fn f() -> u32 { 0 }
+        pub fn $f() -> u64 { 0 }
+    }
+
+    mod $foo {
+        pub fn f() -> i32 { 0 }
+        pub fn $f() -> i64 { 0  }
+    }
+
+    let _: u32 = foo::f();
+    let _: u64 = foo::$f();
+    let _: i32 = $foo::f();
+    let _: i64 = $foo::$f();
+    let _: i64 = $e;
+}
+
+fn main() {
+    m!(foo, f, foo::f());
+    let _: i64 = foo::f();
+}
diff --git a/src/test/ui/hygiene/legacy_interaction.rs b/src/test/ui/hygiene/legacy_interaction.rs
new file mode 100644
index 00000000000..52008eed515
--- /dev/null
+++ b/src/test/ui/hygiene/legacy_interaction.rs
@@ -0,0 +1,42 @@
+// check-pass
+#![allow(dead_code)]
+// ignore-pretty pretty-printing is unhygienic
+
+// aux-build:legacy_interaction.rs
+
+#![feature(decl_macro)]
+#[allow(unused)]
+
+extern crate legacy_interaction;
+// ^ defines
+// ```rust
+//  macro_rules! m {
+//     () => {
+//         fn f() {} // (1)
+//         g() // (2)
+//     }
+// }
+// ```rust
+
+mod def_site {
+    // Unless this macro opts out of hygiene, it should resolve the same wherever it is invoked.
+    pub macro m2() {
+        ::legacy_interaction::m!();
+        f(); // This should resolve to (1)
+        fn g() {} // We want (2) resolve to this, not to (4)
+    }
+}
+
+mod use_site {
+    fn test() {
+        fn f() -> bool { true } // (3)
+        fn g() -> bool { true } // (4)
+
+        ::def_site::m2!();
+
+        let _: bool = f(); // This should resolve to (3)
+        let _: bool = g(); // This should resolve to (4)
+    }
+}
+
+fn main() {}
diff --git a/src/test/ui/hygiene/lexical.rs b/src/test/ui/hygiene/lexical.rs
new file mode 100644
index 00000000000..3d25c720989
--- /dev/null
+++ b/src/test/ui/hygiene/lexical.rs
@@ -0,0 +1,24 @@
+// check-pass
+// ignore-pretty pretty-printing is unhygienic
+
+#![feature(decl_macro)]
+
+mod bar {
+    mod baz {
+        pub fn f() {}
+    }
+
+    pub macro m($f:ident) {
+        baz::f();
+        let _: i32 = $f();
+        {
+            fn $f() -> u32 { 0 }
+            let _: u32 = $f();
+        }
+    }
+}
+
+fn main() {
+    fn f() -> i32 { 0 }
+    bar::m!(f);
+}
diff --git a/src/test/ui/hygiene/local_inner_macros.rs b/src/test/ui/hygiene/local_inner_macros.rs
index f361e3d5d49..71ffcac40d3 100644
--- a/src/test/ui/hygiene/local_inner_macros.rs
+++ b/src/test/ui/hygiene/local_inner_macros.rs
@@ -1,4 +1,4 @@
-// build-pass (FIXME(62277): could be check-pass?)
+// check-pass
 // aux-build:local_inner_macros.rs
 
 extern crate local_inner_macros;
diff --git a/src/test/ui/hygiene/specialization.rs b/src/test/ui/hygiene/specialization.rs
new file mode 100644
index 00000000000..656aa880ad1
--- /dev/null
+++ b/src/test/ui/hygiene/specialization.rs
@@ -0,0 +1,25 @@
+// run-pass
+// ignore-pretty pretty-printing is unhygienic
+
+#![feature(decl_macro)]
+
+trait Tr {
+    fn f(&self) -> &'static str {
+        "This shouldn't happen"
+    }
+}
+
+pub macro m($t:ty) {
+    impl Tr for $t {
+        fn f(&self) -> &'static str {
+            "Run me"
+        }
+    }
+}
+
+struct S;
+m!(S);
+
+fn main() {
+    assert_eq!(S.f(), "Run me");
+}
diff --git a/src/test/ui/hygiene/trait_items-2.rs b/src/test/ui/hygiene/trait_items-2.rs
new file mode 100644
index 00000000000..39edfc37d69
--- /dev/null
+++ b/src/test/ui/hygiene/trait_items-2.rs
@@ -0,0 +1,20 @@
+// check-pass
+// ignore-pretty pretty-printing is unhygienic
+
+#![feature(decl_macro)]
+
+macro m($T:ident, $f:ident) {
+    pub trait $T {
+        fn f(&self) -> u32 { 0 }
+        fn $f(&self) -> i32 { 0 }
+    }
+    impl $T for () {}
+
+    let _: u32 = ().f();
+    let _: i32 = ().$f();
+}
+
+fn main() {
+    m!(T, f);
+    let _: i32 = ().f();
+}
diff --git a/src/test/ui/hygiene/transparent-basic.rs b/src/test/ui/hygiene/transparent-basic.rs
index 69dfc524989..bfa1713e4ed 100644
--- a/src/test/ui/hygiene/transparent-basic.rs
+++ b/src/test/ui/hygiene/transparent-basic.rs
@@ -1,4 +1,4 @@
-// build-pass (FIXME(62277): could be check-pass?)
+// check-pass
 // aux-build:transparent-basic.rs
 
 #![feature(decl_macro, rustc_attrs)]
diff --git a/src/test/ui/hygiene/ty_params.rs b/src/test/ui/hygiene/ty_params.rs
new file mode 100644
index 00000000000..b296bfe5988
--- /dev/null
+++ b/src/test/ui/hygiene/ty_params.rs
@@ -0,0 +1,14 @@
+// check-pass
+// ignore-pretty pretty-printing is unhygienic
+
+#![feature(decl_macro)]
+
+macro m($T:ident) {
+    fn f<T, $T>(t: T, t2: $T) -> (T, $T) {
+        (t, t2)
+    }
+}
+
+m!(T);
+
+fn main() {}
diff --git a/src/test/ui/hygiene/unpretty-debug.rs b/src/test/ui/hygiene/unpretty-debug.rs
new file mode 100644
index 00000000000..6e936bb3d83
--- /dev/null
+++ b/src/test/ui/hygiene/unpretty-debug.rs
@@ -0,0 +1,20 @@
+// check-pass
+// compile-flags: -Zunpretty=expanded,hygiene
+
+// Don't break whenever Symbol numbering changes
+// normalize-stdout-test "\d+#" -> "0#"
+
+// minimal junk
+#![feature(no_core)]
+#![no_core]
+
+macro_rules! foo {
+    ($x: ident) => { y + $x }
+}
+
+fn bar() {
+    let x = 1;
+    foo!(x)
+}
+
+fn y() {}
diff --git a/src/test/ui/hygiene/unpretty-debug.stdout b/src/test/ui/hygiene/unpretty-debug.stdout
new file mode 100644
index 00000000000..beac4c17abf
--- /dev/null
+++ b/src/test/ui/hygiene/unpretty-debug.stdout
@@ -0,0 +1,15 @@
+// check-pass
+// compile-flags: -Zunpretty=expanded,hygiene
+
+// Don't break whenever Symbol numbering changes
+// normalize-stdout-test "\d+#" -> "0#"
+
+// minimal junk
+#![feature /* 0#0 */(no_core)]
+#![no_core /* 0#0 */]
+
+macro_rules! foo /* 0#0 */ { ($ x : ident) => { y + $ x } }
+
+fn bar /* 0#0 */() { let x /* 0#0 */ = 1; y /* 0#1 */ + x /* 0#0 */ }
+
+fn y /* 0#0 */() { }
diff --git a/src/test/ui/hygiene/wrap_unhygienic_example.rs b/src/test/ui/hygiene/wrap_unhygienic_example.rs
new file mode 100644
index 00000000000..50c6b35ab18
--- /dev/null
+++ b/src/test/ui/hygiene/wrap_unhygienic_example.rs
@@ -0,0 +1,34 @@
+// check-pass
+// ignore-pretty pretty-printing is unhygienic
+
+// aux-build:my_crate.rs
+// aux-build:unhygienic_example.rs
+
+#![feature(decl_macro)]
+
+extern crate unhygienic_example;
+extern crate my_crate; // (b)
+
+// Hygienic version of `unhygienic_macro`.
+pub macro hygienic_macro() {
+    fn g() {} // (c)
+    ::unhygienic_example::unhygienic_macro!();
+    // ^ Even though we invoke an unhygienic macro, `hygienic_macro` remains hygienic.
+    // In the above expansion:
+    // (1) `my_crate` always resolves to (b) regardless of invocation site.
+    // (2) The defined function `f` is only usable inside this macro definition.
+    // (3) `g` always resolves to (c) regardless of invocation site.
+    // (4) `$crate::g` remains hygienic and continues to resolve to (a).
+
+    f();
+}
+
+#[allow(unused)]
+fn test_hygienic_macro() {
+    hygienic_macro!();
+
+    fn f() {} // (d) no conflict
+    f(); // resolves to (d)
+}
+
+fn main() {}
diff --git a/src/test/ui/hygiene/xcrate.rs b/src/test/ui/hygiene/xcrate.rs
new file mode 100644
index 00000000000..6981ce3f687
--- /dev/null
+++ b/src/test/ui/hygiene/xcrate.rs
@@ -0,0 +1,12 @@
+// run-pass
+// ignore-pretty pretty-printing is unhygienic
+
+// aux-build:xcrate.rs
+
+#![feature(decl_macro)]
+
+extern crate xcrate;
+
+fn main() {
+    xcrate::test!();
+}