Skip to content

Commit c4788c2

Browse files
authoredJul 16, 2016
Auto merge of #34676 - aravind-pg:inner-attr, r=brson
Better error message for inner attribute following doc comment Before it was always just "an inner attribute is not permitted in this context", whereas now we add a special case for when an inner attr follows an outer attr. If the outer attr is a doc comment, then the error is "an inner attr is not permitted following a doc comment", and otherwise it's "an inner attr is not permitted following an outer attribute". In all other cases it's still "an inner attribute is not permitted in this context". Note that the public API and behaviour of `parse_attribute` is unchanged. Also, all new names are very open to bikeshedding -- they're arguably clunky. Fixes #34516. cc @brson
2 parents 145f0ec + ff95ba3 commit c4788c2

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)