From 4e00cf613428d24d305a89e4f8e79b70ea8e8322 Mon Sep 17 00:00:00 2001 From: Daniel Fagnan Date: Mon, 24 Feb 2014 19:42:40 -0700 Subject: [PATCH 1/2] Added new attribute syntax with backward compatibility. Signed-off-by: Daniel Fagnan --- src/libsyntax/parse/attr.rs | 36 +++++++++++++++++++++++-------- src/libsyntax/parse/comments.rs | 8 +++++-- src/libsyntax/parse/lexer.rs | 18 ++++++++++++++++ src/test/compile-fail/attr.rs | 14 ++++++++++++ src/test/run-pass/attr-mix-new.rs | 16 ++++++++++++++ src/test/run-pass/attr-shebang.rs | 4 ++++ src/test/run-pass/attr.rs | 13 +++++++++++ 7 files changed, 98 insertions(+), 11 deletions(-) create mode 100644 src/test/compile-fail/attr.rs create mode 100644 src/test/run-pass/attr-mix-new.rs create mode 100644 src/test/run-pass/attr-shebang.rs create mode 100644 src/test/run-pass/attr.rs diff --git a/src/libsyntax/parse/attr.rs b/src/libsyntax/parse/attr.rs index c30ab4bda2749..92b93fd88dd64 100644 --- a/src/libsyntax/parse/attr.rs +++ b/src/libsyntax/parse/attr.rs @@ -38,9 +38,6 @@ impl<'a> ParserAttr for Parser<'a> { attrs.push(self.parse_attribute(false)); } token::POUND => { - if self.look_ahead(1, |t| *t != token::LBRACKET) { - break; - } attrs.push(self.parse_attribute(false)); } token::DOC_COMMENT(s) => { @@ -68,6 +65,7 @@ impl<'a> ParserAttr for Parser<'a> { fn parse_attribute(&mut self, permit_inner: bool) -> ast::Attribute { debug!("parse_attributes: permit_inner={:?} self.token={:?}", permit_inner, self.token); + let mut warned = false; let (span, value) = match self.token { INTERPOLATED(token::NtAttr(attr)) => { assert!(attr.node.style == ast::AttrOuter); @@ -77,9 +75,22 @@ impl<'a> ParserAttr for Parser<'a> { token::POUND => { let lo = self.span.lo; self.bump(); + + if self.eat(&token::NOT) { + if !permit_inner { + self.fatal("an inner attribute was not permitted in this context."); + } + } else { + warned = true; + // NOTE: uncomment this after a stage0 snap + //self.warn("The syntax for inner attributes have changed. + // Use `#![lang(foo)]` instead."); + } + self.expect(&token::LBRACKET); let meta_item = self.parse_meta_item(); self.expect(&token::RBRACKET); + let hi = self.span.hi; (mk_sp(lo, hi), meta_item) } @@ -89,12 +100,23 @@ impl<'a> ParserAttr for Parser<'a> { token_str)); } }; - let style = if permit_inner && self.token == token::SEMI { - self.bump(); + + let style = if permit_inner { + + if self.eat(&token::SEMI) { + // Only warn the user once if the syntax is the old one. + if !warned { + // NOTE: uncomment this after a stage0 snap + //self.warn("This uses the old attribute syntax. Semicolons + // are not longer required."); + } + } + ast::AttrInner } else { ast::AttrOuter }; + return Spanned { span: span, node: ast::Attribute_ { @@ -125,10 +147,6 @@ impl<'a> ParserAttr for Parser<'a> { self.parse_attribute(true) } token::POUND => { - if self.look_ahead(1, |t| *t != token::LBRACKET) { - // This is an extension - break; - } self.parse_attribute(true) } token::DOC_COMMENT(s) => { diff --git a/src/libsyntax/parse/comments.rs b/src/libsyntax/parse/comments.rs index 8abc01b6d7545..1221d8401be1d 100644 --- a/src/libsyntax/parse/comments.rs +++ b/src/libsyntax/parse/comments.rs @@ -12,7 +12,7 @@ use ast; use codemap::{BytePos, CharPos, CodeMap, Pos}; use diagnostic; use parse::lexer::{is_whitespace, with_str_from, Reader}; -use parse::lexer::{StringReader, bump, is_eof, nextch_is, TokenAndSpan}; +use parse::lexer::{StringReader, bump, peek, is_eof, nextch_is, TokenAndSpan}; use parse::lexer::{is_line_non_doc_comment, is_block_non_doc_comment}; use parse::lexer; use parse::token; @@ -331,7 +331,11 @@ fn consume_comment(rdr: &StringReader, } else if rdr.curr_is('/') && nextch_is(rdr, '*') { read_block_comment(rdr, code_to_the_left, comments); } else if rdr.curr_is('#') && nextch_is(rdr, '!') { - read_shebang_comment(rdr, code_to_the_left, comments); + // Make sure the following token is **not** the beginning + // of an inner attribute, which starts with the same syntax. + if peek(rdr, 2).unwrap() != '[' { + read_shebang_comment(rdr, code_to_the_left, comments); + } } else { fail!(); } debug!("<<< consume comment"); } diff --git a/src/libsyntax/parse/lexer.rs b/src/libsyntax/parse/lexer.rs index 58ec6acc3ac01..061d460af5e48 100644 --- a/src/libsyntax/parse/lexer.rs +++ b/src/libsyntax/parse/lexer.rs @@ -271,9 +271,21 @@ pub fn bump(rdr: &StringReader) { rdr.curr.set(None); } } + +// EFFECT: Peek 'n' characters ahead. +pub fn peek(rdr: &StringReader, n: uint) -> Option { + let offset = byte_offset(rdr, rdr.pos.get()).to_uint() + (n - 1); + if offset < (rdr.filemap.src).len() { + Some(rdr.filemap.src.char_at(offset)) + } else { + None + } +} + pub fn is_eof(rdr: &StringReader) -> bool { rdr.curr.get().is_none() } + pub fn nextch(rdr: &StringReader) -> Option { let offset = byte_offset(rdr, rdr.pos.get()).to_uint(); if offset < rdr.filemap.deref().src.len() { @@ -370,6 +382,12 @@ fn consume_any_line_comment(rdr: &StringReader) } } else if rdr.curr_is('#') { if nextch_is(rdr, '!') { + + // Parse an inner attribute. + if peek(rdr, 2).unwrap() == '[' { + return None; + } + // I guess this is the only way to figure out if // we're at the beginning of the file... let cmap = CodeMap::new(); diff --git a/src/test/compile-fail/attr.rs b/src/test/compile-fail/attr.rs new file mode 100644 index 0000000000000..4107c84c47223 --- /dev/null +++ b/src/test/compile-fail/attr.rs @@ -0,0 +1,14 @@ +// Copyright 2014 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +fn main() {} + +#![lang(foo)] //~ ERROR An inner attribute was not permitted in this context. +fn foo() {} \ No newline at end of file diff --git a/src/test/run-pass/attr-mix-new.rs b/src/test/run-pass/attr-mix-new.rs new file mode 100644 index 0000000000000..397b49df91597 --- /dev/null +++ b/src/test/run-pass/attr-mix-new.rs @@ -0,0 +1,16 @@ +// Copyright 2014 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#[foo(bar)] +mod foo { + #![feature(globs)] +} + +fn main() {} \ No newline at end of file diff --git a/src/test/run-pass/attr-shebang.rs b/src/test/run-pass/attr-shebang.rs new file mode 100644 index 0000000000000..12f568dd472af --- /dev/null +++ b/src/test/run-pass/attr-shebang.rs @@ -0,0 +1,4 @@ +#![allow(unknown_features)] +#![feature(bogus)] +fn main() { } +// ignore-license \ No newline at end of file diff --git a/src/test/run-pass/attr.rs b/src/test/run-pass/attr.rs new file mode 100644 index 0000000000000..2f30eb8154ff1 --- /dev/null +++ b/src/test/run-pass/attr.rs @@ -0,0 +1,13 @@ +// Copyright 2014 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#[main] +fn foo() { +} From 84a91b860330c2b83fd0546b33a949079d422166 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Thu, 20 Mar 2014 11:21:17 -0700 Subject: [PATCH 2/2] syntax: Tidy up parsing the new attribute syntax --- src/compiletest/compiletest.rs | 1 + src/libarena/lib.rs | 1 + src/libgreen/lib.rs | 1 + src/librustuv/lib.rs | 1 + src/libsyntax/parse/attr.rs | 46 ++++++++----------- src/libsyntax/parse/comments.rs | 12 ++--- src/libsyntax/parse/lexer.rs | 30 +++++++----- src/libtest/lib.rs | 1 + src/test/compile-fail/attr.rs | 4 +- .../compile-fail/column-offset-1-based.rs | 2 +- src/test/compile-fail/issue-1655.rs | 2 +- src/test/run-pass/attr-mix-new.rs | 2 +- src/test/run-pass/attr-shebang.rs | 5 +- src/test/run-pass/attr.rs | 2 + 14 files changed, 56 insertions(+), 54 deletions(-) diff --git a/src/compiletest/compiletest.rs b/src/compiletest/compiletest.rs index ae7f324627271..855bc1e5852d0 100644 --- a/src/compiletest/compiletest.rs +++ b/src/compiletest/compiletest.rs @@ -12,6 +12,7 @@ #[feature(phase)]; #[allow(non_camel_case_types)]; +#[allow(deprecated_owned_vector)]; // NOTE: remove after stage0 #[deny(warnings)]; extern crate test; diff --git a/src/libarena/lib.rs b/src/libarena/lib.rs index 2cc41dd7085d9..dbebb8fb2bc68 100644 --- a/src/libarena/lib.rs +++ b/src/libarena/lib.rs @@ -24,6 +24,7 @@ html_root_url = "http://static.rust-lang.org/doc/master")]; #[allow(missing_doc)]; #[feature(managed_boxes)]; +#[allow(deprecated_owned_vector)]; // NOTE: remove after stage0 extern crate collections; diff --git a/src/libgreen/lib.rs b/src/libgreen/lib.rs index a23d2d4f3a57e..b9846c9c3a2a8 100644 --- a/src/libgreen/lib.rs +++ b/src/libgreen/lib.rs @@ -174,6 +174,7 @@ // NB this does *not* include globs, please keep it that way. #[feature(macro_rules, phase)]; #[allow(visible_private_types)]; +#[allow(deprecated_owned_vector)]; // NOTE: remove after stage0 #[cfg(test)] #[phase(syntax, link)] extern crate log; extern crate rand; diff --git a/src/librustuv/lib.rs b/src/librustuv/lib.rs index 890f44faabc0d..ee4f15e7954bd 100644 --- a/src/librustuv/lib.rs +++ b/src/librustuv/lib.rs @@ -42,6 +42,7 @@ via `close` and `delete` methods. #[feature(macro_rules)]; #[deny(unused_result, unused_must_use)]; #[allow(visible_private_types)]; +#[allow(deprecated_owned_vector)]; // NOTE: remove after stage0 #[cfg(test)] extern crate green; diff --git a/src/libsyntax/parse/attr.rs b/src/libsyntax/parse/attr.rs index 92b93fd88dd64..8f7fb5749a1eb 100644 --- a/src/libsyntax/parse/attr.rs +++ b/src/libsyntax/parse/attr.rs @@ -58,41 +58,40 @@ impl<'a> ParserAttr for Parser<'a> { return attrs; } - // matches attribute = # [ meta_item ] + // matches attribute = # ! [ meta_item ] // - // if permit_inner is true, then a trailing `;` indicates an inner + // if permit_inner is true, then a leading `!` indicates an inner // attribute fn parse_attribute(&mut self, permit_inner: bool) -> ast::Attribute { debug!("parse_attributes: permit_inner={:?} self.token={:?}", permit_inner, self.token); - let mut warned = false; - let (span, value) = match self.token { + let (span, value, mut style) = match self.token { INTERPOLATED(token::NtAttr(attr)) => { assert!(attr.node.style == ast::AttrOuter); self.bump(); - (attr.span, attr.node.value) + (attr.span, attr.node.value, ast::AttrOuter) } token::POUND => { let lo = self.span.lo; self.bump(); - if self.eat(&token::NOT) { + let style = if self.eat(&token::NOT) { if !permit_inner { - self.fatal("an inner attribute was not permitted in this context."); + self.span_err(self.span, + "an inner attribute is not permitted in \ + this context"); } + ast::AttrInner } else { - warned = true; - // NOTE: uncomment this after a stage0 snap - //self.warn("The syntax for inner attributes have changed. - // Use `#![lang(foo)]` instead."); - } + ast::AttrOuter + }; self.expect(&token::LBRACKET); let meta_item = self.parse_meta_item(); self.expect(&token::RBRACKET); let hi = self.span.hi; - (mk_sp(lo, hi), meta_item) + (mk_sp(lo, hi), meta_item, style) } _ => { let token_str = self.this_token_to_str(); @@ -101,21 +100,12 @@ impl<'a> ParserAttr for Parser<'a> { } }; - let style = if permit_inner { - - if self.eat(&token::SEMI) { - // Only warn the user once if the syntax is the old one. - if !warned { - // NOTE: uncomment this after a stage0 snap - //self.warn("This uses the old attribute syntax. Semicolons - // are not longer required."); - } - } - - ast::AttrInner - } else { - ast::AttrOuter - }; + if permit_inner && self.eat(&token::SEMI) { + // NOTE: uncomment this after a stage0 snap + //self.warn("This uses the old attribute syntax. Semicolons + // are not longer required."); + style = ast::AttrInner; + } return Spanned { span: span, diff --git a/src/libsyntax/parse/comments.rs b/src/libsyntax/parse/comments.rs index 1221d8401be1d..43ae9b97350df 100644 --- a/src/libsyntax/parse/comments.rs +++ b/src/libsyntax/parse/comments.rs @@ -12,7 +12,7 @@ use ast; use codemap::{BytePos, CharPos, CodeMap, Pos}; use diagnostic; use parse::lexer::{is_whitespace, with_str_from, Reader}; -use parse::lexer::{StringReader, bump, peek, is_eof, nextch_is, TokenAndSpan}; +use parse::lexer::{StringReader, bump, is_eof, nextch_is, TokenAndSpan}; use parse::lexer::{is_line_non_doc_comment, is_block_non_doc_comment}; use parse::lexer; use parse::token; @@ -319,7 +319,9 @@ fn read_block_comment(rdr: &StringReader, fn peeking_at_comment(rdr: &StringReader) -> bool { return (rdr.curr_is('/') && nextch_is(rdr, '/')) || (rdr.curr_is('/') && nextch_is(rdr, '*')) || - (rdr.curr_is('#') && nextch_is(rdr, '!')); + // consider shebangs comments, but not inner attributes + (rdr.curr_is('#') && nextch_is(rdr, '!') && + !lexer::nextnextch_is(rdr, '[')); } fn consume_comment(rdr: &StringReader, @@ -331,11 +333,7 @@ fn consume_comment(rdr: &StringReader, } else if rdr.curr_is('/') && nextch_is(rdr, '*') { read_block_comment(rdr, code_to_the_left, comments); } else if rdr.curr_is('#') && nextch_is(rdr, '!') { - // Make sure the following token is **not** the beginning - // of an inner attribute, which starts with the same syntax. - if peek(rdr, 2).unwrap() != '[' { - read_shebang_comment(rdr, code_to_the_left, comments); - } + read_shebang_comment(rdr, code_to_the_left, comments); } else { fail!(); } debug!("<<< consume comment"); } diff --git a/src/libsyntax/parse/lexer.rs b/src/libsyntax/parse/lexer.rs index 061d460af5e48..ca2fbd245873f 100644 --- a/src/libsyntax/parse/lexer.rs +++ b/src/libsyntax/parse/lexer.rs @@ -18,9 +18,10 @@ use parse::token::{str_to_ident}; use std::cell::{Cell, RefCell}; use std::char; -use std::rc::Rc; use std::mem::replace; use std::num::from_str_radix; +use std::rc::Rc; +use std::str; pub use ext::tt::transcribe::{TtReader, new_tt_reader}; @@ -272,16 +273,6 @@ pub fn bump(rdr: &StringReader) { } } -// EFFECT: Peek 'n' characters ahead. -pub fn peek(rdr: &StringReader, n: uint) -> Option { - let offset = byte_offset(rdr, rdr.pos.get()).to_uint() + (n - 1); - if offset < (rdr.filemap.src).len() { - Some(rdr.filemap.src.char_at(offset)) - } else { - None - } -} - pub fn is_eof(rdr: &StringReader) -> bool { rdr.curr.get().is_none() } @@ -298,6 +289,21 @@ pub fn nextch_is(rdr: &StringReader, c: char) -> bool { nextch(rdr) == Some(c) } +pub fn nextnextch(rdr: &StringReader) -> Option { + let offset = byte_offset(rdr, rdr.pos.get()).to_uint(); + let s = rdr.filemap.deref().src.as_slice(); + if offset >= s.len() { return None } + let str::CharRange { next, .. } = s.char_range_at(offset); + if next < s.len() { + Some(s.char_at(next)) + } else { + None + } +} +pub fn nextnextch_is(rdr: &StringReader, c: char) -> bool { + nextnextch(rdr) == Some(c) +} + fn hex_digit_val(c: Option) -> int { let d = c.unwrap_or('\x00'); @@ -384,7 +390,7 @@ fn consume_any_line_comment(rdr: &StringReader) if nextch_is(rdr, '!') { // Parse an inner attribute. - if peek(rdr, 2).unwrap() == '[' { + if nextnextch_is(rdr, '[') { return None; } diff --git a/src/libtest/lib.rs b/src/libtest/lib.rs index 3ec80d97d0613..ec7d48cf12bda 100644 --- a/src/libtest/lib.rs +++ b/src/libtest/lib.rs @@ -33,6 +33,7 @@ html_root_url = "http://static.rust-lang.org/doc/master")]; #[feature(asm, macro_rules)]; +#[allow(deprecated_owned_vector)]; // NOTE: remove after stage0 extern crate collections; extern crate getopts; diff --git a/src/test/compile-fail/attr.rs b/src/test/compile-fail/attr.rs index 4107c84c47223..b1f7a791f0958 100644 --- a/src/test/compile-fail/attr.rs +++ b/src/test/compile-fail/attr.rs @@ -10,5 +10,5 @@ fn main() {} -#![lang(foo)] //~ ERROR An inner attribute was not permitted in this context. -fn foo() {} \ No newline at end of file +#![lang(foo)] //~ ERROR an inner attribute is not permitted in this context +fn foo() {} diff --git a/src/test/compile-fail/column-offset-1-based.rs b/src/test/compile-fail/column-offset-1-based.rs index 5b38b17ad959d..3a6594f64f36a 100644 --- a/src/test/compile-fail/column-offset-1-based.rs +++ b/src/test/compile-fail/column-offset-1-based.rs @@ -8,4 +8,4 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -# //~ ERROR 11:1: 11:2 error: expected item +# //~ ERROR 11:1: 11:2 error: expected `[` but found `` diff --git a/src/test/compile-fail/issue-1655.rs b/src/test/compile-fail/issue-1655.rs index 37cfc642405dd..e2810b854f795 100644 --- a/src/test/compile-fail/issue-1655.rs +++ b/src/test/compile-fail/issue-1655.rs @@ -8,7 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -// error-pattern:expected item +// error-pattern:expected `[` but found `~` mod blade_runner { #~[doc( brief = "Blade Runner is probably the best movie ever", diff --git a/src/test/run-pass/attr-mix-new.rs b/src/test/run-pass/attr-mix-new.rs index 397b49df91597..af615912f2823 100644 --- a/src/test/run-pass/attr-mix-new.rs +++ b/src/test/run-pass/attr-mix-new.rs @@ -13,4 +13,4 @@ mod foo { #![feature(globs)] } -fn main() {} \ No newline at end of file +pub fn main() {} diff --git a/src/test/run-pass/attr-shebang.rs b/src/test/run-pass/attr-shebang.rs index 12f568dd472af..f4919f768b9cd 100644 --- a/src/test/run-pass/attr-shebang.rs +++ b/src/test/run-pass/attr-shebang.rs @@ -1,4 +1,5 @@ #![allow(unknown_features)] #![feature(bogus)] -fn main() { } -// ignore-license \ No newline at end of file +pub fn main() { } +// ignore-license +// ignore-fast diff --git a/src/test/run-pass/attr.rs b/src/test/run-pass/attr.rs index 2f30eb8154ff1..6a19a44e07420 100644 --- a/src/test/run-pass/attr.rs +++ b/src/test/run-pass/attr.rs @@ -8,6 +8,8 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +// ignore-fast + #[main] fn foo() { }