Add syntax support for attributes on expressions and all syntax

nodes in statement position.

Extended #[cfg] folder to allow removal of statements, and
of expressions in optional positions like expression lists and trailing
block expressions.

Extended lint checker to recognize lint levels on expressions and
locals.
This commit is contained in:
Marvin Löbel 2015-11-03 17:39:51 +01:00
parent 6ef02eff89
commit 2a8f358de7
34 changed files with 1602 additions and 416 deletions

View file

@ -1,16 +0,0 @@
// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// compile-flags: -Z parse-only
fn main() {
#[attr] //~ ERROR expected item after attributes
println!("hi");
}

View file

@ -1,16 +0,0 @@
// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// compile-flags: -Z parse-only
fn main() {
#[attr] //~ ERROR expected item
let __isize = 0;
}

View file

@ -1,21 +0,0 @@
// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// compile-flags: -Z parse-only
// error-pattern:expected item
fn f() {
#[foo = "bar"]
let x = 10;
}
fn main() {
}

View file

@ -10,7 +10,7 @@
// compile-flags: -Z parse-only
// error-pattern:expected item
// error-pattern:expected statement
fn f() {
#[foo = "bar"]

View file

@ -1,17 +0,0 @@
// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// compile-flags: -Z parse-only
fn main() {
/// hi
println!("hi");
//~^^ ERROR expected item after doc comment
}

View file

@ -12,5 +12,5 @@
fn main() {
println!("Hi"); /// hi
//~^ ERROR expected item after doc comment
}
//~^ ERROR expected statement

View file

@ -13,5 +13,5 @@
fn main() {
/// hi
;
//~^^ ERROR expected item after doc comment
//~^ ERROR expected statement
}

View file

@ -0,0 +1,281 @@
// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// pp-exact
#![feature(custom_attribute)]
#![feature(box_syntax)]
#![feature(placement_in_syntax)]
fn main() { }
fn _0() {
#[attr]
foo();
}
fn _1() {
#[attr]
unsafe {
// code
}
}
fn _2() {
#[attr]
{ foo(); }
{
#![attr]
foo()
}
}
fn _3() {
#[attr]
match () { _ => { } }
}
fn _4() {
#[attr]
match () {
#![attr]
_ => (),
}
let _ =
#[attr] match () {
#![attr]
() => (),
};
}
fn _5() {
#[attr]
let x = 1;
let x = #[attr] 1;
let y = ();
let z = ();
foo3(x, #[attr] y, z);
qux(3 + #[attr] 2);
}
fn _6() {
#[attr]
[#![attr] 1, 2, 3];
let _ = #[attr] [#![attr] 1, 2, 3];
#[attr]
[#![attr] 1; 4];
let _ = #[attr] [#![attr] 1; 4];
}
struct Foo {
data: (),
}
struct Bar(());
fn _7() {
#[attr]
Foo{#![attr] data: (),};
let _ = #[attr] Foo{#![attr] data: (),};
}
fn _8() {
#[attr]
(#![attr] );
#[attr]
(#![attr] 0);
#[attr]
(#![attr] 0,);
#[attr]
(#![attr] 0, 1);
}
fn _9() {
macro_rules! stmt_mac(( ) => { let _ = ( ) ; });
#[attr]
stmt_mac!();
/*
// pre existing pp bug: delimiter styles gets lost:
#[attr]
stmt_mac!{ };
#[attr]
stmt_mac![];
#[attr]
stmt_mac!{ } // pre-existing pp bug: compiler ICEs with a None unwrap
*/
let _ = ();
}
macro_rules! expr_mac(( ) => { ( ) });
fn _10() {
let _ = #[attr] expr_mac!();
/*
// pre existing pp bug: delimiter styles gets lost:
let _ = #[attr] expr_mac![];
let _ = #[attr] expr_mac!{};
*/
}
fn _11() {
let _ = #[attr] box 0;
let _: [(); 0] = #[attr] [#![attr] ];
let _ = #[attr] [#![attr] 0, 0];
let _ = #[attr] [#![attr] 0; 0];
let _ = #[attr] foo();
let _ = #[attr] 1i32.clone();
let _ = #[attr] (#![attr] );
let _ = #[attr] (#![attr] 0);
let _ = #[attr] (#![attr] 0,);
let _ = #[attr] (#![attr] 0, 0);
let _ = #[attr] 0 + #[attr] 0;
let _ = #[attr] !0;
let _ = #[attr] -0i32;
let _ = #[attr] false;
let _ = #[attr] 'c';
let _ = #[attr] 0;
let _ = #[attr] 0 as usize;
let _ =
#[attr] while false {
#![attr]
};
let _ =
#[attr] while let None = Some(()) {
#![attr]
};
let _ =
#[attr] for _ in 0..0 {
#![attr]
};
// FIXME: pp bug, two spaces after the loop
let _ =
#[attr] loop {
#![attr]
};
let _ =
#[attr] match false {
#![attr]
_ => (),
};
let _ = #[attr] || #[attr] ();
let _ = #[attr] move || #[attr] ();
let _ = #[attr] || {
#![attr]
#[attr]
() };
let _ = #[attr] move || {
#![attr]
#[attr]
() };
let _ =
#[attr] {
#![attr]
};
let _ =
#[attr] {
#![attr]
let _ = ();
};
let _ =
#[attr] {
#![attr]
let _ = ();
()
};
let mut x = 0;
let _ = #[attr] x = 15;
let _ = #[attr] x += 15;
let s = Foo{data: (),};
let _ = #[attr] s.data;
let _ = (#[attr] s).data;
let t = Bar(());
let _ = #[attr] t.0;
let _ = (#[attr] t).0;
let v = vec!(0);
let _ = #[attr] v[0];
let _ = (#[attr] v)[0];
let _ = #[attr] 0..#[attr] 0;
let _ = #[attr] 0..;
let _ = #[attr] (0..0);
let _ = #[attr] (0..);
let _ = #[attr] (..0);
let _ = #[attr] (..);
let _: fn(&u32) -> u32 = #[attr] std::clone::Clone::clone;
let _ = #[attr] &0;
let _ = #[attr] &mut 0;
let _ = #[attr] &#[attr] 0;
let _ = #[attr] &mut #[attr] 0;
// FIXME: pp bug, extra space after keyword?
while false { let _ = #[attr] continue ; }
while true { let _ = #[attr] break ; }
|| #[attr] return;
let _ = #[attr] expr_mac!();
/* FIXME: pp bug, loosing delimiter styles
let _ = #[attr] expr_mac![];
let _ = #[attr] expr_mac!{};
*/
let _ = #[attr] Foo{#![attr] data: (),};
let _ = #[attr] Foo{#![attr] ..s};
let _ = #[attr] Foo{#![attr] data: (), ..s};
let _ = #[attr] (#![attr] 0);
}
fn _12() {
#[attr]
let _ = 0;
#[attr]
0;
#[attr]
expr_mac!();
#[attr]
{
#![attr]
}
}
/////////////////
fn foo() { }
fn foo3(_: i32, _: (), _: ()) { }
fn qux(_: i32) { }

View file

@ -0,0 +1,306 @@
// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
#![feature(rustc_private)]
extern crate syntax;
use syntax::ast::*;
use syntax::ast;
use syntax::parse;
use syntax::parse::{ParseSess,filemap_to_tts, PResult};
use syntax::parse::new_parser_from_source_str;
use syntax::parse::parser::Parser;
use syntax::parse::token;
use syntax::ptr::P;
use syntax::str::char_at;
use syntax::parse::attr::*;
use syntax::print::pprust;
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,
Vec::new(),
"bogofile".to_string(),
source_str)
}
fn with_error_checking_parse<T, F>(s: String, f: F) -> PResult<T> where
F: FnOnce(&mut Parser) -> PResult<T>,
{
let ps = ParseSess::new();
let mut p = string_to_parser(&ps, s);
let x = f(&mut p);
if ps.span_diagnostic.handler().has_errors() || p.token != token::Eof {
return Err(p.fatal("parse error"));
}
x
}
fn expr(s: &str) -> PResult<P<ast::Expr>> {
with_error_checking_parse(s.to_string(), |p| {
p.parse_expr_nopanic()
})
}
fn stmt(s: &str) -> PResult<P<ast::Stmt>> {
with_error_checking_parse(s.to_string(), |p| {
p.parse_stmt_nopanic().map(|s| s.unwrap())
})
}
fn attr(s: &str) -> PResult<ast::Attribute> {
with_error_checking_parse(s.to_string(), |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 check_expr_attrs(es: &str, expected: &[&str]) {
let e = expr(es).expect("parse error");
let actual = &e.attrs;
str_compare(es,
&expected.iter().map(|r| attr(r).unwrap()).collect::<Vec<_>>(),
actual.as_attrs(),
pprust::attribute_to_string);
}
fn check_stmt_attrs(es: &str, expected: &[&str]) {
let e = stmt(es).expect("parse error");
let actual = e.node.attrs();
str_compare(es,
&expected.iter().map(|r| attr(r).unwrap()).collect::<Vec<_>>(),
actual,
pprust::attribute_to_string);
}
fn reject_expr_parse(es: &str) {
assert!(expr(es).is_err(), "parser did not reject `{}`", es);
}
fn reject_stmt_parse(es: &str) {
assert!(stmt(es).is_err(), "parser did not reject `{}`", es);
}
fn main() {
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] 0 <- #[attr] 0", none);
check_expr_attrs("#[attr] (0 <- 0)", outer);
reject_expr_parse("0 #[attr] <- 0");
reject_expr_parse("0 <- #![attr] 0");
check_expr_attrs("in #[attr] 0 {#[attr] 0}", none);
check_expr_attrs("#[attr] (in 0 {0})", outer);
reject_expr_parse("in 0 #[attr] {0}");
reject_expr_parse("in 0 {#![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?
// would require 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]
}
}");
}

View file

@ -0,0 +1,97 @@
// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
#![deny(non_snake_case)]
fn main() {
let a = 413;
#[cfg(unset)]
let a = ();
assert_eq!(a, 413);
let mut b = 612;
#[cfg(unset)]
{
b = 1111;
}
assert_eq!(b, 612);
#[cfg(unset)]
undefined_fn();
#[cfg(unset)]
undefined_macro!();
#[cfg(unset)]
undefined_macro![];
#[cfg(unset)]
undefined_macro!{};
// pretty printer bug...
// #[cfg(unset)]
// undefined_macro!{}
let () = (#[cfg(unset)] 341,); // Should this also work on parens?
let t = (1, #[cfg(unset)] 3, 4);
assert_eq!(t, (1, 4));
let f = |_: u32, _: u32| ();
f(2, 1, #[cfg(unset)] 6);
let _: u32 = a.clone(#[cfg(unset)] undefined);
let _: [(); 0] = [#[cfg(unset)] 126];
let t = [#[cfg(unset)] 1, 2, 6];
assert_eq!(t, [2, 6]);
{
let r;
#[cfg(unset)]
(r = 5);
#[cfg(not(unset))]
(r = 10);
assert_eq!(r, 10);
}
// check that macro expanded code works
macro_rules! if_cfg {
($cfg:meta $ib:block else $eb:block) => {
{
let r;
#[cfg($cfg)]
(r = $ib);
#[cfg(not($cfg))]
(r = $eb);
r
}
}
}
let n = if_cfg!(unset {
413
} else {
612
});
assert_eq!((#[cfg(unset)] 1, #[cfg(not(unset))] 2), (2,));
assert_eq!(n, 612);
// check that lints work
#[allow(non_snake_case)]
let FOOBAR = {
fn SYLADEX() {}
};
#[allow(non_snake_case)]
{
fn CRUXTRUDER() {}
}
}