Skip to content

Commit 2416017

Browse files
committed
Tweak macro parse errors when reaching EOF during macro call parse
- Add detail on origin of current parser when reaching EOF and stop saying "found <eof>" and point at the end of macro calls - Handle empty `cfg_attr` attribute - Reword empty `derive` attribute error
1 parent fc45382 commit 2416017

21 files changed

+176
-93
lines changed

src/libfmt_macros/lib.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -627,7 +627,7 @@ mod tests {
627627
use super::*;
628628

629629
fn same(fmt: &'static str, p: &[Piece<'static>]) {
630-
let parser = Parser::new(fmt, None, vec![], false);
630+
let parser = Parser::new(fmt, None, vec![], false, None);
631631
assert!(parser.collect::<Vec<Piece<'static>>>() == p);
632632
}
633633

@@ -643,7 +643,7 @@ mod tests {
643643
}
644644

645645
fn musterr(s: &str) {
646-
let mut p = Parser::new(s, None, vec![], false);
646+
let mut p = Parser::new(s, None, vec![], false, None);
647647
p.next();
648648
assert!(!p.errors.is_empty());
649649
}

src/librustc/traits/on_unimplemented.rs

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -226,12 +226,12 @@ impl<'a, 'gcx, 'tcx> OnUnimplementedFormatString {
226226
Ok(result)
227227
}
228228

229-
fn verify(&self,
230-
tcx: TyCtxt<'a, 'gcx, 'tcx>,
231-
trait_def_id: DefId,
232-
span: Span)
233-
-> Result<(), ErrorReported>
234-
{
229+
fn verify(
230+
&self,
231+
tcx: TyCtxt<'a, 'gcx, 'tcx>,
232+
trait_def_id: DefId,
233+
span: Span,
234+
) -> Result<(), ErrorReported> {
235235
let name = tcx.item_name(trait_def_id);
236236
let generics = tcx.generics_of(trait_def_id);
237237
let parser = Parser::new(&self.0, None, vec![], false);
@@ -272,12 +272,12 @@ impl<'a, 'gcx, 'tcx> OnUnimplementedFormatString {
272272
result
273273
}
274274

275-
pub fn format(&self,
276-
tcx: TyCtxt<'a, 'gcx, 'tcx>,
277-
trait_ref: ty::TraitRef<'tcx>,
278-
options: &FxHashMap<String, String>)
279-
-> String
280-
{
275+
pub fn format(
276+
&self,
277+
tcx: TyCtxt<'a, 'gcx, 'tcx>,
278+
trait_ref: ty::TraitRef<'tcx>,
279+
options: &FxHashMap<String, String>,
280+
) -> String {
281281
let name = tcx.item_name(trait_ref.def_id);
282282
let trait_str = tcx.def_path_str(trait_ref.def_id);
283283
let generics = tcx.generics_of(trait_ref.def_id);

src/libsyntax/attr/mod.rs

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -278,7 +278,14 @@ impl Attribute {
278278
pub fn parse<'a, T, F>(&self, sess: &'a ParseSess, mut f: F) -> PResult<'a, T>
279279
where F: FnMut(&mut Parser<'a>) -> PResult<'a, T>,
280280
{
281-
let mut parser = Parser::new(sess, self.tokens.clone(), None, false, false);
281+
let mut parser = Parser::new(
282+
sess,
283+
self.tokens.clone(),
284+
None,
285+
false,
286+
false,
287+
Some("attribute"),
288+
);
282289
let result = f(&mut parser)?;
283290
if parser.token != token::Eof {
284291
parser.unexpected()?;

src/libsyntax/config.rs

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,17 @@ impl<'a> StripUnconfigured<'a> {
9494
if !attr.check_name(sym::cfg_attr) {
9595
return vec![attr];
9696
}
97+
if attr.tokens.len() == 0 {
98+
self.sess.span_diagnostic.struct_span_err(attr.span, "bad `cfg_attr` attribute")
99+
.span_label(attr.span, "missing condition and attribute")
100+
.note("`cfg_attr` must be of the form: \
101+
`#[cfg_attr(condition, attribute)]`")
102+
.note("for more information, visit \
103+
<https://doc.rust-lang.org/reference/conditional-compilation.html\
104+
#the-cfg_attr-attribute>")
105+
.emit();
106+
return vec![];
107+
}
97108

98109
let (cfg_predicate, expanded_attrs) = match attr.parse(self.sess, |parser| {
99110
parser.expect(&token::OpenDelim(token::Paren))?;
@@ -117,7 +128,7 @@ impl<'a> StripUnconfigured<'a> {
117128
Ok(result) => result,
118129
Err(mut e) => {
119130
e.emit();
120-
return Vec::new();
131+
return vec![];
121132
}
122133
};
123134

src/libsyntax/ext/base.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -850,7 +850,11 @@ impl<'a> ExtCtxt<'a> {
850850
}
851851

852852
pub fn new_parser_from_tts(&self, tts: &[tokenstream::TokenTree]) -> parser::Parser<'a> {
853-
parse::stream_to_parser(self.parse_sess, tts.iter().cloned().collect())
853+
parse::stream_to_parser(
854+
self.parse_sess,
855+
tts.iter().cloned().collect(),
856+
Some("macro arguments"),
857+
)
854858
}
855859
pub fn source_map(&self) -> &'a SourceMap { self.parse_sess.source_map() }
856860
pub fn parse_sess(&self) -> &'a parse::ParseSess { self.parse_sess }

src/libsyntax/ext/derive.rs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,11 @@ pub fn collect_derives(cx: &mut ExtCtxt<'_>, attrs: &mut Vec<ast::Attribute>) ->
1717
return true;
1818
}
1919
if !attr.is_meta_item_list() {
20-
cx.span_err(attr.span,
21-
"attribute must be of the form `#[derive(Trait1, Trait2, ...)]`");
20+
cx.struct_span_err(attr.span, "bad `derive` attribute")
21+
.span_label(attr.span, "missing traits to be derived")
22+
.note("`derive` must be of the form: \
23+
`#[derive(Trait1, Trait2, ...)]`")
24+
.emit();
2225
return false;
2326
}
2427

src/libsyntax/ext/tt/macro_parser.rs

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -658,7 +658,14 @@ pub fn parse(
658658
recurse_into_modules: bool,
659659
) -> NamedParseResult {
660660
// Create a parser that can be used for the "black box" parts.
661-
let mut parser = Parser::new(sess, tts, directory, recurse_into_modules, true);
661+
let mut parser = Parser::new(
662+
sess,
663+
tts,
664+
directory,
665+
recurse_into_modules,
666+
true,
667+
Some("macro arguments"),
668+
);
662669

663670
// A queue of possible matcher positions. We initialize it with the matcher position in which
664671
// the "dot" is before the first token of the first token tree in `ms`. `inner_parse_loop` then

src/libsyntax/ext/tt/macro_rules.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -172,7 +172,7 @@ fn generic_extension<'cx>(cx: &'cx mut ExtCtxt<'_>,
172172
path: Cow::from(cx.current_expansion.module.directory.as_path()),
173173
ownership: cx.current_expansion.directory_ownership,
174174
};
175-
let mut p = Parser::new(cx.parse_sess(), tts, Some(directory), true, false);
175+
let mut p = Parser::new(cx.parse_sess(), tts, Some(directory), true, false, None);
176176
p.root_module_name = cx.current_expansion.module.mod_path.last()
177177
.map(|id| id.as_str().to_string());
178178

src/libsyntax/parse/mod.rs

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -236,7 +236,7 @@ fn maybe_source_file_to_parser(
236236
) -> Result<Parser<'_>, Vec<Diagnostic>> {
237237
let end_pos = source_file.end_pos;
238238
let (stream, unclosed_delims) = maybe_file_to_stream(sess, source_file, None)?;
239-
let mut parser = stream_to_parser(sess, stream);
239+
let mut parser = stream_to_parser(sess, stream, None);
240240
parser.unclosed_delims = unclosed_delims;
241241
if parser.token == token::Eof && parser.span.is_dummy() {
242242
parser.span = Span::new(end_pos, end_pos, parser.span.ctxt());
@@ -248,7 +248,7 @@ fn maybe_source_file_to_parser(
248248
// must preserve old name for now, because quote! from the *existing*
249249
// compiler expands into it
250250
pub fn new_parser_from_tts(sess: &ParseSess, tts: Vec<TokenTree>) -> Parser<'_> {
251-
stream_to_parser(sess, tts.into_iter().collect())
251+
stream_to_parser(sess, tts.into_iter().collect(), Some("macro arguments"))
252252
}
253253

254254

@@ -328,8 +328,12 @@ pub fn maybe_file_to_stream(
328328
}
329329

330330
/// Given stream and the `ParseSess`, produces a parser.
331-
pub fn stream_to_parser(sess: &ParseSess, stream: TokenStream) -> Parser<'_> {
332-
Parser::new(sess, stream, None, true, false)
331+
pub fn stream_to_parser<'a>(
332+
sess: &'a ParseSess,
333+
stream: TokenStream,
334+
is_subparser: Option<&'static str>,
335+
) -> Parser<'a> {
336+
Parser::new(sess, stream, None, true, false, is_subparser)
333337
}
334338

335339
/// Given stream, the `ParseSess` and the base directory, produces a parser.

src/libsyntax/parse/parser.rs

Lines changed: 58 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ use crate::symbol::{kw, sym, Symbol};
5151
use errors::{Applicability, DiagnosticBuilder, DiagnosticId, FatalError};
5252
use rustc_target::spec::abi::{self, Abi};
5353
use syntax_pos::{
54-
Span, MultiSpan, BytePos, FileName,
54+
BytePos, DUMMY_SP, FileName, MultiSpan, Span,
5555
hygiene::CompilerDesugaringKind,
5656
};
5757
use log::{debug, trace};
@@ -233,6 +233,8 @@ pub struct Parser<'a> {
233233
/// error.
234234
crate unclosed_delims: Vec<UnmatchedBrace>,
235235
last_unexpected_token_span: Option<Span>,
236+
/// If `true`, this `Parser` is not parsing Rust code but rather a macro call.
237+
is_subparser: Option<&'static str>,
236238
}
237239

238240
impl<'a> Drop for Parser<'a> {
@@ -309,7 +311,7 @@ impl TokenCursor {
309311
self.frame = frame;
310312
continue
311313
} else {
312-
return TokenAndSpan { tok: token::Eof, sp: syntax_pos::DUMMY_SP }
314+
return TokenAndSpan { tok: token::Eof, sp: DUMMY_SP }
313315
};
314316

315317
match self.frame.last_token {
@@ -533,17 +535,19 @@ enum TokenExpectType {
533535
}
534536

535537
impl<'a> Parser<'a> {
536-
pub fn new(sess: &'a ParseSess,
537-
tokens: TokenStream,
538-
directory: Option<Directory<'a>>,
539-
recurse_into_file_modules: bool,
540-
desugar_doc_comments: bool)
541-
-> Self {
538+
pub fn new(
539+
sess: &'a ParseSess,
540+
tokens: TokenStream,
541+
directory: Option<Directory<'a>>,
542+
recurse_into_file_modules: bool,
543+
desugar_doc_comments: bool,
544+
is_subparser: Option<&'static str>,
545+
) -> Self {
542546
let mut parser = Parser {
543547
sess,
544548
token: token::Whitespace,
545-
span: syntax_pos::DUMMY_SP,
546-
prev_span: syntax_pos::DUMMY_SP,
549+
span: DUMMY_SP,
550+
prev_span: DUMMY_SP,
547551
meta_var_span: None,
548552
prev_token_kind: PrevTokenKind::Other,
549553
restrictions: Restrictions::empty(),
@@ -568,6 +572,7 @@ impl<'a> Parser<'a> {
568572
max_angle_bracket_count: 0,
569573
unclosed_delims: Vec::new(),
570574
last_unexpected_token_span: None,
575+
is_subparser,
571576
};
572577

573578
let tok = parser.next_tok();
@@ -639,16 +644,28 @@ impl<'a> Parser<'a> {
639644
} else {
640645
let token_str = pprust::token_to_string(t);
641646
let this_token_str = self.this_token_descr();
642-
let mut err = self.fatal(&format!("expected `{}`, found {}",
643-
token_str,
644-
this_token_str));
645-
646-
let sp = if self.token == token::Token::Eof {
647-
// EOF, don't want to point at the following char, but rather the last token
648-
self.prev_span
649-
} else {
650-
self.sess.source_map().next_point(self.prev_span)
647+
let (prev_sp, sp) = match (&self.token, self.is_subparser) {
648+
// Point at the end of the macro call when reaching end of macro arguments.
649+
(token::Token::Eof, Some(_)) => {
650+
let sp = self.sess.source_map().next_point(self.span);
651+
(sp, sp)
652+
}
653+
// We don't want to point at the following span after DUMMY_SP.
654+
// This happens when the parser finds an empty TokenStream.
655+
_ if self.prev_span == DUMMY_SP => (self.span, self.span),
656+
// EOF, don't want to point at the following char, but rather the last token.
657+
(token::Token::Eof, None) => (self.prev_span, self.span),
658+
_ => (self.sess.source_map().next_point(self.prev_span), self.span),
651659
};
660+
let msg = format!(
661+
"expected `{}`, found {}",
662+
token_str,
663+
match (&self.token, self.is_subparser) {
664+
(token::Token::Eof, Some(origin)) => format!("end of {}", origin),
665+
_ => this_token_str,
666+
},
667+
);
668+
let mut err = self.struct_span_err(sp, &msg);
652669
let label_exp = format!("expected `{}`", token_str);
653670
match self.recover_closing_delimiter(&[t.clone()], err) {
654671
Err(e) => err = e,
@@ -657,15 +674,15 @@ impl<'a> Parser<'a> {
657674
}
658675
}
659676
let cm = self.sess.source_map();
660-
match (cm.lookup_line(self.span.lo()), cm.lookup_line(sp.lo())) {
677+
match (cm.lookup_line(prev_sp.lo()), cm.lookup_line(sp.lo())) {
661678
(Ok(ref a), Ok(ref b)) if a.line == b.line => {
662679
// When the spans are in the same line, it means that the only content
663680
// between them is whitespace, point only at the found token.
664-
err.span_label(self.span, label_exp);
681+
err.span_label(sp, label_exp);
665682
}
666683
_ => {
667-
err.span_label(sp, label_exp);
668-
err.span_label(self.span, "unexpected token");
684+
err.span_label(prev_sp, label_exp);
685+
err.span_label(sp, "unexpected token");
669686
}
670687
}
671688
Err(err)
@@ -812,7 +829,7 @@ impl<'a> Parser<'a> {
812829
// | expected one of 8 possible tokens here
813830
err.span_label(self.span, label_exp);
814831
}
815-
_ if self.prev_span == syntax_pos::DUMMY_SP => {
832+
_ if self.prev_span == DUMMY_SP => {
816833
// Account for macro context where the previous span might not be
817834
// available to avoid incorrect output (#54841).
818835
err.span_label(self.span, "unexpected token");
@@ -2041,7 +2058,7 @@ impl<'a> Parser<'a> {
20412058
path = self.parse_path(PathStyle::Type)?;
20422059
path_span = path_lo.to(self.prev_span);
20432060
} else {
2044-
path = ast::Path { segments: Vec::new(), span: syntax_pos::DUMMY_SP };
2061+
path = ast::Path { segments: Vec::new(), span: DUMMY_SP };
20452062
path_span = self.span.to(self.span);
20462063
}
20472064

@@ -2627,16 +2644,24 @@ impl<'a> Parser<'a> {
26272644
}
26282645
Err(mut err) => {
26292646
self.cancel(&mut err);
2630-
let msg = format!("expected expression, found {}",
2631-
self.this_token_descr());
2632-
let mut err = self.fatal(&msg);
2647+
let (span, msg) = match (&self.token, self.is_subparser) {
2648+
(&token::Token::Eof, Some(origin)) => {
2649+
let sp = self.sess.source_map().next_point(self.span);
2650+
(sp, format!( "expected expression, found end of {}", origin))
2651+
}
2652+
_ => (self.span, format!(
2653+
"expected expression, found {}",
2654+
self.this_token_descr(),
2655+
)),
2656+
};
2657+
let mut err = self.struct_span_err(span, &msg);
26332658
let sp = self.sess.source_map().start_point(self.span);
26342659
if let Some(sp) = self.sess.ambiguous_block_expr_parse.borrow()
26352660
.get(&sp)
26362661
{
26372662
self.sess.expr_parentheses_needed(&mut err, *sp, None);
26382663
}
2639-
err.span_label(self.span, "expected expression");
2664+
err.span_label(span, "expected expression");
26402665
return Err(err);
26412666
}
26422667
}
@@ -5592,7 +5617,7 @@ impl<'a> Parser<'a> {
55925617
where_clause: WhereClause {
55935618
id: ast::DUMMY_NODE_ID,
55945619
predicates: Vec::new(),
5595-
span: syntax_pos::DUMMY_SP,
5620+
span: DUMMY_SP,
55965621
},
55975622
span: span_lo.to(self.prev_span),
55985623
})
@@ -5838,7 +5863,7 @@ impl<'a> Parser<'a> {
58385863
let mut where_clause = WhereClause {
58395864
id: ast::DUMMY_NODE_ID,
58405865
predicates: Vec::new(),
5841-
span: syntax_pos::DUMMY_SP,
5866+
span: DUMMY_SP,
58425867
};
58435868

58445869
if !self.eat_keyword(kw::Where) {
@@ -7005,15 +7030,15 @@ impl<'a> Parser<'a> {
70057030
Ident::with_empty_ctxt(sym::warn_directory_ownership)),
70067031
tokens: TokenStream::empty(),
70077032
is_sugared_doc: false,
7008-
span: syntax_pos::DUMMY_SP,
7033+
span: DUMMY_SP,
70097034
};
70107035
attr::mark_known(&attr);
70117036
attrs.push(attr);
70127037
}
70137038
Ok((id, ItemKind::Mod(module), Some(attrs)))
70147039
} else {
70157040
let placeholder = ast::Mod {
7016-
inner: syntax_pos::DUMMY_SP,
7041+
inner: DUMMY_SP,
70177042
items: Vec::new(),
70187043
inline: false
70197044
};

0 commit comments

Comments
 (0)