syntax: Better error message for inner attr following doc comment
This commit is contained in:
parent
3dbbe2f716
commit
ff95ba3a8c
3 changed files with 92 additions and 14 deletions
|
@ -18,23 +18,43 @@ use parse::token;
|
||||||
use parse::parser::{Parser, TokenType};
|
use parse::parser::{Parser, TokenType};
|
||||||
use ptr::P;
|
use ptr::P;
|
||||||
|
|
||||||
|
#[derive(PartialEq, Eq, Debug)]
|
||||||
|
enum InnerAttributeParsePolicy<'a> {
|
||||||
|
Permitted,
|
||||||
|
NotPermitted { reason: &'a str },
|
||||||
|
}
|
||||||
|
|
||||||
|
const DEFAULT_UNEXPECTED_INNER_ATTR_ERR_MSG: &'static str = "an inner attribute is not \
|
||||||
|
permitted in this context";
|
||||||
|
|
||||||
impl<'a> Parser<'a> {
|
impl<'a> Parser<'a> {
|
||||||
/// Parse attributes that appear before an item
|
/// Parse attributes that appear before an item
|
||||||
pub fn parse_outer_attributes(&mut self) -> PResult<'a, Vec<ast::Attribute>> {
|
pub fn parse_outer_attributes(&mut self) -> PResult<'a, Vec<ast::Attribute>> {
|
||||||
let mut attrs: Vec<ast::Attribute> = Vec::new();
|
let mut attrs: Vec<ast::Attribute> = Vec::new();
|
||||||
|
let mut just_parsed_doc_comment = false;
|
||||||
loop {
|
loop {
|
||||||
debug!("parse_outer_attributes: self.token={:?}", self.token);
|
debug!("parse_outer_attributes: self.token={:?}", self.token);
|
||||||
match self.token {
|
match self.token {
|
||||||
token::Pound => {
|
token::Pound => {
|
||||||
attrs.push(self.parse_attribute(false)?);
|
let inner_error_reason = if just_parsed_doc_comment {
|
||||||
|
"an inner attribute is not permitted following an outer doc comment"
|
||||||
|
} else if !attrs.is_empty() {
|
||||||
|
"an inner attribute is not permitted following an outer attribute"
|
||||||
|
} else {
|
||||||
|
DEFAULT_UNEXPECTED_INNER_ATTR_ERR_MSG
|
||||||
|
};
|
||||||
|
let inner_parse_policy =
|
||||||
|
InnerAttributeParsePolicy::NotPermitted { reason: inner_error_reason };
|
||||||
|
attrs.push(self.parse_attribute_with_inner_parse_policy(inner_parse_policy)?);
|
||||||
|
just_parsed_doc_comment = false;
|
||||||
}
|
}
|
||||||
token::DocComment(s) => {
|
token::DocComment(s) => {
|
||||||
let attr = ::attr::mk_sugared_doc_attr(
|
let attr = ::attr::mk_sugared_doc_attr(
|
||||||
attr::mk_attr_id(),
|
attr::mk_attr_id(),
|
||||||
self.id_to_interned_str(ast::Ident::with_empty_ctxt(s)),
|
self.id_to_interned_str(ast::Ident::with_empty_ctxt(s)),
|
||||||
self.span.lo,
|
self.span.lo,
|
||||||
self.span.hi
|
self.span.hi
|
||||||
);
|
);
|
||||||
if attr.node.style != ast::AttrStyle::Outer {
|
if attr.node.style != ast::AttrStyle::Outer {
|
||||||
let mut err = self.fatal("expected outer doc comment");
|
let mut err = self.fatal("expected outer doc comment");
|
||||||
err.note("inner doc comments like this (starting with \
|
err.note("inner doc comments like this (starting with \
|
||||||
|
@ -43,6 +63,7 @@ impl<'a> Parser<'a> {
|
||||||
}
|
}
|
||||||
attrs.push(attr);
|
attrs.push(attr);
|
||||||
self.bump();
|
self.bump();
|
||||||
|
just_parsed_doc_comment = true;
|
||||||
}
|
}
|
||||||
_ => break,
|
_ => break,
|
||||||
}
|
}
|
||||||
|
@ -55,26 +76,46 @@ impl<'a> Parser<'a> {
|
||||||
/// If permit_inner is true, then a leading `!` indicates an inner
|
/// If permit_inner is true, then a leading `!` indicates an inner
|
||||||
/// attribute
|
/// attribute
|
||||||
pub fn parse_attribute(&mut self, permit_inner: bool) -> PResult<'a, ast::Attribute> {
|
pub fn parse_attribute(&mut self, permit_inner: bool) -> PResult<'a, ast::Attribute> {
|
||||||
debug!("parse_attributes: permit_inner={:?} self.token={:?}",
|
debug!("parse_attribute: permit_inner={:?} self.token={:?}",
|
||||||
permit_inner,
|
permit_inner,
|
||||||
self.token);
|
self.token);
|
||||||
|
let inner_parse_policy = if permit_inner {
|
||||||
|
InnerAttributeParsePolicy::Permitted
|
||||||
|
} else {
|
||||||
|
InnerAttributeParsePolicy::NotPermitted
|
||||||
|
{ reason: DEFAULT_UNEXPECTED_INNER_ATTR_ERR_MSG }
|
||||||
|
};
|
||||||
|
self.parse_attribute_with_inner_parse_policy(inner_parse_policy)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The same as `parse_attribute`, except it takes in an `InnerAttributeParsePolicy`
|
||||||
|
/// that prescribes how to handle inner attributes.
|
||||||
|
fn parse_attribute_with_inner_parse_policy(&mut self,
|
||||||
|
inner_parse_policy: InnerAttributeParsePolicy)
|
||||||
|
-> PResult<'a, ast::Attribute> {
|
||||||
|
debug!("parse_attribute_with_inner_parse_policy: inner_parse_policy={:?} self.token={:?}",
|
||||||
|
inner_parse_policy,
|
||||||
|
self.token);
|
||||||
let (span, value, mut style) = match self.token {
|
let (span, value, mut style) = match self.token {
|
||||||
token::Pound => {
|
token::Pound => {
|
||||||
let lo = self.span.lo;
|
let lo = self.span.lo;
|
||||||
self.bump();
|
self.bump();
|
||||||
|
|
||||||
if permit_inner {
|
if inner_parse_policy == InnerAttributeParsePolicy::Permitted {
|
||||||
self.expected_tokens.push(TokenType::Token(token::Not));
|
self.expected_tokens.push(TokenType::Token(token::Not));
|
||||||
}
|
}
|
||||||
let style = if self.token == token::Not {
|
let style = if self.token == token::Not {
|
||||||
self.bump();
|
self.bump();
|
||||||
if !permit_inner {
|
if let InnerAttributeParsePolicy::NotPermitted { reason } = inner_parse_policy
|
||||||
|
{
|
||||||
let span = self.span;
|
let span = self.span;
|
||||||
self.diagnostic()
|
self.diagnostic()
|
||||||
.struct_span_err(span,
|
.struct_span_err(span, reason)
|
||||||
"an inner attribute is not permitted in this context")
|
.note("inner attributes and doc comments, like `#![no_std]` or \
|
||||||
.help("place inner attribute at the top of the module or \
|
`//! My crate`, annotate the item enclosing them, and are \
|
||||||
block")
|
usually found at the beginning of source files. Outer \
|
||||||
|
attributes and doc comments, like `#[test]` and
|
||||||
|
`/// My function`, annotate the item following them.")
|
||||||
.emit()
|
.emit()
|
||||||
}
|
}
|
||||||
ast::AttrStyle::Inner
|
ast::AttrStyle::Inner
|
||||||
|
@ -95,7 +136,8 @@ impl<'a> Parser<'a> {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
if permit_inner && self.token == token::Semi {
|
if inner_parse_policy == InnerAttributeParsePolicy::Permitted &&
|
||||||
|
self.token == token::Semi {
|
||||||
self.bump();
|
self.bump();
|
||||||
self.span_warn(span,
|
self.span_warn(span,
|
||||||
"this inner attribute syntax is deprecated. The new syntax is \
|
"this inner attribute syntax is deprecated. The new syntax is \
|
||||||
|
|
20
src/test/parse-fail/inner-attr-after-doc-comment.rs
Normal file
20
src/test/parse-fail/inner-attr-after-doc-comment.rs
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
// Copyright 2016 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
|
||||||
|
|
||||||
|
#![feature(lang_items)]
|
||||||
|
/**
|
||||||
|
* My module
|
||||||
|
*/
|
||||||
|
|
||||||
|
#![recursion_limit="100"]
|
||||||
|
//~^ ERROR an inner attribute is not permitted following an outer doc comment
|
||||||
|
fn main() {}
|
16
src/test/parse-fail/inner-attr.rs
Normal file
16
src/test/parse-fail/inner-attr.rs
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
// Copyright 2016 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
|
||||||
|
|
||||||
|
#[feature(lang_items)]
|
||||||
|
|
||||||
|
#![recursion_limit="100"] //~ ERROR an inner attribute is not permitted following an outer attribute
|
||||||
|
fn main() {}
|
Loading…
Add table
Add a link
Reference in a new issue