Skip to content

Commit ff95ba3

Browse files
committed
syntax: Better error message for inner attr following doc comment
1 parent 3dbbe2f commit ff95ba3

File tree

3 files changed

+92
-14
lines changed

3 files changed

+92
-14
lines changed

src/libsyntax/parse/attr.rs

+56-14
Original file line numberDiff line numberDiff line change
@@ -18,23 +18,43 @@ use parse::token;
1818
use parse::parser::{Parser, TokenType};
1919
use ptr::P;
2020

21+
#[derive(PartialEq, Eq, Debug)]
22+
enum InnerAttributeParsePolicy<'a> {
23+
Permitted,
24+
NotPermitted { reason: &'a str },
25+
}
26+
27+
const DEFAULT_UNEXPECTED_INNER_ATTR_ERR_MSG: &'static str = "an inner attribute is not \
28+
permitted in this context";
29+
2130
impl<'a> Parser<'a> {
2231
/// Parse attributes that appear before an item
2332
pub fn parse_outer_attributes(&mut self) -> PResult<'a, Vec<ast::Attribute>> {
2433
let mut attrs: Vec<ast::Attribute> = Vec::new();
34+
let mut just_parsed_doc_comment = false;
2535
loop {
2636
debug!("parse_outer_attributes: self.token={:?}", self.token);
2737
match self.token {
2838
token::Pound => {
29-
attrs.push(self.parse_attribute(false)?);
39+
let inner_error_reason = if just_parsed_doc_comment {
40+
"an inner attribute is not permitted following an outer doc comment"
41+
} else if !attrs.is_empty() {
42+
"an inner attribute is not permitted following an outer attribute"
43+
} else {
44+
DEFAULT_UNEXPECTED_INNER_ATTR_ERR_MSG
45+
};
46+
let inner_parse_policy =
47+
InnerAttributeParsePolicy::NotPermitted { reason: inner_error_reason };
48+
attrs.push(self.parse_attribute_with_inner_parse_policy(inner_parse_policy)?);
49+
just_parsed_doc_comment = false;
3050
}
3151
token::DocComment(s) => {
3252
let attr = ::attr::mk_sugared_doc_attr(
33-
attr::mk_attr_id(),
34-
self.id_to_interned_str(ast::Ident::with_empty_ctxt(s)),
35-
self.span.lo,
36-
self.span.hi
37-
);
53+
attr::mk_attr_id(),
54+
self.id_to_interned_str(ast::Ident::with_empty_ctxt(s)),
55+
self.span.lo,
56+
self.span.hi
57+
);
3858
if attr.node.style != ast::AttrStyle::Outer {
3959
let mut err = self.fatal("expected outer doc comment");
4060
err.note("inner doc comments like this (starting with \
@@ -43,6 +63,7 @@ impl<'a> Parser<'a> {
4363
}
4464
attrs.push(attr);
4565
self.bump();
66+
just_parsed_doc_comment = true;
4667
}
4768
_ => break,
4869
}
@@ -55,26 +76,46 @@ impl<'a> Parser<'a> {
5576
/// If permit_inner is true, then a leading `!` indicates an inner
5677
/// attribute
5778
pub fn parse_attribute(&mut self, permit_inner: bool) -> PResult<'a, ast::Attribute> {
58-
debug!("parse_attributes: permit_inner={:?} self.token={:?}",
79+
debug!("parse_attribute: permit_inner={:?} self.token={:?}",
5980
permit_inner,
6081
self.token);
82+
let inner_parse_policy = if permit_inner {
83+
InnerAttributeParsePolicy::Permitted
84+
} else {
85+
InnerAttributeParsePolicy::NotPermitted
86+
{ reason: DEFAULT_UNEXPECTED_INNER_ATTR_ERR_MSG }
87+
};
88+
self.parse_attribute_with_inner_parse_policy(inner_parse_policy)
89+
}
90+
91+
/// The same as `parse_attribute`, except it takes in an `InnerAttributeParsePolicy`
92+
/// that prescribes how to handle inner attributes.
93+
fn parse_attribute_with_inner_parse_policy(&mut self,
94+
inner_parse_policy: InnerAttributeParsePolicy)
95+
-> PResult<'a, ast::Attribute> {
96+
debug!("parse_attribute_with_inner_parse_policy: inner_parse_policy={:?} self.token={:?}",
97+
inner_parse_policy,
98+
self.token);
6199
let (span, value, mut style) = match self.token {
62100
token::Pound => {
63101
let lo = self.span.lo;
64102
self.bump();
65103

66-
if permit_inner {
104+
if inner_parse_policy == InnerAttributeParsePolicy::Permitted {
67105
self.expected_tokens.push(TokenType::Token(token::Not));
68106
}
69107
let style = if self.token == token::Not {
70108
self.bump();
71-
if !permit_inner {
109+
if let InnerAttributeParsePolicy::NotPermitted { reason } = inner_parse_policy
110+
{
72111
let span = self.span;
73112
self.diagnostic()
74-
.struct_span_err(span,
75-
"an inner attribute is not permitted in this context")
76-
.help("place inner attribute at the top of the module or \
77-
block")
113+
.struct_span_err(span, reason)
114+
.note("inner attributes and doc comments, like `#![no_std]` or \
115+
`//! My crate`, annotate the item enclosing them, and are \
116+
usually found at the beginning of source files. Outer \
117+
attributes and doc comments, like `#[test]` and
118+
`/// My function`, annotate the item following them.")
78119
.emit()
79120
}
80121
ast::AttrStyle::Inner
@@ -95,7 +136,8 @@ impl<'a> Parser<'a> {
95136
}
96137
};
97138

98-
if permit_inner && self.token == token::Semi {
139+
if inner_parse_policy == InnerAttributeParsePolicy::Permitted &&
140+
self.token == token::Semi {
99141
self.bump();
100142
self.span_warn(span,
101143
"this inner attribute syntax is deprecated. The new syntax is \
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
// compile-flags: -Z parse-only
12+
13+
#![feature(lang_items)]
14+
/**
15+
* My module
16+
*/
17+
18+
#![recursion_limit="100"]
19+
//~^ ERROR an inner attribute is not permitted following an outer doc comment
20+
fn main() {}

src/test/parse-fail/inner-attr.rs

+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
// compile-flags: -Z parse-only
12+
13+
#[feature(lang_items)]
14+
15+
#![recursion_limit="100"] //~ ERROR an inner attribute is not permitted following an outer attribute
16+
fn main() {}

0 commit comments

Comments
 (0)