From 4e00cf613428d24d305a89e4f8e79b70ea8e8322 Mon Sep 17 00:00:00 2001 From: Daniel Fagnan Date: Mon, 24 Feb 2014 19:42:40 -0700 Subject: [PATCH] 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 c30ab4bda27..92b93fd88dd 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 8abc01b6d75..1221d8401be 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 58ec6acc3ac..061d460af5e 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 00000000000..4107c84c472 --- /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 00000000000..397b49df915 --- /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 00000000000..12f568dd472 --- /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 00000000000..2f30eb8154f --- /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() { +}