libsyntax: Parse and report errors for a few obsolete syntaxes

This commit is contained in:
Brian Anderson 2012-09-08 15:50:29 -07:00
parent 2508c24276
commit 25dc59dc59
5 changed files with 253 additions and 14 deletions

View file

@ -78,6 +78,7 @@ fn parse_crate_from_crate_file(input: &Path, cfg: ast::crate_cfg,
cx, cdirs, &prefix, &companionmod);
let mut hi = p.span.hi;
p.expect(token::EOF);
p.abort_if_errors();
return @ast_util::respan(ast_util::mk_sp(lo, hi),
{directives: cdirs,
module: m,
@ -100,6 +101,7 @@ fn parse_crate_from_source_str(name: ~str, source: @~str, cfg: ast::crate_cfg,
let (p, rdr) = new_parser_etc_from_source_str(sess, cfg, name,
codemap::fss_none, source);
let r = p.parse_crate_mod(cfg);
p.abort_if_errors();
sess.chpos = rdr.chpos;
sess.byte_pos = sess.byte_pos + rdr.pos;
return r;
@ -110,6 +112,7 @@ fn parse_expr_from_source_str(name: ~str, source: @~str, cfg: ast::crate_cfg,
let (p, rdr) = new_parser_etc_from_source_str(sess, cfg, name,
codemap::fss_none, source);
let r = p.parse_expr();
p.abort_if_errors();
sess.chpos = rdr.chpos;
sess.byte_pos = sess.byte_pos + rdr.pos;
return r;
@ -121,6 +124,7 @@ fn parse_item_from_source_str(name: ~str, source: @~str, cfg: ast::crate_cfg,
let (p, rdr) = new_parser_etc_from_source_str(sess, cfg, name,
codemap::fss_none, source);
let r = p.parse_item(attrs);
p.abort_if_errors();
sess.chpos = rdr.chpos;
sess.byte_pos = sess.byte_pos + rdr.pos;
return r;
@ -132,6 +136,7 @@ fn parse_stmt_from_source_str(name: ~str, source: @~str, cfg: ast::crate_cfg,
let (p, rdr) = new_parser_etc_from_source_str(sess, cfg, name,
codemap::fss_none, source);
let r = p.parse_stmt(attrs);
p.abort_if_errors();
sess.chpos = rdr.chpos;
sess.byte_pos = sess.byte_pos + rdr.pos;
return r;
@ -149,6 +154,7 @@ fn parse_from_source_str<T>(f: fn (p: parser) -> T,
if !p.reader.is_eof() {
p.reader.fatal(~"expected end-of-string");
}
p.abort_if_errors();
sess.chpos = rdr.chpos;
sess.byte_pos = sess.byte_pos + rdr.pos;
return r;

View file

@ -0,0 +1,145 @@
/*!
Support for parsing unsupported, old syntaxes, for the
purpose of reporting errors. Parsing of these syntaxes
is tested by compile-test/obsolete-syntax.rs.
Obsolete syntax that becomes too hard to parse can be
removed.
*/
use codemap::span;
use ast::{expr, expr_lit, lit_nil};
use ast_util::{respan};
use token::token;
/// The specific types of unsupported syntax
pub enum ObsoleteSyntax {
ObsoleteLowerCaseKindBounds,
ObsoleteLet,
ObsoleteFieldTerminator,
ObsoleteStructCtor,
ObsoleteWith
}
impl ObsoleteSyntax : cmp::Eq {
pure fn eq(&&other: ObsoleteSyntax) -> bool {
self as uint == other as uint
}
pure fn ne(&&other: ObsoleteSyntax) -> bool {
!self.eq(other)
}
}
impl ObsoleteSyntax: to_bytes::IterBytes {
#[inline(always)]
fn iter_bytes(lsb0: bool, f: to_bytes::Cb) {
(self as uint).iter_bytes(lsb0, f);
}
}
pub trait ObsoleteReporter {
fn obsolete(sp: span, kind: ObsoleteSyntax);
fn obsolete_expr(sp: span, kind: ObsoleteSyntax) -> @expr;
}
impl parser : ObsoleteReporter {
/// Reports an obsolete syntax non-fatal error.
fn obsolete(sp: span, kind: ObsoleteSyntax) {
let (kind_str, desc) = match kind {
ObsoleteLowerCaseKindBounds => (
"lower-case kind bounds",
"the `send`, `copy`, `const`, and `owned` \
kinds are represented as traits now, and \
should be camel cased"
),
ObsoleteLet => (
"`let` in field declaration",
"declare fields as `field: Type`"
),
ObsoleteFieldTerminator => (
"field declaration terminated with semicolon",
"fields are now separated by commas"
),
ObsoleteStructCtor => (
"struct constructor",
"structs are now constructed with `MyStruct { foo: val }` \
syntax. Structs with private fields cannot be created \
outside of their defining module"
),
ObsoleteWith => (
"with",
"record update is done with `..`, e.g. \
`MyStruct { foo: bar, .. baz }`"
),
};
self.report(sp, kind, kind_str, desc);
}
// Reports an obsolete syntax non-fatal error, and returns
// a placeholder expression
fn obsolete_expr(sp: span, kind: ObsoleteSyntax) -> @expr {
self.obsolete(sp, kind);
self.mk_expr(sp.lo, sp.hi, expr_lit(@respan(sp, lit_nil)))
}
priv fn report(sp: span, kind: ObsoleteSyntax, kind_str: &str,
desc: &str) {
self.span_err(sp, fmt!("obsolete syntax: %s", kind_str));
if !self.obsolete_set.contains_key(kind) {
self.sess.span_diagnostic.handler().note(fmt!("%s", desc));
self.obsolete_set.insert(kind, ());
}
}
fn token_is_obsolete_ident(ident: &str, token: token) -> bool {
match token {
token::IDENT(copy sid, _) => {
str::eq_slice(*self.id_to_str(sid), ident)
}
_ => false
}
}
fn is_obsolete_ident(ident: &str) -> bool {
self.token_is_obsolete_ident(ident, copy self.token)
}
fn eat_obsolete_ident(ident: &str) -> bool {
if self.is_obsolete_ident(ident) {
self.bump();
true
} else {
false
}
}
fn try_parse_obsolete_struct_ctor() -> bool {
if self.eat_obsolete_ident("new") {
self.obsolete(copy self.last_span, ObsoleteStructCtor);
self.parse_fn_decl(|p| p.parse_arg());
self.parse_block();
true
} else {
false
}
}
fn try_parse_obsolete_with() -> bool {
if self.token == token::COMMA
&& self.token_is_obsolete_ident("with",
self.look_ahead(1u)) {
self.bump();
}
if self.eat_obsolete_ident("with") {
self.obsolete(copy self.last_span, ObsoleteWith);
self.parse_expr();
true
} else {
false
}
}
}

View file

@ -15,6 +15,12 @@ use common::{seq_sep_trailing_disallowed, seq_sep_trailing_allowed,
seq_sep_none, token_to_str};
use dvec::DVec;
use vec::{push};
use obsolete::{
ObsoleteReporter, ObsoleteSyntax,
ObsoleteLowerCaseKindBounds, ObsoleteLet,
ObsoleteFieldTerminator, ObsoleteStructCtor,
ObsoleteWith
};
use ast::{_mod, add, alt_check, alt_exhaustive, arg, arm, attribute,
bind_by_ref, bind_by_implicit_ref, bind_by_value, bind_by_move,
bitand, bitor, bitxor, blk, blk_check_mode, bound_const,
@ -208,7 +214,8 @@ fn parser(sess: parse_sess, cfg: ast::crate_cfg,
restriction: UNRESTRICTED,
quote_depth: 0u,
keywords: token::keyword_table(),
restricted_keywords: token::restricted_keyword_table()
restricted_keywords: token::restricted_keyword_table(),
obsolete_set: std::map::hashmap(),
}
}
@ -228,6 +235,9 @@ struct parser {
interner: interner<@~str>,
keywords: hashmap<~str, ()>,
restricted_keywords: hashmap<~str, ()>,
/// The set of seen errors about obsolete syntax. Used to suppress
/// extra detail when the same error is seen twice
obsolete_set: hashmap<ObsoleteSyntax, ()>,
drop {} /* do not copy the parser; its state is tied to outside state */
@ -276,6 +286,12 @@ struct parser {
fn warn(m: ~str) {
self.sess.span_diagnostic.span_warn(copy self.span, m)
}
fn span_err(sp: span, m: ~str) {
self.sess.span_diagnostic.span_err(sp, m)
}
fn abort_if_errors() {
self.sess.span_diagnostic.handler().abort_if_errors();
}
fn get_id() -> node_id { next_node_id(self.sess) }
pure fn id_to_str(id: ident) -> @~str { self.sess.interner.get(id) }
@ -1004,24 +1020,28 @@ struct parser {
// It's a struct literal.
self.bump();
let mut fields = ~[];
let mut base = None;
vec::push(fields, self.parse_field(token::COLON));
while self.token != token::RBRACE {
if self.try_parse_obsolete_with() {
break;
}
self.expect(token::COMMA);
if self.token == token::RBRACE ||
self.token == token::DOTDOT {
if self.eat(token::DOTDOT) {
base = Some(self.parse_expr());
break;
}
if self.token == token::RBRACE {
// Accept an optional trailing comma.
break;
}
vec::push(fields, self.parse_field(token::COLON));
}
let base;
if self.eat(token::DOTDOT) {
base = Some(self.parse_expr());
} else {
base = None;
}
hi = pth.span.hi;
self.expect(token::RBRACE);
ex = expr_struct(pth, fields, base);
@ -1664,6 +1684,10 @@ struct parser {
base = Some(self.parse_expr()); break;
}
if self.try_parse_obsolete_with() {
break;
}
self.expect(token::COMMA);
if self.token == token::RBRACE {
// record ends by an optional trailing comma
@ -2281,12 +2305,22 @@ struct parser {
if is_ident(self.token) {
// XXX: temporary until kinds become traits
let maybe_bound = match self.token {
token::IDENT(sid, _) => {
token::IDENT(copy sid, _) => {
match *self.id_to_str(sid) {
~"Send" => Some(bound_send),
~"Copy" => Some(bound_copy),
~"Const" => Some(bound_const),
~"Owned" => Some(bound_owned),
~"send"
| ~"copy"
| ~"const"
| ~"owned" => {
self.obsolete(copy self.span,
ObsoleteLowerCaseKindBounds);
None
}
_ => None
}
}
@ -2737,11 +2771,18 @@ struct parser {
}
fn parse_single_class_item(vis: visibility) -> @class_member {
if (self.token_is_keyword(~"mut", copy self.token) ||
!self.is_any_keyword(copy self.token)) &&
!self.token_is_pound_or_doc_comment(self.token) {
let obsolete_let = self.eat_obsolete_ident("let");
if obsolete_let { self.obsolete(copy self.last_span, ObsoleteLet) }
if (obsolete_let || self.token_is_keyword(~"mut", copy self.token) ||
!self.is_any_keyword(copy self.token)) &&
!self.token_is_pound_or_doc_comment(self.token) {
let a_var = self.parse_instance_var(vis);
match self.token {
token::SEMI => {
self.obsolete(copy self.span, ObsoleteFieldTerminator);
self.bump();
}
token::COMMA => {
self.bump();
}
@ -2792,6 +2833,10 @@ struct parser {
let attrs = self.parse_outer_attributes();
if self.try_parse_obsolete_struct_ctor() {
return members(~[]);
}
if self.eat_keyword(~"drop") {
return self.parse_dtor(attrs);
}

View file

@ -52,6 +52,9 @@ mod parse {
/// Routines the parser uses to classify AST nodes
mod classify;
/// Reporting obsolete syntax
mod obsolete;
}
mod print {

View file

@ -0,0 +1,40 @@
fn f1<T: copy>() -> T { }
//~^ ERROR obsolete syntax: lower-case kind bounds
fn f1<T: send>() -> T { }
//~^ ERROR obsolete syntax: lower-case kind bounds
fn f1<T: const>() -> T { }
//~^ ERROR obsolete syntax: lower-case kind bounds
fn f1<T: owned>() -> T { }
//~^ ERROR obsolete syntax: lower-case kind bounds
struct s {
let foo: (),
//~^ ERROR obsolete syntax: `let` in field declaration
bar: ();
//~^ ERROR obsolete syntax: field declaration terminated with semicolon
new() { }
//~^ ERROR obsolete syntax: struct constructor
}
fn obsolete_with() {
struct S {
foo: (),
bar: (),
}
let a = S { foo: (), bar: () };
let b = S { foo: () with a };
//~^ ERROR obsolete syntax: with
let c = S { foo: (), with a };
//~^ ERROR obsolete syntax: with
let a = { foo: (), bar: () };
let b = { foo: () with a };
//~^ ERROR obsolete syntax: with
let c = { foo: (), with a };
//~^ ERROR obsolete syntax: with
}
fn main() { }