Skip to content

Commit 519b040

Browse files
authored
Rollup merge of rust-lang#61026 - estebank:macro-eof-spans, r=petrochenkov
Tweak macro parse errors when reaching EOF during macro call parse Add detail on origin of current parser when reaching EOF, stop saying "found `<eof>`" and point at the end of macro calls. Fix rust-lang#27569.
2 parents dec4c52 + ee7593e commit 519b040

20 files changed

+184
-114
lines changed

src/librustc/traits/on_unimplemented.rs

+12-12
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

+8-1
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/ext/base.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ use crate::parse::{self, parser, DirectoryOwnership};
1111
use crate::parse::token;
1212
use crate::ptr::P;
1313
use crate::symbol::{kw, sym, Ident, Symbol};
14-
use crate::ThinVec;
14+
use crate::{ThinVec, MACRO_ARGUMENTS};
1515
use crate::tokenstream::{self, TokenStream};
1616

1717
use errors::{DiagnosticBuilder, DiagnosticId};
@@ -850,7 +850,7 @@ 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(self.parse_sess, tts.iter().cloned().collect(), MACRO_ARGUMENTS)
854854
}
855855
pub fn source_map(&self) -> &'a SourceMap { self.parse_sess.source_map() }
856856
pub fn parse_sess(&self) -> &'a parse::ParseSess { self.parse_sess }

src/libsyntax/ext/tt/macro_parser.rs

+8-1
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+
crate::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

+1-1
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/lib.rs

+2
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,8 @@ pub use rustc_data_structures::thin_vec::ThinVec;
3131
use ast::AttrId;
3232
use syntax_pos::edition::Edition;
3333

34+
const MACRO_ARGUMENTS: Option<&'static str> = Some("macro arguments");
35+
3436
// A variant of 'try!' that panics on an Err. This is used as a crutch on the
3537
// way towards a non-panic!-prone parser. It should be used for fatal parsing
3638
// errors; eventually we plan to convert all code using panictry to just use

src/libsyntax/parse/diagnostics.rs

+73-2
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ use crate::symbol::kw;
1313
use crate::ThinVec;
1414
use errors::{Applicability, DiagnosticBuilder};
1515
use log::debug;
16-
use syntax_pos::Span;
16+
use syntax_pos::{Span, DUMMY_SP};
1717

1818
pub trait RecoverQPath: Sized + 'static {
1919
const PATH_STYLE: PathStyle = PathStyle::Expr;
@@ -201,7 +201,7 @@ impl<'a> Parser<'a> {
201201

202202
let mut path = ast::Path {
203203
segments: Vec::new(),
204-
span: syntax_pos::DUMMY_SP,
204+
span: DUMMY_SP,
205205
};
206206
self.parse_path_segments(&mut path.segments, T::PATH_STYLE)?;
207207
path.span = ty_span.to(self.prev_span);
@@ -267,6 +267,58 @@ impl<'a> Parser<'a> {
267267
}
268268
}
269269

270+
/// Create a `DiagnosticBuilder` for an unexpected token `t` and try to recover if it is a
271+
/// closing delimiter.
272+
pub fn unexpected_try_recover(
273+
&mut self,
274+
t: &token::Token,
275+
) -> PResult<'a, bool /* recovered */> {
276+
let token_str = pprust::token_to_string(t);
277+
let this_token_str = self.this_token_descr();
278+
let (prev_sp, sp) = match (&self.token, self.subparser_name) {
279+
// Point at the end of the macro call when reaching end of macro arguments.
280+
(token::Token::Eof, Some(_)) => {
281+
let sp = self.sess.source_map().next_point(self.span);
282+
(sp, sp)
283+
}
284+
// We don't want to point at the following span after DUMMY_SP.
285+
// This happens when the parser finds an empty TokenStream.
286+
_ if self.prev_span == DUMMY_SP => (self.span, self.span),
287+
// EOF, don't want to point at the following char, but rather the last token.
288+
(token::Token::Eof, None) => (self.prev_span, self.span),
289+
_ => (self.sess.source_map().next_point(self.prev_span), self.span),
290+
};
291+
let msg = format!(
292+
"expected `{}`, found {}",
293+
token_str,
294+
match (&self.token, self.subparser_name) {
295+
(token::Token::Eof, Some(origin)) => format!("end of {}", origin),
296+
_ => this_token_str,
297+
},
298+
);
299+
let mut err = self.struct_span_err(sp, &msg);
300+
let label_exp = format!("expected `{}`", token_str);
301+
match self.recover_closing_delimiter(&[t.clone()], err) {
302+
Err(e) => err = e,
303+
Ok(recovered) => {
304+
return Ok(recovered);
305+
}
306+
}
307+
let cm = self.sess.source_map();
308+
match (cm.lookup_line(prev_sp.lo()), cm.lookup_line(sp.lo())) {
309+
(Ok(ref a), Ok(ref b)) if a.line == b.line => {
310+
// When the spans are in the same line, it means that the only content
311+
// between them is whitespace, point only at the found token.
312+
err.span_label(sp, label_exp);
313+
}
314+
_ => {
315+
err.span_label(prev_sp, label_exp);
316+
err.span_label(sp, "unexpected token");
317+
}
318+
}
319+
Err(err)
320+
}
321+
270322
/// Consume alternative await syntaxes like `await <expr>`, `await? <expr>`, `await(<expr>)`
271323
/// and `await { <expr> }`.
272324
crate fn parse_incorrect_await_syntax(
@@ -562,4 +614,23 @@ impl<'a> Parser<'a> {
562614
}
563615
}
564616

617+
crate fn expected_expression_found(&self) -> DiagnosticBuilder<'a> {
618+
let (span, msg) = match (&self.token, self.subparser_name) {
619+
(&token::Token::Eof, Some(origin)) => {
620+
let sp = self.sess.source_map().next_point(self.span);
621+
(sp, format!("expected expression, found end of {}", origin))
622+
}
623+
_ => (self.span, format!(
624+
"expected expression, found {}",
625+
self.this_token_descr(),
626+
)),
627+
};
628+
let mut err = self.struct_span_err(span, &msg);
629+
let sp = self.sess.source_map().start_point(self.span);
630+
if let Some(sp) = self.sess.ambiguous_block_expr_parse.borrow().get(&sp) {
631+
self.sess.expr_parentheses_needed(&mut err, *sp, None);
632+
}
633+
err.span_label(span, "expected expression");
634+
err
635+
}
565636
}

src/libsyntax/parse/mod.rs

+14-8
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(), crate::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+
subparser_name: Option<&'static str>,
335+
) -> Parser<'a> {
336+
Parser::new(sess, stream, None, true, false, subparser_name)
333337
}
334338

335339
/// Given stream, the `ParseSess` and the base directory, produces a parser.
@@ -343,10 +347,12 @@ pub fn stream_to_parser(sess: &ParseSess, stream: TokenStream) -> Parser<'_> {
343347
/// The main usage of this function is outside of rustc, for those who uses
344348
/// libsyntax as a library. Please do not remove this function while refactoring
345349
/// just because it is not used in rustc codebase!
346-
pub fn stream_to_parser_with_base_dir<'a>(sess: &'a ParseSess,
347-
stream: TokenStream,
348-
base_dir: Directory<'a>) -> Parser<'a> {
349-
Parser::new(sess, stream, Some(base_dir), true, false)
350+
pub fn stream_to_parser_with_base_dir<'a>(
351+
sess: &'a ParseSess,
352+
stream: TokenStream,
353+
base_dir: Directory<'a>,
354+
) -> Parser<'a> {
355+
Parser::new(sess, stream, Some(base_dir), true, false, None)
350356
}
351357

352358
/// A sequence separator.

0 commit comments

Comments
 (0)