about summary refs log tree commit diff
path: root/src/test
diff options
context:
space:
mode:
Diffstat (limited to 'src/test')
-rw-r--r--src/test/pretty/ast-stmt-expr-attr.rs175
-rw-r--r--src/test/ui-fulldeps/ast_stmt_expr_attr.rs311
-rw-r--r--src/test/ui/did_you_mean/issue-54109-and_instead_of_ampersands.rs31
-rw-r--r--src/test/ui/did_you_mean/issue-54109-and_instead_of_ampersands.stderr95
-rw-r--r--src/test/ui/parser/attr-stmt-expr-attr-bad-2.rs2
-rw-r--r--src/test/ui/parser/attr-stmt-expr-attr-bad-2.stderr8
-rw-r--r--src/test/ui/parser/attr-stmt-expr-attr-bad-3.rs2
-rw-r--r--src/test/ui/parser/attr-stmt-expr-attr-bad-3.stderr8
-rw-r--r--src/test/ui/parser/attr-stmt-expr-attr-bad.rs107
-rw-r--r--src/test/ui/parser/attr-stmt-expr-attr-bad.stderr390
-rw-r--r--src/test/ui/parser/do-catch-suggests-try.rs7
-rw-r--r--src/test/ui/parser/do-catch-suggests-try.stderr17
-rw-r--r--src/test/ui/parser/issue-65257-invalid-var-decl-recovery.rs21
-rw-r--r--src/test/ui/parser/issue-65257-invalid-var-decl-recovery.stderr67
-rw-r--r--src/test/ui/parser/recover-labeled-non-block-expr.rs5
-rw-r--r--src/test/ui/parser/recover-labeled-non-block-expr.stderr17
-rw-r--r--src/test/ui/parser/recovery-attr-on-if.rs9
-rw-r--r--src/test/ui/parser/recovery-attr-on-if.stderr35
-rw-r--r--src/test/ui/parser/stmt_expr_attrs_placement.rs22
-rw-r--r--src/test/ui/parser/stmt_expr_attrs_placement.stderr10
20 files changed, 969 insertions, 370 deletions
diff --git a/src/test/pretty/ast-stmt-expr-attr.rs b/src/test/pretty/ast-stmt-expr-attr.rs
new file mode 100644
index 00000000000..5b975424512
--- /dev/null
+++ b/src/test/pretty/ast-stmt-expr-attr.rs
@@ -0,0 +1,175 @@
+// pp-exact
+
+fn main() { }
+
+#[cfg(FALSE)]
+fn syntax() {
+    let _ = #[attr] box 0;
+    let _ = #[attr] [#![attr] ];
+    let _ = #[attr] [#![attr] 0];
+    let _ = #[attr] [#![attr] 0; 0];
+    let _ = #[attr] [#![attr] 0, 0, 0];
+    let _ = #[attr] foo();
+    let _ = #[attr] x.foo();
+    let _ = #[attr] (#![attr] );
+    let _ = #[attr] (#![attr] #[attr] 0,);
+    let _ = #[attr] (#![attr] #[attr] 0, 0);
+    let _ = #[attr] 0 + #[attr] 0;
+    let _ = #[attr] 0 / #[attr] 0;
+    let _ = #[attr] 0 & #[attr] 0;
+    let _ = #[attr] 0 % #[attr] 0;
+    let _ = #[attr] (0 + 0);
+    let _ = #[attr] !0;
+    let _ = #[attr] -0;
+    let _ = #[attr] false;
+    let _ = #[attr] 0;
+    let _ = #[attr] 'c';
+    let _ = #[attr] x as Y;
+    let _ = #[attr] (x as Y);
+    let _ =
+        #[attr] while true {
+                    #![attr]
+                };
+    let _ =
+        #[attr] while let Some(false) = true {
+                    #![attr]
+                };
+    let _ =
+        #[attr] for x in y {
+                    #![attr]
+                };
+    let _ =
+        #[attr] loop  {
+                    #![attr]
+                };
+    let _ =
+        #[attr] match true {
+                    #![attr]
+                            #[attr]
+                            _ => false,
+                };
+    let _ = #[attr] || #[attr] foo;
+    let _ = #[attr] move || #[attr] foo;
+    let _ =
+        #[attr] ||
+                    #[attr] {
+                                #![attr]
+                                foo
+                            };
+    let _ =
+        #[attr] move ||
+                    #[attr] {
+                                #![attr]
+                                foo
+                            };
+    let _ =
+        #[attr] ||
+                    {
+                        #![attr]
+                        foo
+                    };
+    let _ =
+        #[attr] move ||
+                    {
+                        #![attr]
+                        foo
+                    };
+    let _ =
+        #[attr] {
+                    #![attr]
+                };
+    let _ =
+        #[attr] {
+                    #![attr]
+                    let _ = ();
+                };
+    let _ =
+        #[attr] {
+                    #![attr]
+                    let _ = ();
+                    foo
+                };
+    let _ = #[attr] x = y;
+    let _ = #[attr] (x = y);
+    let _ = #[attr] x += y;
+    let _ = #[attr] (x += y);
+    let _ = #[attr] foo.bar;
+    let _ = (#[attr] foo).bar;
+    let _ = #[attr] foo.0;
+    let _ = (#[attr] foo).0;
+    let _ = #[attr] foo[bar];
+    let _ = (#[attr] foo)[bar];
+    let _ = #[attr] 0..#[attr] 0;
+    let _ = #[attr] 0..;
+    let _ = #[attr] (0..0);
+    let _ = #[attr] (0..);
+    let _ = #[attr] (..0);
+    let _ = #[attr] (..);
+    let _ = #[attr] foo::bar::baz;
+    let _ = #[attr] &0;
+    let _ = #[attr] &mut 0;
+    let _ = #[attr] &#[attr] 0;
+    let _ = #[attr] &mut #[attr] 0;
+    let _ = #[attr] break ;
+    let _ = #[attr] continue ;
+    let _ = #[attr] return;
+    let _ = #[attr] foo!();
+    let _ = #[attr] foo!(# ! [attr]);
+    let _ = #[attr] foo![];
+    let _ = #[attr] foo![# ! [attr]];
+    let _ = #[attr] foo! { };
+    let _ = #[attr] foo! { # ! [attr] };
+    let _ = #[attr] Foo{#![attr] bar: baz,};
+    let _ = #[attr] Foo{#![attr] ..foo};
+    let _ = #[attr] Foo{#![attr] bar: baz, ..foo};
+    let _ = #[attr] (#![attr] 0);
+
+    {
+        #[attr]
+        let _ = 0;
+
+        #[attr]
+        0;
+
+        #[attr]
+        foo!();
+
+        #[attr]
+        foo! { }
+
+        #[attr]
+        foo![];
+    }
+
+    {
+        #[attr]
+        let _ = 0;
+    }
+    {
+
+        #[attr]
+        0
+    }
+    {
+
+        #[attr]
+        {
+            #![attr]
+        }
+    }
+    {
+
+        #[attr]
+        foo!()
+    }
+    {
+
+        #[attr]
+        foo![]
+    }
+    {
+
+        #[attr]
+        foo! { }
+    }
+}
diff --git a/src/test/ui-fulldeps/ast_stmt_expr_attr.rs b/src/test/ui-fulldeps/ast_stmt_expr_attr.rs
deleted file mode 100644
index d6d49df63ef..00000000000
--- a/src/test/ui-fulldeps/ast_stmt_expr_attr.rs
+++ /dev/null
@@ -1,311 +0,0 @@
-// run-pass
-
-#![allow(unused_imports)]
-// ignore-cross-compile
-
-#![feature(rustc_private)]
-
-extern crate syntax;
-extern crate syntax_expand;
-extern crate rustc_parse;
-extern crate rustc_errors;
-
-use rustc_errors::PResult;
-use rustc_parse::parser::attr::*;
-use rustc_parse::new_parser_from_source_str;
-use rustc_parse::parser::Parser;
-use syntax::ast::*;
-use syntax::attr::*;
-use syntax::ast;
-use syntax::sess::ParseSess;
-use syntax::source_map::{FilePathMapping, FileName};
-use syntax::ptr::P;
-use syntax::print::pprust;
-use syntax::token;
-use std::fmt;
-
-// Copied out of syntax::util::parser_testing
-
-pub fn string_to_parser<'a>(ps: &'a ParseSess, source_str: String) -> Parser<'a> {
-    new_parser_from_source_str(ps, FileName::Custom(source_str.clone()), source_str)
-}
-
-fn with_error_checking_parse<'a, T, F>(s: String, ps: &'a ParseSess, f: F) -> PResult<'a, T> where
-    F: FnOnce(&mut Parser<'a>) -> PResult<'a, T>,
-{
-    let mut p = string_to_parser(&ps, s);
-    let x = f(&mut p);
-
-    if ps.span_diagnostic.has_errors() || p.token != token::Eof {
-        if let Err(mut e) = x {
-            e.cancel();
-        }
-        return Err(p.fatal("parse error"));
-    }
-
-    x
-}
-
-fn expr<'a>(s: &str, ps: &'a ParseSess) -> PResult<'a, P<ast::Expr>> {
-    with_error_checking_parse(s.to_string(), ps, |p| {
-        p.parse_expr()
-    })
-}
-
-fn stmt<'a>(s: &str, ps: &'a ParseSess) -> PResult<'a, ast::Stmt> {
-    with_error_checking_parse(s.to_string(), ps, |p| {
-        p.parse_stmt().map(|s| s.unwrap())
-    })
-}
-
-fn attr<'a>(s: &str, ps: &'a ParseSess) -> PResult<'a, ast::Attribute> {
-    with_error_checking_parse(s.to_string(), ps, |p| {
-        p.parse_attribute(true)
-    })
-}
-
-fn str_compare<T, F: Fn(&T) -> String>(e: &str, expected: &[T], actual: &[T], f: F) {
-    let expected: Vec<_> = expected.iter().map(|e| f(e)).collect();
-    let actual: Vec<_> = actual.iter().map(|e| f(e)).collect();
-
-    if expected != actual {
-        panic!("parsed `{}` as {:?}, expected {:?}", e, actual, expected);
-    }
-}
-
-fn sess() -> ParseSess {
-    ParseSess::new(FilePathMapping::empty())
-}
-
-fn check_expr_attrs(es: &str, expected: &[&str]) {
-    let ps = sess();
-    let e = expr(es, &ps).expect("parse error");
-    let actual = &e.attrs;
-    str_compare(es,
-                &expected.iter().map(|r| attr(r, &ps).unwrap()).collect::<Vec<_>>(),
-                &actual,
-                pprust::attribute_to_string);
-}
-
-fn check_stmt_attrs(es: &str, expected: &[&str]) {
-    let ps = sess();
-    let e = stmt(es, &ps).expect("parse error");
-    let actual = e.kind.attrs();
-    str_compare(es,
-                &expected.iter().map(|r| attr(r, &ps).unwrap()).collect::<Vec<_>>(),
-                actual,
-                pprust::attribute_to_string);
-}
-
-fn reject_expr_parse(es: &str) {
-    let ps = sess();
-    match expr(es, &ps) {
-        Ok(_) => panic!("parser did not reject `{}`", es),
-        Err(mut e) => e.cancel(),
-    };
-}
-
-fn reject_stmt_parse(es: &str) {
-    let ps = sess();
-    match stmt(es, &ps) {
-        Ok(_) => panic!("parser did not reject `{}`", es),
-        Err(mut e) => e.cancel(),
-    };
-}
-
-fn main() {
-    syntax::with_default_globals(|| run());
-}
-
-fn run() {
-    let both = &["#[attr]", "#![attr]"];
-    let outer = &["#[attr]"];
-    let none = &[];
-
-    check_expr_attrs("#[attr] box 0", outer);
-    reject_expr_parse("box #![attr] 0");
-
-    check_expr_attrs("#[attr] [#![attr]]", both);
-    check_expr_attrs("#[attr] [#![attr] 0]", both);
-    check_expr_attrs("#[attr] [#![attr] 0; 0]", both);
-    check_expr_attrs("#[attr] [#![attr] 0, 0, 0]", both);
-    reject_expr_parse("[#[attr]]");
-
-    check_expr_attrs("#[attr] foo()", outer);
-    check_expr_attrs("#[attr] x.foo()", outer);
-    reject_expr_parse("foo#[attr]()");
-    reject_expr_parse("foo(#![attr])");
-    reject_expr_parse("x.foo(#![attr])");
-    reject_expr_parse("x.#[attr]foo()");
-    reject_expr_parse("x.#![attr]foo()");
-
-    check_expr_attrs("#[attr] (#![attr])", both);
-    check_expr_attrs("#[attr] (#![attr] #[attr] 0,)", both);
-    check_expr_attrs("#[attr] (#![attr] #[attr] 0, 0)", both);
-
-    check_expr_attrs("#[attr] 0 + #[attr] 0", none);
-    check_expr_attrs("#[attr] 0 / #[attr] 0", none);
-    check_expr_attrs("#[attr] 0 & #[attr] 0", none);
-    check_expr_attrs("#[attr] 0 % #[attr] 0", none);
-    check_expr_attrs("#[attr] (0 + 0)", outer);
-    reject_expr_parse("0 + #![attr] 0");
-
-    check_expr_attrs("#[attr] !0", outer);
-    check_expr_attrs("#[attr] -0", outer);
-    reject_expr_parse("!#![attr] 0");
-    reject_expr_parse("-#![attr] 0");
-
-    check_expr_attrs("#[attr] false", outer);
-    check_expr_attrs("#[attr] 0", outer);
-    check_expr_attrs("#[attr] 'c'", outer);
-
-    check_expr_attrs("#[attr] x as Y", none);
-    check_expr_attrs("#[attr] (x as Y)", outer);
-    reject_expr_parse("x #![attr] as Y");
-
-    reject_expr_parse("#[attr] if false {}");
-    reject_expr_parse("if false #[attr] {}");
-    reject_expr_parse("if false {#![attr]}");
-    reject_expr_parse("if false {} #[attr] else {}");
-    reject_expr_parse("if false {} else #[attr] {}");
-    reject_expr_parse("if false {} else {#![attr]}");
-    reject_expr_parse("if false {} else #[attr] if true {}");
-    reject_expr_parse("if false {} else if true #[attr] {}");
-    reject_expr_parse("if false {} else if true {#![attr]}");
-
-    reject_expr_parse("#[attr] if let Some(false) = false {}");
-    reject_expr_parse("if let Some(false) = false #[attr] {}");
-    reject_expr_parse("if let Some(false) = false {#![attr]}");
-    reject_expr_parse("if let Some(false) = false {} #[attr] else {}");
-    reject_expr_parse("if let Some(false) = false {} else #[attr] {}");
-    reject_expr_parse("if let Some(false) = false {} else {#![attr]}");
-    reject_expr_parse("if let Some(false) = false {} else #[attr] if let Some(false) = true {}");
-    reject_expr_parse("if let Some(false) = false {} else if let Some(false) = true #[attr] {}");
-    reject_expr_parse("if let Some(false) = false {} else if let Some(false) = true {#![attr]}");
-
-    check_expr_attrs("#[attr] while true {#![attr]}", both);
-
-    check_expr_attrs("#[attr] while let Some(false) = true {#![attr]}", both);
-
-    check_expr_attrs("#[attr] for x in y {#![attr]}", both);
-
-    check_expr_attrs("#[attr] loop {#![attr]}", both);
-
-    check_expr_attrs("#[attr] match true {#![attr] #[attr] _ => false}", both);
-
-    check_expr_attrs("#[attr]      || #[attr] foo", outer);
-    check_expr_attrs("#[attr] move || #[attr] foo", outer);
-    check_expr_attrs("#[attr]      || #[attr] { #![attr] foo }", outer);
-    check_expr_attrs("#[attr] move || #[attr] { #![attr] foo }", outer);
-    check_expr_attrs("#[attr]      || { #![attr] foo }", outer);
-    check_expr_attrs("#[attr] move || { #![attr] foo }", outer);
-    reject_expr_parse("|| #![attr] foo");
-    reject_expr_parse("move || #![attr] foo");
-    reject_expr_parse("|| #![attr] {foo}");
-    reject_expr_parse("move || #![attr] {foo}");
-
-    check_expr_attrs("#[attr] { #![attr] }", both);
-    check_expr_attrs("#[attr] { #![attr] let _ = (); }", both);
-    check_expr_attrs("#[attr] { #![attr] let _ = (); foo }", both);
-
-    check_expr_attrs("#[attr] x = y", none);
-    check_expr_attrs("#[attr] (x = y)", outer);
-
-    check_expr_attrs("#[attr] x += y", none);
-    check_expr_attrs("#[attr] (x += y)", outer);
-
-    check_expr_attrs("#[attr] foo.bar", outer);
-    check_expr_attrs("(#[attr] foo).bar", none);
-
-    check_expr_attrs("#[attr] foo.0", outer);
-    check_expr_attrs("(#[attr] foo).0", none);
-
-    check_expr_attrs("#[attr] foo[bar]", outer);
-    check_expr_attrs("(#[attr] foo)[bar]", none);
-
-    check_expr_attrs("#[attr] 0..#[attr] 0", none);
-    check_expr_attrs("#[attr] 0..", none);
-    reject_expr_parse("#[attr] ..#[attr] 0");
-    reject_expr_parse("#[attr] ..");
-
-    check_expr_attrs("#[attr] (0..0)", outer);
-    check_expr_attrs("#[attr] (0..)", outer);
-    check_expr_attrs("#[attr] (..0)", outer);
-    check_expr_attrs("#[attr] (..)", outer);
-
-    check_expr_attrs("#[attr] foo::bar::baz", outer);
-
-    check_expr_attrs("#[attr] &0", outer);
-    check_expr_attrs("#[attr] &mut 0", outer);
-    check_expr_attrs("#[attr] & #[attr] 0", outer);
-    check_expr_attrs("#[attr] &mut #[attr] 0", outer);
-    reject_expr_parse("#[attr] &#![attr] 0");
-    reject_expr_parse("#[attr] &mut #![attr] 0");
-
-    check_expr_attrs("#[attr] break", outer);
-    check_expr_attrs("#[attr] continue", outer);
-    check_expr_attrs("#[attr] return", outer);
-
-    check_expr_attrs("#[attr] foo!()", outer);
-    check_expr_attrs("#[attr] foo!(#![attr])", outer);
-    check_expr_attrs("#[attr] foo![]", outer);
-    check_expr_attrs("#[attr] foo![#![attr]]", outer);
-    check_expr_attrs("#[attr] foo!{}", outer);
-    check_expr_attrs("#[attr] foo!{#![attr]}", outer);
-
-    check_expr_attrs("#[attr] Foo { #![attr] bar: baz }", both);
-    check_expr_attrs("#[attr] Foo { #![attr] ..foo }", both);
-    check_expr_attrs("#[attr] Foo { #![attr] bar: baz, ..foo }", both);
-
-    check_expr_attrs("#[attr] (#![attr] 0)", both);
-
-    // Look at statements in their natural habitat...
-    check_expr_attrs("{
-        #[attr] let _ = 0;
-        #[attr] 0;
-        #[attr] foo!();
-        #[attr] foo!{}
-        #[attr] foo![];
-    }", none);
-
-    check_stmt_attrs("#[attr] let _ = 0", outer);
-    check_stmt_attrs("#[attr] 0",         outer);
-    check_stmt_attrs("#[attr] {#![attr]}", both);
-    check_stmt_attrs("#[attr] foo!()",    outer);
-    check_stmt_attrs("#[attr] foo![]",    outer);
-    check_stmt_attrs("#[attr] foo!{}",    outer);
-
-    reject_stmt_parse("#[attr] #![attr] let _ = 0");
-    reject_stmt_parse("#[attr] #![attr] 0");
-    reject_stmt_parse("#[attr] #![attr] foo!()");
-    reject_stmt_parse("#[attr] #![attr] foo![]");
-    reject_stmt_parse("#[attr] #![attr] foo!{}");
-
-    // FIXME: Allow attributes in pattern constexprs?
-    // note: requires parens in patterns to allow disambiguation
-
-    reject_expr_parse("match 0 {
-        0..=#[attr] 10 => ()
-    }");
-    reject_expr_parse("match 0 {
-        0..=#[attr] -10 => ()
-    }");
-    reject_expr_parse("match 0 {
-        0..=-#[attr] 10 => ()
-    }");
-    reject_expr_parse("match 0 {
-        0..=#[attr] FOO => ()
-    }");
-
-    // make sure we don't catch this bug again...
-    reject_expr_parse("{
-        fn foo() {
-            #[attr];
-        }
-    }");
-    reject_expr_parse("{
-        fn foo() {
-            #[attr]
-        }
-    }");
-}
diff --git a/src/test/ui/did_you_mean/issue-54109-and_instead_of_ampersands.rs b/src/test/ui/did_you_mean/issue-54109-and_instead_of_ampersands.rs
index 687479bad3f..44421b077fa 100644
--- a/src/test/ui/did_you_mean/issue-54109-and_instead_of_ampersands.rs
+++ b/src/test/ui/did_you_mean/issue-54109-and_instead_of_ampersands.rs
@@ -1,17 +1,25 @@
+fn main() {}
+
 fn test_and() {
     let a = true;
     let b = false;
-    if a and b {
-        //~^ ERROR expected `{`, found `and`
+
+    let _ = a and b; //~ ERROR `and` is not a logical operator
+
+    if a and b { //~ ERROR `and` is not a logical operator
         println!("both");
     }
+
+    let _recovery_witness: () = 0; //~ ERROR mismatched types
 }
 
 fn test_or() {
     let a = true;
     let b = false;
-    if a or b {
-        //~^ ERROR expected `{`, found `or`
+
+    let _ = a or b; //~ ERROR `or` is not a logical operator
+
+    if a or b { //~ ERROR `or` is not a logical operator
         println!("both");
     }
 }
@@ -19,8 +27,7 @@ fn test_or() {
 fn test_and_par() {
     let a = true;
     let b = false;
-    if (a and b) {
-        //~^ ERROR expected one of `!`, `)`, `,`, `.`, `::`, `?`, `{`, or an operator, found `and`
+    if (a and b) {  //~ ERROR `and` is not a logical operator
         println!("both");
     }
 }
@@ -28,8 +35,7 @@ fn test_and_par() {
 fn test_or_par() {
     let a = true;
     let b = false;
-    if (a or b) {
-        //~^ ERROR expected one of `!`, `)`, `,`, `.`, `::`, `?`, `{`, or an operator, found `or`
+    if (a or b) {  //~ ERROR `or` is not a logical operator
         println!("both");
     }
 }
@@ -37,8 +43,7 @@ fn test_or_par() {
 fn test_while_and() {
     let a = true;
     let b = false;
-    while a and b {
-        //~^ ERROR expected one of `!`, `.`, `::`, `?`, `{`, or an operator, found `and`
+    while a and b {  //~ ERROR `and` is not a logical operator
         println!("both");
     }
 }
@@ -46,11 +51,7 @@ fn test_while_and() {
 fn test_while_or() {
     let a = true;
     let b = false;
-    while a or b {
-        //~^ ERROR expected one of `!`, `.`, `::`, `?`, `{`, or an operator, found `or`
+    while a or b { //~ ERROR `or` is not a logical operator
         println!("both");
     }
 }
-
-fn main() {
-}
diff --git a/src/test/ui/did_you_mean/issue-54109-and_instead_of_ampersands.stderr b/src/test/ui/did_you_mean/issue-54109-and_instead_of_ampersands.stderr
index f230395f7a5..528c62f501e 100644
--- a/src/test/ui/did_you_mean/issue-54109-and_instead_of_ampersands.stderr
+++ b/src/test/ui/did_you_mean/issue-54109-and_instead_of_ampersands.stderr
@@ -1,58 +1,75 @@
-error: expected `{`, found `and`
-  --> $DIR/issue-54109-and_instead_of_ampersands.rs:4:10
+error: `and` is not a logical operator
+  --> $DIR/issue-54109-and_instead_of_ampersands.rs:7:15
+   |
+LL |     let _ = a and b;
+   |               ^^^ help: use `&&` to perform logical conjunction
+   |
+   = note: unlike in e.g., python and PHP, `&&` and `||` are used for logical operators
+
+error: `and` is not a logical operator
+  --> $DIR/issue-54109-and_instead_of_ampersands.rs:9:10
    |
 LL |     if a and b {
-   |     --   ^^^
-   |     |    |
-   |     |    expected `{`
-   |     |    help: use `&&` instead of `and` for the boolean operator
-   |     this `if` statement has a condition, but no block
+   |          ^^^ help: use `&&` to perform logical conjunction
+   |
+   = note: unlike in e.g., python and PHP, `&&` and `||` are used for logical operators
+
+error: `or` is not a logical operator
+  --> $DIR/issue-54109-and_instead_of_ampersands.rs:20:15
+   |
+LL |     let _ = a or b;
+   |               ^^ help: use `||` to perform logical disjunction
+   |
+   = note: unlike in e.g., python and PHP, `&&` and `||` are used for logical operators
 
-error: expected `{`, found `or`
-  --> $DIR/issue-54109-and_instead_of_ampersands.rs:13:10
+error: `or` is not a logical operator
+  --> $DIR/issue-54109-and_instead_of_ampersands.rs:22:10
    |
 LL |     if a or b {
-   |     --   ^^
-   |     |    |
-   |     |    expected `{`
-   |     |    help: use `||` instead of `or` for the boolean operator
-   |     this `if` statement has a condition, but no block
+   |          ^^ help: use `||` to perform logical disjunction
+   |
+   = note: unlike in e.g., python and PHP, `&&` and `||` are used for logical operators
 
-error: expected one of `!`, `)`, `,`, `.`, `::`, `?`, `{`, or an operator, found `and`
-  --> $DIR/issue-54109-and_instead_of_ampersands.rs:22:11
+error: `and` is not a logical operator
+  --> $DIR/issue-54109-and_instead_of_ampersands.rs:30:11
    |
 LL |     if (a and b) {
-   |           ^^^
-   |           |
-   |           expected one of 8 possible tokens
-   |           help: use `&&` instead of `and` for the boolean operator
+   |           ^^^ help: use `&&` to perform logical conjunction
+   |
+   = note: unlike in e.g., python and PHP, `&&` and `||` are used for logical operators
 
-error: expected one of `!`, `)`, `,`, `.`, `::`, `?`, `{`, or an operator, found `or`
-  --> $DIR/issue-54109-and_instead_of_ampersands.rs:31:11
+error: `or` is not a logical operator
+  --> $DIR/issue-54109-and_instead_of_ampersands.rs:38:11
    |
 LL |     if (a or b) {
-   |           ^^
-   |           |
-   |           expected one of 8 possible tokens
-   |           help: use `||` instead of `or` for the boolean operator
+   |           ^^ help: use `||` to perform logical disjunction
+   |
+   = note: unlike in e.g., python and PHP, `&&` and `||` are used for logical operators
 
-error: expected one of `!`, `.`, `::`, `?`, `{`, or an operator, found `and`
-  --> $DIR/issue-54109-and_instead_of_ampersands.rs:40:13
+error: `and` is not a logical operator
+  --> $DIR/issue-54109-and_instead_of_ampersands.rs:46:13
    |
 LL |     while a and b {
-   |             ^^^
-   |             |
-   |             expected one of `!`, `.`, `::`, `?`, `{`, or an operator
-   |             help: use `&&` instead of `and` for the boolean operator
+   |             ^^^ help: use `&&` to perform logical conjunction
+   |
+   = note: unlike in e.g., python and PHP, `&&` and `||` are used for logical operators
 
-error: expected one of `!`, `.`, `::`, `?`, `{`, or an operator, found `or`
-  --> $DIR/issue-54109-and_instead_of_ampersands.rs:49:13
+error: `or` is not a logical operator
+  --> $DIR/issue-54109-and_instead_of_ampersands.rs:54:13
    |
 LL |     while a or b {
-   |             ^^
-   |             |
-   |             expected one of `!`, `.`, `::`, `?`, `{`, or an operator
-   |             help: use `||` instead of `or` for the boolean operator
+   |             ^^ help: use `||` to perform logical disjunction
+   |
+   = note: unlike in e.g., python and PHP, `&&` and `||` are used for logical operators
+
+error[E0308]: mismatched types
+  --> $DIR/issue-54109-and_instead_of_ampersands.rs:13:33
+   |
+LL |     let _recovery_witness: () = 0;
+   |                            --   ^ expected `()`, found integer
+   |                            |
+   |                            expected due to this
 
-error: aborting due to 6 previous errors
+error: aborting due to 9 previous errors
 
+For more information about this error, try `rustc --explain E0308`.
diff --git a/src/test/ui/parser/attr-stmt-expr-attr-bad-2.rs b/src/test/ui/parser/attr-stmt-expr-attr-bad-2.rs
new file mode 100644
index 00000000000..e5ac59ae463
--- /dev/null
+++ b/src/test/ui/parser/attr-stmt-expr-attr-bad-2.rs
@@ -0,0 +1,2 @@
+#[cfg(FALSE)] fn e() { let _ = x.#![attr]foo(); }
+//~^ ERROR unexpected token: `#`
diff --git a/src/test/ui/parser/attr-stmt-expr-attr-bad-2.stderr b/src/test/ui/parser/attr-stmt-expr-attr-bad-2.stderr
new file mode 100644
index 00000000000..ca1043250ba
--- /dev/null
+++ b/src/test/ui/parser/attr-stmt-expr-attr-bad-2.stderr
@@ -0,0 +1,8 @@
+error: unexpected token: `#`
+  --> $DIR/attr-stmt-expr-attr-bad-2.rs:1:34
+   |
+LL | #[cfg(FALSE)] fn e() { let _ = x.#![attr]foo(); }
+   |                                  ^
+
+error: aborting due to previous error
+
diff --git a/src/test/ui/parser/attr-stmt-expr-attr-bad-3.rs b/src/test/ui/parser/attr-stmt-expr-attr-bad-3.rs
new file mode 100644
index 00000000000..7dc71af52f4
--- /dev/null
+++ b/src/test/ui/parser/attr-stmt-expr-attr-bad-3.rs
@@ -0,0 +1,2 @@
+#[cfg(FALSE)] fn e() { let _ = x.#[attr]foo(); }
+//~^ ERROR unexpected token: `#`
diff --git a/src/test/ui/parser/attr-stmt-expr-attr-bad-3.stderr b/src/test/ui/parser/attr-stmt-expr-attr-bad-3.stderr
new file mode 100644
index 00000000000..ab9366d042a
--- /dev/null
+++ b/src/test/ui/parser/attr-stmt-expr-attr-bad-3.stderr
@@ -0,0 +1,8 @@
+error: unexpected token: `#`
+  --> $DIR/attr-stmt-expr-attr-bad-3.rs:1:34
+   |
+LL | #[cfg(FALSE)] fn e() { let _ = x.#[attr]foo(); }
+   |                                  ^
+
+error: aborting due to previous error
+
diff --git a/src/test/ui/parser/attr-stmt-expr-attr-bad.rs b/src/test/ui/parser/attr-stmt-expr-attr-bad.rs
new file mode 100644
index 00000000000..ef10010ed0e
--- /dev/null
+++ b/src/test/ui/parser/attr-stmt-expr-attr-bad.rs
@@ -0,0 +1,107 @@
+fn main() {}
+
+#[cfg(FALSE)] fn e() { let _ = box #![attr] 0; }
+//~^ ERROR an inner attribute is not permitted in this context
+#[cfg(FALSE)] fn e() { let _ = [#[attr]]; }
+//~^ ERROR expected expression, found `]`
+#[cfg(FALSE)] fn e() { let _ = foo#[attr](); }
+//~^ ERROR expected one of
+#[cfg(FALSE)] fn e() { let _ = foo(#![attr]); }
+//~^ ERROR an inner attribute is not permitted in this context
+//~| ERROR expected expression, found `)`
+#[cfg(FALSE)] fn e() { let _ = x.foo(#![attr]); }
+//~^ ERROR an inner attribute is not permitted in this context
+//~| ERROR expected expression, found `)`
+#[cfg(FALSE)] fn e() { let _ = 0 + #![attr] 0; }
+//~^ ERROR an inner attribute is not permitted in this context
+#[cfg(FALSE)] fn e() { let _ = !#![attr] 0; }
+//~^ ERROR an inner attribute is not permitted in this context
+#[cfg(FALSE)] fn e() { let _ = -#![attr] 0; }
+//~^ ERROR an inner attribute is not permitted in this context
+#[cfg(FALSE)] fn e() { let _ = x #![attr] as Y; }
+//~^ ERROR expected one of
+#[cfg(FALSE)] fn e() { let _ = || #![attr] foo; }
+//~^ ERROR an inner attribute is not permitted in this context
+#[cfg(FALSE)] fn e() { let _ = move || #![attr] foo; }
+//~^ ERROR an inner attribute is not permitted in this context
+#[cfg(FALSE)] fn e() { let _ = || #![attr] {foo}; }
+//~^ ERROR an inner attribute is not permitted in this context
+#[cfg(FALSE)] fn e() { let _ = move || #![attr] {foo}; }
+//~^ ERROR an inner attribute is not permitted in this context
+#[cfg(FALSE)] fn e() { let _ = #[attr] ..#[attr] 0; }
+//~^ ERROR expected expression, found `..`
+#[cfg(FALSE)] fn e() { let _ = #[attr] ..; }
+//~^ ERROR expected expression, found `..`
+#[cfg(FALSE)] fn e() { let _ = #[attr] &#![attr] 0; }
+//~^ ERROR an inner attribute is not permitted in this context
+#[cfg(FALSE)] fn e() { let _ = #[attr] &mut #![attr] 0; }
+//~^ ERROR an inner attribute is not permitted in this context
+#[cfg(FALSE)] fn e() { let _ = #[attr] if 0 {}; }
+//~^ ERROR attributes are not yet allowed on `if` expressions
+#[cfg(FALSE)] fn e() { let _ = if 0 #[attr] {}; }
+//~^ ERROR expected `{`, found `#`
+#[cfg(FALSE)] fn e() { let _ = if 0 {#![attr]}; }
+//~^ ERROR an inner attribute is not permitted in this context
+#[cfg(FALSE)] fn e() { let _ = if 0 {} #[attr] else {}; }
+//~^ ERROR expected one of
+#[cfg(FALSE)] fn e() { let _ = if 0 {} else #[attr] {}; }
+//~^ ERROR expected `{`, found `#`
+#[cfg(FALSE)] fn e() { let _ = if 0 {} else {#![attr]}; }
+//~^ ERROR an inner attribute is not permitted in this context
+#[cfg(FALSE)] fn e() { let _ = if 0 {} else #[attr] if 0 {}; }
+//~^ ERROR attributes are not yet allowed on `if` expressions
+//~| ERROR expected `{`, found `#`
+#[cfg(FALSE)] fn e() { let _ = if 0 {} else if 0 #[attr] {}; }
+//~^ ERROR expected `{`, found `#`
+#[cfg(FALSE)] fn e() { let _ = if 0 {} else if 0 {#![attr]}; }
+//~^ ERROR an inner attribute is not permitted in this context
+#[cfg(FALSE)] fn e() { let _ = #[attr] if let _ = 0 {}; }
+//~^ ERROR attributes are not yet allowed on `if` expressions
+#[cfg(FALSE)] fn e() { let _ = if let _ = 0 #[attr] {}; }
+//~^ ERROR expected `{`, found `#`
+#[cfg(FALSE)] fn e() { let _ = if let _ = 0 {#![attr]}; }
+//~^ ERROR an inner attribute is not permitted in this context
+#[cfg(FALSE)] fn e() { let _ = if let _ = 0 {} #[attr] else {}; }
+//~^ ERROR expected one of
+#[cfg(FALSE)] fn e() { let _ = if let _ = 0 {} else #[attr] {}; }
+//~^ ERROR expected `{`, found `#`
+#[cfg(FALSE)] fn e() { let _ = if let _ = 0 {} else {#![attr]}; }
+//~^ ERROR an inner attribute is not permitted in this context
+#[cfg(FALSE)] fn e() { let _ = if let _ = 0 {} else #[attr] if let _ = 0 {}; }
+//~^ ERROR attributes are not yet allowed on `if` expressions
+//~| ERROR expected `{`, found `#`
+#[cfg(FALSE)] fn e() { let _ = if let _ = 0 {} else if let _ = 0 #[attr] {}; }
+//~^ ERROR expected `{`, found `#`
+#[cfg(FALSE)] fn e() { let _ = if let _ = 0 {} else if let _ = 0 {#![attr]}; }
+//~^ ERROR an inner attribute is not permitted in this context
+
+#[cfg(FALSE)] fn s() { #[attr] #![attr] let _ = 0; }
+//~^ ERROR an inner attribute is not permitted following an outer attribute
+#[cfg(FALSE)] fn s() { #[attr] #![attr] 0; }
+//~^ ERROR an inner attribute is not permitted following an outer attribute
+#[cfg(FALSE)] fn s() { #[attr] #![attr] foo!(); }
+//~^ ERROR an inner attribute is not permitted following an outer attribute
+#[cfg(FALSE)] fn s() { #[attr] #![attr] foo![]; }
+//~^ ERROR an inner attribute is not permitted following an outer attribute
+#[cfg(FALSE)] fn s() { #[attr] #![attr] foo!{}; }
+//~^ ERROR an inner attribute is not permitted following an outer attribute
+
+// FIXME: Allow attributes in pattern constexprs?
+// note: requires parens in patterns to allow disambiguation
+
+#[cfg(FALSE)] fn e() { match 0 { 0..=#[attr] 10 => () } }
+//~^ ERROR `X..=` range patterns are not supported
+//~| ERROR expected one of `=>`, `if`, or `|`, found `#`
+#[cfg(FALSE)] fn e() { match 0 { 0..=#[attr] -10 => () } }
+//~^ ERROR `X..=` range patterns are not supported
+//~| ERROR expected one of `=>`, `if`, or `|`, found `#`
+#[cfg(FALSE)] fn e() { match 0 { 0..=-#[attr] 10 => () } }
+//~^ ERROR unexpected token: `#`
+#[cfg(FALSE)] fn e() { match 0 { 0..=#[attr] FOO => () } }
+//~^ ERROR `X..=` range patterns are not supported
+//~| ERROR expected one of `=>`, `if`, or `|`, found `#`
+
+// make sure we don't catch this bug again...
+#[cfg(FALSE)] fn e() { { fn foo() { #[attr]; } } }
+//~^ ERROR expected statement after outer attribute
+#[cfg(FALSE)] fn e() { { fn foo() { #[attr] } } }
diff --git a/src/test/ui/parser/attr-stmt-expr-attr-bad.stderr b/src/test/ui/parser/attr-stmt-expr-attr-bad.stderr
new file mode 100644
index 00000000000..9a0d3176714
--- /dev/null
+++ b/src/test/ui/parser/attr-stmt-expr-attr-bad.stderr
@@ -0,0 +1,390 @@
+error: an inner attribute is not permitted in this context
+  --> $DIR/attr-stmt-expr-attr-bad.rs:3:36
+   |
+LL | #[cfg(FALSE)] fn e() { let _ = box #![attr] 0; }
+   |                                    ^^^^^^^^
+   |
+   = note: inner attributes, like `#![no_std]`, annotate the item enclosing them, and are usually found at the beginning of source files. Outer attributes, like `#[test]`, annotate the item following them.
+
+error: expected expression, found `]`
+  --> $DIR/attr-stmt-expr-attr-bad.rs:5:40
+   |
+LL | #[cfg(FALSE)] fn e() { let _ = [#[attr]]; }
+   |                                        ^ expected expression
+
+error: expected one of `!`, `.`, `::`, `;`, `?`, `{`, or an operator, found `#`
+  --> $DIR/attr-stmt-expr-attr-bad.rs:7:35
+   |
+LL | #[cfg(FALSE)] fn e() { let _ = foo#[attr](); }
+   |                                   ^ expected one of 7 possible tokens
+
+error: an inner attribute is not permitted in this context
+  --> $DIR/attr-stmt-expr-attr-bad.rs:9:36
+   |
+LL | #[cfg(FALSE)] fn e() { let _ = foo(#![attr]); }
+   |                                    ^^^^^^^^
+   |
+   = note: inner attributes, like `#![no_std]`, annotate the item enclosing them, and are usually found at the beginning of source files. Outer attributes, like `#[test]`, annotate the item following them.
+
+error: expected expression, found `)`
+  --> $DIR/attr-stmt-expr-attr-bad.rs:9:44
+   |
+LL | #[cfg(FALSE)] fn e() { let _ = foo(#![attr]); }
+   |                                            ^ expected expression
+
+error: an inner attribute is not permitted in this context
+  --> $DIR/attr-stmt-expr-attr-bad.rs:12:38
+   |
+LL | #[cfg(FALSE)] fn e() { let _ = x.foo(#![attr]); }
+   |                                      ^^^^^^^^
+   |
+   = note: inner attributes, like `#![no_std]`, annotate the item enclosing them, and are usually found at the beginning of source files. Outer attributes, like `#[test]`, annotate the item following them.
+
+error: expected expression, found `)`
+  --> $DIR/attr-stmt-expr-attr-bad.rs:12:46
+   |
+LL | #[cfg(FALSE)] fn e() { let _ = x.foo(#![attr]); }
+   |                                              ^ expected expression
+
+error: an inner attribute is not permitted in this context
+  --> $DIR/attr-stmt-expr-attr-bad.rs:15:36
+   |
+LL | #[cfg(FALSE)] fn e() { let _ = 0 + #![attr] 0; }
+   |                                    ^^^^^^^^
+   |
+   = note: inner attributes, like `#![no_std]`, annotate the item enclosing them, and are usually found at the beginning of source files. Outer attributes, like `#[test]`, annotate the item following them.
+
+error: an inner attribute is not permitted in this context
+  --> $DIR/attr-stmt-expr-attr-bad.rs:17:33
+   |
+LL | #[cfg(FALSE)] fn e() { let _ = !#![attr] 0; }
+   |                                 ^^^^^^^^
+   |
+   = note: inner attributes, like `#![no_std]`, annotate the item enclosing them, and are usually found at the beginning of source files. Outer attributes, like `#[test]`, annotate the item following them.
+
+error: an inner attribute is not permitted in this context
+  --> $DIR/attr-stmt-expr-attr-bad.rs:19:33
+   |
+LL | #[cfg(FALSE)] fn e() { let _ = -#![attr] 0; }
+   |                                 ^^^^^^^^
+   |
+   = note: inner attributes, like `#![no_std]`, annotate the item enclosing them, and are usually found at the beginning of source files. Outer attributes, like `#[test]`, annotate the item following them.
+
+error: expected one of `!`, `.`, `::`, `;`, `?`, `{`, or an operator, found `#`
+  --> $DIR/attr-stmt-expr-attr-bad.rs:21:34
+   |
+LL | #[cfg(FALSE)] fn e() { let _ = x #![attr] as Y; }
+   |                                  ^ expected one of 7 possible tokens
+
+error: an inner attribute is not permitted in this context
+  --> $DIR/attr-stmt-expr-attr-bad.rs:23:35
+   |
+LL | #[cfg(FALSE)] fn e() { let _ = || #![attr] foo; }
+   |                                   ^^^^^^^^
+   |
+   = note: inner attributes, like `#![no_std]`, annotate the item enclosing them, and are usually found at the beginning of source files. Outer attributes, like `#[test]`, annotate the item following them.
+
+error: an inner attribute is not permitted in this context
+  --> $DIR/attr-stmt-expr-attr-bad.rs:25:40
+   |
+LL | #[cfg(FALSE)] fn e() { let _ = move || #![attr] foo; }
+   |                                        ^^^^^^^^
+   |
+   = note: inner attributes, like `#![no_std]`, annotate the item enclosing them, and are usually found at the beginning of source files. Outer attributes, like `#[test]`, annotate the item following them.
+
+error: an inner attribute is not permitted in this context
+  --> $DIR/attr-stmt-expr-attr-bad.rs:27:35
+   |
+LL | #[cfg(FALSE)] fn e() { let _ = || #![attr] {foo}; }
+   |                                   ^^^^^^^^
+   |
+   = note: inner attributes, like `#![no_std]`, annotate the item enclosing them, and are usually found at the beginning of source files. Outer attributes, like `#[test]`, annotate the item following them.
+
+error: an inner attribute is not permitted in this context
+  --> $DIR/attr-stmt-expr-attr-bad.rs:29:40
+   |
+LL | #[cfg(FALSE)] fn e() { let _ = move || #![attr] {foo}; }
+   |                                        ^^^^^^^^
+   |
+   = note: inner attributes, like `#![no_std]`, annotate the item enclosing them, and are usually found at the beginning of source files. Outer attributes, like `#[test]`, annotate the item following them.
+
+error: expected expression, found `..`
+  --> $DIR/attr-stmt-expr-attr-bad.rs:31:40
+   |
+LL | #[cfg(FALSE)] fn e() { let _ = #[attr] ..#[attr] 0; }
+   |                                        ^^ expected expression
+
+error: expected expression, found `..`
+  --> $DIR/attr-stmt-expr-attr-bad.rs:33:40
+   |
+LL | #[cfg(FALSE)] fn e() { let _ = #[attr] ..; }
+   |                                        ^^ expected expression
+
+error: an inner attribute is not permitted in this context
+  --> $DIR/attr-stmt-expr-attr-bad.rs:35:41
+   |
+LL | #[cfg(FALSE)] fn e() { let _ = #[attr] &#![attr] 0; }
+   |                                         ^^^^^^^^
+   |
+   = note: inner attributes, like `#![no_std]`, annotate the item enclosing them, and are usually found at the beginning of source files. Outer attributes, like `#[test]`, annotate the item following them.
+
+error: an inner attribute is not permitted in this context
+  --> $DIR/attr-stmt-expr-attr-bad.rs:37:45
+   |
+LL | #[cfg(FALSE)] fn e() { let _ = #[attr] &mut #![attr] 0; }
+   |                                             ^^^^^^^^
+   |
+   = note: inner attributes, like `#![no_std]`, annotate the item enclosing them, and are usually found at the beginning of source files. Outer attributes, like `#[test]`, annotate the item following them.
+
+error: attributes are not yet allowed on `if` expressions
+  --> $DIR/attr-stmt-expr-attr-bad.rs:39:32
+   |
+LL | #[cfg(FALSE)] fn e() { let _ = #[attr] if 0 {}; }
+   |                                ^^^^^^^
+
+error: expected `{`, found `#`
+  --> $DIR/attr-stmt-expr-attr-bad.rs:41:37
+   |
+LL | #[cfg(FALSE)] fn e() { let _ = if 0 #[attr] {}; }
+   |                                --   ^       --- help: try placing this code inside a block: `{ {}; }`
+   |                                |    |
+   |                                |    expected `{`
+   |                                this `if` statement has a condition, but no block
+
+error: an inner attribute is not permitted in this context
+  --> $DIR/attr-stmt-expr-attr-bad.rs:43:38
+   |
+LL | #[cfg(FALSE)] fn e() { let _ = if 0 {#![attr]}; }
+   |                                      ^^^^^^^^
+   |
+   = note: inner attributes, like `#![no_std]`, annotate the item enclosing them, and are usually found at the beginning of source files. Outer attributes, like `#[test]`, annotate the item following them.
+
+error: expected one of `.`, `;`, `?`, `else`, or an operator, found `#`
+  --> $DIR/attr-stmt-expr-attr-bad.rs:45:40
+   |
+LL | #[cfg(FALSE)] fn e() { let _ = if 0 {} #[attr] else {}; }
+   |                                        ^ expected one of `.`, `;`, `?`, `else`, or an operator
+
+error: expected `{`, found `#`
+  --> $DIR/attr-stmt-expr-attr-bad.rs:47:45
+   |
+LL | #[cfg(FALSE)] fn e() { let _ = if 0 {} else #[attr] {}; }
+   |                                             ^       --- help: try placing this code inside a block: `{ {}; }`
+   |                                             |
+   |                                             expected `{`
+
+error: an inner attribute is not permitted in this context
+  --> $DIR/attr-stmt-expr-attr-bad.rs:49:46
+   |
+LL | #[cfg(FALSE)] fn e() { let _ = if 0 {} else {#![attr]}; }
+   |                                              ^^^^^^^^
+   |
+   = note: inner attributes, like `#![no_std]`, annotate the item enclosing them, and are usually found at the beginning of source files. Outer attributes, like `#[test]`, annotate the item following them.
+
+error: attributes are not yet allowed on `if` expressions
+  --> $DIR/attr-stmt-expr-attr-bad.rs:51:45
+   |
+LL | #[cfg(FALSE)] fn e() { let _ = if 0 {} else #[attr] if 0 {}; }
+   |                                             ^^^^^^^
+
+error: expected `{`, found `#`
+  --> $DIR/attr-stmt-expr-attr-bad.rs:51:45
+   |
+LL | #[cfg(FALSE)] fn e() { let _ = if 0 {} else #[attr] if 0 {}; }
+   |                                             ^       -------- help: try placing this code inside a block: `{ if 0 {}; }`
+   |                                             |
+   |                                             expected `{`
+
+error: expected `{`, found `#`
+  --> $DIR/attr-stmt-expr-attr-bad.rs:54:50
+   |
+LL | #[cfg(FALSE)] fn e() { let _ = if 0 {} else if 0 #[attr] {}; }
+   |                                             --   ^       --- help: try placing this code inside a block: `{ {}; }`
+   |                                             |    |
+   |                                             |    expected `{`
+   |                                             this `if` statement has a condition, but no block
+
+error: an inner attribute is not permitted in this context
+  --> $DIR/attr-stmt-expr-attr-bad.rs:56:51
+   |
+LL | #[cfg(FALSE)] fn e() { let _ = if 0 {} else if 0 {#![attr]}; }
+   |                                                   ^^^^^^^^
+   |
+   = note: inner attributes, like `#![no_std]`, annotate the item enclosing them, and are usually found at the beginning of source files. Outer attributes, like `#[test]`, annotate the item following them.
+
+error: attributes are not yet allowed on `if` expressions
+  --> $DIR/attr-stmt-expr-attr-bad.rs:58:32
+   |
+LL | #[cfg(FALSE)] fn e() { let _ = #[attr] if let _ = 0 {}; }
+   |                                ^^^^^^^
+
+error: expected `{`, found `#`
+  --> $DIR/attr-stmt-expr-attr-bad.rs:60:45
+   |
+LL | #[cfg(FALSE)] fn e() { let _ = if let _ = 0 #[attr] {}; }
+   |                                --           ^       --- help: try placing this code inside a block: `{ {}; }`
+   |                                |            |
+   |                                |            expected `{`
+   |                                this `if` statement has a condition, but no block
+
+error: an inner attribute is not permitted in this context
+  --> $DIR/attr-stmt-expr-attr-bad.rs:62:46
+   |
+LL | #[cfg(FALSE)] fn e() { let _ = if let _ = 0 {#![attr]}; }
+   |                                              ^^^^^^^^
+   |
+   = note: inner attributes, like `#![no_std]`, annotate the item enclosing them, and are usually found at the beginning of source files. Outer attributes, like `#[test]`, annotate the item following them.
+
+error: expected one of `.`, `;`, `?`, `else`, or an operator, found `#`
+  --> $DIR/attr-stmt-expr-attr-bad.rs:64:48
+   |
+LL | #[cfg(FALSE)] fn e() { let _ = if let _ = 0 {} #[attr] else {}; }
+   |                                                ^ expected one of `.`, `;`, `?`, `else`, or an operator
+
+error: expected `{`, found `#`
+  --> $DIR/attr-stmt-expr-attr-bad.rs:66:53
+   |
+LL | #[cfg(FALSE)] fn e() { let _ = if let _ = 0 {} else #[attr] {}; }
+   |                                                     ^       --- help: try placing this code inside a block: `{ {}; }`
+   |                                                     |
+   |                                                     expected `{`
+
+error: an inner attribute is not permitted in this context
+  --> $DIR/attr-stmt-expr-attr-bad.rs:68:54
+   |
+LL | #[cfg(FALSE)] fn e() { let _ = if let _ = 0 {} else {#![attr]}; }
+   |                                                      ^^^^^^^^
+   |
+   = note: inner attributes, like `#![no_std]`, annotate the item enclosing them, and are usually found at the beginning of source files. Outer attributes, like `#[test]`, annotate the item following them.
+
+error: attributes are not yet allowed on `if` expressions
+  --> $DIR/attr-stmt-expr-attr-bad.rs:70:53
+   |
+LL | #[cfg(FALSE)] fn e() { let _ = if let _ = 0 {} else #[attr] if let _ = 0 {}; }
+   |                                                     ^^^^^^^
+
+error: expected `{`, found `#`
+  --> $DIR/attr-stmt-expr-attr-bad.rs:70:53
+   |
+LL | #[cfg(FALSE)] fn e() { let _ = if let _ = 0 {} else #[attr] if let _ = 0 {}; }
+   |                                                     ^       ---------------- help: try placing this code inside a block: `{ if let _ = 0 {}; }`
+   |                                                     |
+   |                                                     expected `{`
+
+error: expected `{`, found `#`
+  --> $DIR/attr-stmt-expr-attr-bad.rs:73:66
+   |
+LL | #[cfg(FALSE)] fn e() { let _ = if let _ = 0 {} else if let _ = 0 #[attr] {}; }
+   |                                                     --           ^       --- help: try placing this code inside a block: `{ {}; }`
+   |                                                     |            |
+   |                                                     |            expected `{`
+   |                                                     this `if` statement has a condition, but no block
+
+error: an inner attribute is not permitted in this context
+  --> $DIR/attr-stmt-expr-attr-bad.rs:75:67
+   |
+LL | #[cfg(FALSE)] fn e() { let _ = if let _ = 0 {} else if let _ = 0 {#![attr]}; }
+   |                                                                   ^^^^^^^^
+   |
+   = note: inner attributes, like `#![no_std]`, annotate the item enclosing them, and are usually found at the beginning of source files. Outer attributes, like `#[test]`, annotate the item following them.
+
+error: an inner attribute is not permitted following an outer attribute
+  --> $DIR/attr-stmt-expr-attr-bad.rs:78:32
+   |
+LL | #[cfg(FALSE)] fn s() { #[attr] #![attr] let _ = 0; }
+   |                        ------- ^^^^^^^^ not permitted following an outer attibute
+   |                        |
+   |                        previous outer attribute
+   |
+   = note: inner attributes, like `#![no_std]`, annotate the item enclosing them, and are usually found at the beginning of source files. Outer attributes, like `#[test]`, annotate the item following them.
+
+error: an inner attribute is not permitted following an outer attribute
+  --> $DIR/attr-stmt-expr-attr-bad.rs:80:32
+   |
+LL | #[cfg(FALSE)] fn s() { #[attr] #![attr] 0; }
+   |                        ------- ^^^^^^^^ not permitted following an outer attibute
+   |                        |
+   |                        previous outer attribute
+   |
+   = note: inner attributes, like `#![no_std]`, annotate the item enclosing them, and are usually found at the beginning of source files. Outer attributes, like `#[test]`, annotate the item following them.
+
+error: an inner attribute is not permitted following an outer attribute
+  --> $DIR/attr-stmt-expr-attr-bad.rs:82:32
+   |
+LL | #[cfg(FALSE)] fn s() { #[attr] #![attr] foo!(); }
+   |                        ------- ^^^^^^^^ not permitted following an outer attibute
+   |                        |
+   |                        previous outer attribute
+   |
+   = note: inner attributes, like `#![no_std]`, annotate the item enclosing them, and are usually found at the beginning of source files. Outer attributes, like `#[test]`, annotate the item following them.
+
+error: an inner attribute is not permitted following an outer attribute
+  --> $DIR/attr-stmt-expr-attr-bad.rs:84:32
+   |
+LL | #[cfg(FALSE)] fn s() { #[attr] #![attr] foo![]; }
+   |                        ------- ^^^^^^^^ not permitted following an outer attibute
+   |                        |
+   |                        previous outer attribute
+   |
+   = note: inner attributes, like `#![no_std]`, annotate the item enclosing them, and are usually found at the beginning of source files. Outer attributes, like `#[test]`, annotate the item following them.
+
+error: an inner attribute is not permitted following an outer attribute
+  --> $DIR/attr-stmt-expr-attr-bad.rs:86:32
+   |
+LL | #[cfg(FALSE)] fn s() { #[attr] #![attr] foo!{}; }
+   |                        ------- ^^^^^^^^ not permitted following an outer attibute
+   |                        |
+   |                        previous outer attribute
+   |
+   = note: inner attributes, like `#![no_std]`, annotate the item enclosing them, and are usually found at the beginning of source files. Outer attributes, like `#[test]`, annotate the item following them.
+
+error: `X..=` range patterns are not supported
+  --> $DIR/attr-stmt-expr-attr-bad.rs:92:34
+   |
+LL | #[cfg(FALSE)] fn e() { match 0 { 0..=#[attr] 10 => () } }
+   |                                  ^^^^ help: try using the maximum value for the type: `0..=MAX`
+
+error: expected one of `=>`, `if`, or `|`, found `#`
+  --> $DIR/attr-stmt-expr-attr-bad.rs:92:38
+   |
+LL | #[cfg(FALSE)] fn e() { match 0 { 0..=#[attr] 10 => () } }
+   |                                      ^ expected one of `=>`, `if`, or `|`
+
+error: `X..=` range patterns are not supported
+  --> $DIR/attr-stmt-expr-attr-bad.rs:95:34
+   |
+LL | #[cfg(FALSE)] fn e() { match 0 { 0..=#[attr] -10 => () } }
+   |                                  ^^^^ help: try using the maximum value for the type: `0..=MAX`
+
+error: expected one of `=>`, `if`, or `|`, found `#`
+  --> $DIR/attr-stmt-expr-attr-bad.rs:95:38
+   |
+LL | #[cfg(FALSE)] fn e() { match 0 { 0..=#[attr] -10 => () } }
+   |                                      ^ expected one of `=>`, `if`, or `|`
+
+error: unexpected token: `#`
+  --> $DIR/attr-stmt-expr-attr-bad.rs:98:39
+   |
+LL | #[cfg(FALSE)] fn e() { match 0 { 0..=-#[attr] 10 => () } }
+   |                                       ^
+
+error: `X..=` range patterns are not supported
+  --> $DIR/attr-stmt-expr-attr-bad.rs:100:34
+   |
+LL | #[cfg(FALSE)] fn e() { match 0 { 0..=#[attr] FOO => () } }
+   |                                  ^^^^ help: try using the maximum value for the type: `0..=MAX`
+
+error: expected one of `=>`, `if`, or `|`, found `#`
+  --> $DIR/attr-stmt-expr-attr-bad.rs:100:38
+   |
+LL | #[cfg(FALSE)] fn e() { match 0 { 0..=#[attr] FOO => () } }
+   |                                      ^ expected one of `=>`, `if`, or `|`
+
+error: expected statement after outer attribute
+  --> $DIR/attr-stmt-expr-attr-bad.rs:105:44
+   |
+LL | #[cfg(FALSE)] fn e() { { fn foo() { #[attr]; } } }
+   |                                            ^
+
+error: aborting due to 52 previous errors
+
diff --git a/src/test/ui/parser/do-catch-suggests-try.rs b/src/test/ui/parser/do-catch-suggests-try.rs
index d805ab75882..f64568d06e9 100644
--- a/src/test/ui/parser/do-catch-suggests-try.rs
+++ b/src/test/ui/parser/do-catch-suggests-try.rs
@@ -1,5 +1,10 @@
+#![feature(try_blocks)]
+
 fn main() {
     let _: Option<()> = do catch {};
     //~^ ERROR found removed `do catch` syntax
-    //~^^ HELP following RFC #2388, the new non-placeholder syntax is `try`
+    //~| replace with the new syntax
+    //~| following RFC #2388, the new non-placeholder syntax is `try`
+
+    let _recovery_witness: () = 1; //~ ERROR mismatched types
 }
diff --git a/src/test/ui/parser/do-catch-suggests-try.stderr b/src/test/ui/parser/do-catch-suggests-try.stderr
index e151d4cf8a6..cd8907b7eac 100644
--- a/src/test/ui/parser/do-catch-suggests-try.stderr
+++ b/src/test/ui/parser/do-catch-suggests-try.stderr
@@ -1,10 +1,19 @@
 error: found removed `do catch` syntax
-  --> $DIR/do-catch-suggests-try.rs:2:25
+  --> $DIR/do-catch-suggests-try.rs:4:25
    |
 LL |     let _: Option<()> = do catch {};
-   |                         ^^
+   |                         ^^^^^^^^ help: replace with the new syntax: `try`
    |
-   = help: following RFC #2388, the new non-placeholder syntax is `try`
+   = note: following RFC #2388, the new non-placeholder syntax is `try`
 
-error: aborting due to previous error
+error[E0308]: mismatched types
+  --> $DIR/do-catch-suggests-try.rs:9:33
+   |
+LL |     let _recovery_witness: () = 1;
+   |                            --   ^ expected `()`, found integer
+   |                            |
+   |                            expected due to this
+
+error: aborting due to 2 previous errors
 
+For more information about this error, try `rustc --explain E0308`.
diff --git a/src/test/ui/parser/issue-65257-invalid-var-decl-recovery.rs b/src/test/ui/parser/issue-65257-invalid-var-decl-recovery.rs
new file mode 100644
index 00000000000..c1826f8caae
--- /dev/null
+++ b/src/test/ui/parser/issue-65257-invalid-var-decl-recovery.rs
@@ -0,0 +1,21 @@
+fn main() {
+    auto n = 0;//~ ERROR invalid variable declaration
+    //~^ HELP write `let` instead of `auto` to introduce a new variable
+    auto m;//~ ERROR invalid variable declaration
+    //~^ HELP write `let` instead of `auto` to introduce a new variable
+    m = 0;
+
+    var n = 0;//~ ERROR invalid variable declaration
+    //~^ HELP write `let` instead of `var` to introduce a new variable
+    var m;//~ ERROR invalid variable declaration
+    //~^ HELP write `let` instead of `var` to introduce a new variable
+    m = 0;
+
+    mut n = 0;//~ ERROR invalid variable declaration
+    //~^ HELP missing keyword
+    mut var;//~ ERROR invalid variable declaration
+    //~^ HELP missing keyword
+    var = 0;
+
+    let _recovery_witness: () = 0; //~ ERROR mismatched types
+}
diff --git a/src/test/ui/parser/issue-65257-invalid-var-decl-recovery.stderr b/src/test/ui/parser/issue-65257-invalid-var-decl-recovery.stderr
new file mode 100644
index 00000000000..ad72dd30542
--- /dev/null
+++ b/src/test/ui/parser/issue-65257-invalid-var-decl-recovery.stderr
@@ -0,0 +1,67 @@
+error: invalid variable declaration
+  --> $DIR/issue-65257-invalid-var-decl-recovery.rs:2:5
+   |
+LL |     auto n = 0;
+   |     ^^^^
+   |
+help: write `let` instead of `auto` to introduce a new variable
+   |
+LL |     let n = 0;
+   |     ^^^
+
+error: invalid variable declaration
+  --> $DIR/issue-65257-invalid-var-decl-recovery.rs:4:5
+   |
+LL |     auto m;
+   |     ^^^^
+   |
+help: write `let` instead of `auto` to introduce a new variable
+   |
+LL |     let m;
+   |     ^^^
+
+error: invalid variable declaration
+  --> $DIR/issue-65257-invalid-var-decl-recovery.rs:8:5
+   |
+LL |     var n = 0;
+   |     ^^^
+   |
+help: write `let` instead of `var` to introduce a new variable
+   |
+LL |     let n = 0;
+   |     ^^^
+
+error: invalid variable declaration
+  --> $DIR/issue-65257-invalid-var-decl-recovery.rs:10:5
+   |
+LL |     var m;
+   |     ^^^
+   |
+help: write `let` instead of `var` to introduce a new variable
+   |
+LL |     let m;
+   |     ^^^
+
+error: invalid variable declaration
+  --> $DIR/issue-65257-invalid-var-decl-recovery.rs:14:5
+   |
+LL |     mut n = 0;
+   |     ^^^ help: missing keyword: `let mut`
+
+error: invalid variable declaration
+  --> $DIR/issue-65257-invalid-var-decl-recovery.rs:16:5
+   |
+LL |     mut var;
+   |     ^^^ help: missing keyword: `let mut`
+
+error[E0308]: mismatched types
+  --> $DIR/issue-65257-invalid-var-decl-recovery.rs:20:33
+   |
+LL |     let _recovery_witness: () = 0;
+   |                            --   ^ expected `()`, found integer
+   |                            |
+   |                            expected due to this
+
+error: aborting due to 7 previous errors
+
+For more information about this error, try `rustc --explain E0308`.
diff --git a/src/test/ui/parser/recover-labeled-non-block-expr.rs b/src/test/ui/parser/recover-labeled-non-block-expr.rs
new file mode 100644
index 00000000000..be92170acf0
--- /dev/null
+++ b/src/test/ui/parser/recover-labeled-non-block-expr.rs
@@ -0,0 +1,5 @@
+fn main() {
+    'label: 1 + 1; //~ ERROR expected `while`, `for`, `loop` or `{` after a label
+
+    let _recovery_witness: () = 0; //~ ERROR mismatched types
+}
diff --git a/src/test/ui/parser/recover-labeled-non-block-expr.stderr b/src/test/ui/parser/recover-labeled-non-block-expr.stderr
new file mode 100644
index 00000000000..771a915288c
--- /dev/null
+++ b/src/test/ui/parser/recover-labeled-non-block-expr.stderr
@@ -0,0 +1,17 @@
+error: expected `while`, `for`, `loop` or `{` after a label
+  --> $DIR/recover-labeled-non-block-expr.rs:2:13
+   |
+LL |     'label: 1 + 1;
+   |             ^ expected `while`, `for`, `loop` or `{` after a label
+
+error[E0308]: mismatched types
+  --> $DIR/recover-labeled-non-block-expr.rs:4:33
+   |
+LL |     let _recovery_witness: () = 0;
+   |                            --   ^ expected `()`, found integer
+   |                            |
+   |                            expected due to this
+
+error: aborting due to 2 previous errors
+
+For more information about this error, try `rustc --explain E0308`.
diff --git a/src/test/ui/parser/recovery-attr-on-if.rs b/src/test/ui/parser/recovery-attr-on-if.rs
new file mode 100644
index 00000000000..0d1f5be7b49
--- /dev/null
+++ b/src/test/ui/parser/recovery-attr-on-if.rs
@@ -0,0 +1,9 @@
+fn main() {
+    #[attr] if true {};
+    //~^ ERROR cannot find attribute
+    //~| ERROR attributes are not yet allowed on `if` expressions
+    #[attr] if true {};
+    //~^ ERROR cannot find attribute
+    //~| ERROR attributes are not yet allowed on `if` expressions
+    let _recovery_witness: () = 0; //~ ERROR mismatched types
+}
diff --git a/src/test/ui/parser/recovery-attr-on-if.stderr b/src/test/ui/parser/recovery-attr-on-if.stderr
new file mode 100644
index 00000000000..a02846827c9
--- /dev/null
+++ b/src/test/ui/parser/recovery-attr-on-if.stderr
@@ -0,0 +1,35 @@
+error: attributes are not yet allowed on `if` expressions
+  --> $DIR/recovery-attr-on-if.rs:2:5
+   |
+LL |     #[attr] if true {};
+   |     ^^^^^^^
+
+error: attributes are not yet allowed on `if` expressions
+  --> $DIR/recovery-attr-on-if.rs:5:5
+   |
+LL |     #[attr] if true {};
+   |     ^^^^^^^
+
+error: cannot find attribute `attr` in this scope
+  --> $DIR/recovery-attr-on-if.rs:5:7
+   |
+LL |     #[attr] if true {};
+   |       ^^^^
+
+error: cannot find attribute `attr` in this scope
+  --> $DIR/recovery-attr-on-if.rs:2:7
+   |
+LL |     #[attr] if true {};
+   |       ^^^^
+
+error[E0308]: mismatched types
+  --> $DIR/recovery-attr-on-if.rs:8:33
+   |
+LL |     let _recovery_witness: () = 0;
+   |                            --   ^ expected `()`, found integer
+   |                            |
+   |                            expected due to this
+
+error: aborting due to 5 previous errors
+
+For more information about this error, try `rustc --explain E0308`.
diff --git a/src/test/ui/parser/stmt_expr_attrs_placement.rs b/src/test/ui/parser/stmt_expr_attrs_placement.rs
new file mode 100644
index 00000000000..b8a794f4b92
--- /dev/null
+++ b/src/test/ui/parser/stmt_expr_attrs_placement.rs
@@ -0,0 +1,22 @@
+#![feature(stmt_expr_attributes)]
+
+// Test that various placements of the inner attribute are parsed correctly,
+// or not.
+
+fn main() {
+    let a = #![allow(warnings)] (1, 2);
+    //~^ ERROR an inner attribute is not permitted in this context
+
+    let b = (#![allow(warnings)] 1, 2);
+
+    let c = {
+        #![allow(warnings)]
+        (#![allow(warnings)] 1, 2)
+    };
+
+    let d = {
+        #![allow(warnings)]
+        let e = (#![allow(warnings)] 1, 2);
+        e
+    };
+}
diff --git a/src/test/ui/parser/stmt_expr_attrs_placement.stderr b/src/test/ui/parser/stmt_expr_attrs_placement.stderr
new file mode 100644
index 00000000000..1886a0f9ba0
--- /dev/null
+++ b/src/test/ui/parser/stmt_expr_attrs_placement.stderr
@@ -0,0 +1,10 @@
+error: an inner attribute is not permitted in this context
+  --> $DIR/stmt_expr_attrs_placement.rs:7:13
+   |
+LL |     let a = #![allow(warnings)] (1, 2);
+   |             ^^^^^^^^^^^^^^^^^^^
+   |
+   = note: inner attributes, like `#![no_std]`, annotate the item enclosing them, and are usually found at the beginning of source files. Outer attributes, like `#[test]`, annotate the item following them.
+
+error: aborting due to previous error
+