detect unused attrs in one more place, allow parsing to continue for all

changed a bunch of fatal()'s into err()'s, to allow parsing to proceed.
This commit is contained in:
John Clements 2013-04-30 12:02:56 -07:00
parent 05ab83eea8
commit b621820dc4
4 changed files with 57 additions and 28 deletions

View File

@ -612,4 +612,20 @@ fn parser_done(p: Parser){
string_to_expr(@~"3 + 4");
string_to_expr(@~"a::z.froob(b,@(987+3))");
}
#[test] fn attrs_fix_bug () {
string_to_item(@~"pub fn mk_file_writer(path: &Path, flags: &[FileFlag])
-> Result<@Writer, ~str> {
#[cfg(windows)]
fn wb() -> c_int {
(O_WRONLY | libc::consts::os::extra::O_BINARY) as c_int
}
#[cfg(unix)]
fn wb() -> c_int { O_WRONLY as c_int }
let mut fflags: c_int = wb();
}");
}
}

View File

@ -2588,20 +2588,22 @@ fn parse_name_and_ty(&self,
})
}
// parse a statement. may include decl
fn parse_stmt(&self, first_item_attrs: ~[attribute]) -> @stmt {
// parse a statement. may include decl.
// precondition: any attributes are parsed already
fn parse_stmt(&self, item_attrs: ~[attribute]) -> @stmt {
maybe_whole!(self, nt_stmt);
fn check_expected_item(p: &Parser, current_attrs: &[attribute]) {
// If we have attributes then we should have an item
if !current_attrs.is_empty() {
p.fatal(~"expected item after attrs");
p.span_err(*p.last_span,
~"expected item after attributes");
}
}
let lo = self.span.lo;
if self.is_keyword("let") {
check_expected_item(self, first_item_attrs);
check_expected_item(self, item_attrs);
self.expect_keyword("let");
let decl = self.parse_let();
return @spanned(lo, decl.span.hi, stmt_decl(decl, self.get_id()));
@ -2614,7 +2616,7 @@ fn check_expected_item(p: &Parser, current_attrs: &[attribute]) {
// to the macro clause of parse_item_or_view_item. This
// could use some cleanup, it appears to me.
check_expected_item(self, first_item_attrs);
check_expected_item(self, item_attrs);
// Potential trouble: if we allow macros with paths instead of
// idents, we'd need to look ahead past the whole path here...
@ -2650,9 +2652,6 @@ fn check_expected_item(p: &Parser, current_attrs: &[attribute]) {
}
} else {
let item_attrs = vec::append(first_item_attrs,
self.parse_outer_attributes());
match self.parse_item_or_view_item(/*bad*/ copy item_attrs,
false) {
iovi_item(i) => {
@ -2727,6 +2726,7 @@ fn parse_block_tail_(&self, lo: BytePos, s: blk_check_mode,
let mut stmts = ~[];
let mut expr = None;
// wouldn't it be more uniform to parse view items only, here?
let ParsedItemsAndViewItems {
attrs_remaining: attrs_remaining,
view_items: view_items,
@ -2741,23 +2741,29 @@ fn parse_block_tail_(&self, lo: BytePos, s: blk_check_mode,
stmt_decl(decl, self.get_id())));
}
let mut initial_attrs = attrs_remaining;
let mut attributes_box = attrs_remaining;
if *self.token == token::RBRACE && !vec::is_empty(initial_attrs) {
self.fatal(~"expected item");
}
while *self.token != token::RBRACE {
while (*self.token != token::RBRACE) {
// parsing items even when they're not allowed lets us give
// better error messages and recover more gracefully.
attributes_box.push_all(self.parse_outer_attributes());
match *self.token {
token::SEMI => {
if !vec::is_empty(attributes_box) {
self.span_err(*self.last_span,~"expected item after attributes");
attributes_box = ~[];
}
self.bump(); // empty
}
token::RBRACE => {
// fall through and out.
}
_ => {
let stmt = self.parse_stmt(initial_attrs);
initial_attrs = ~[];
let stmt = self.parse_stmt(attributes_box);
attributes_box = ~[];
match stmt.node {
stmt_expr(e, stmt_id) => {
// Expression without semicolon
// expression without semicolon
match *self.token {
token::SEMI => {
self.bump();
@ -2773,7 +2779,7 @@ fn parse_block_tail_(&self, lo: BytePos, s: blk_check_mode,
self.fatal(
fmt!(
"expected `;` or `}` after \
expression but found `%s`",
expression but found `%s`",
self.token_to_str(&t)
)
);
@ -2782,9 +2788,8 @@ fn parse_block_tail_(&self, lo: BytePos, s: blk_check_mode,
}
}
}
stmt_mac(ref m, _) => {
// Statement macro; might be an expr
// statement macro; might be an expr
match *self.token {
token::SEMI => {
self.bump();
@ -2803,8 +2808,7 @@ fn parse_block_tail_(&self, lo: BytePos, s: blk_check_mode,
_ => { stmts.push(stmt); }
}
}
_ => { // All other kinds of statements:
_ => { // all other kinds of statements:
stmts.push(stmt);
if classify::stmt_ends_with_semi(stmt) {
@ -2815,6 +2819,11 @@ fn parse_block_tail_(&self, lo: BytePos, s: blk_check_mode,
}
}
}
if !vec::is_empty(attributes_box) {
self.span_err(*self.last_span,~"expected item after attributes");
}
let hi = self.span.hi;
self.bump();
let bloc = ast::blk_ {
@ -3519,7 +3528,7 @@ fn parse_mod_items(&self, term: token::Token,
if first && attrs_remaining_len > 0u {
// We parsed attributes for the first item but didn't find it
self.fatal(~"expected item");
self.span_err(*self.last_span,~"expected item after attributes");
}
ast::_mod { view_items: view_items, items: items }
@ -3724,11 +3733,15 @@ fn parse_foreign_mod_items(&self,
first_item_attrs: ~[attribute])
-> foreign_mod {
let ParsedItemsAndViewItems {
attrs_remaining: _,
attrs_remaining: attrs_remaining,
view_items: view_items,
items: _,
foreign_items: foreign_items
} = self.parse_foreign_items(first_item_attrs, true);
if (! attrs_remaining.is_empty()) {
self.span_err(*self.last_span,
~"expected item after attributes");
}
assert!(*self.token == token::RBRACE);
ast::foreign_mod {
sort: sort,

View File

@ -9,6 +9,6 @@
// except according to those terms.
fn main() {
#[attr]
debug!("hi"); //~ ERROR expected item after attrs
#[attr] //~ ERROR expected item after attributes
debug!("hi");
}

View File

@ -9,6 +9,6 @@
// except according to those terms.
fn main() {
#[attr]
let _i = 0; //~ ERROR expected item
#[attr] //~ ERROR expected item
let _i = 0;
}