Strip frontmatter in fewer places

This commit is contained in:
León Orell Valerian Liehr
2025-09-08 19:25:22 +02:00
parent ec87250101
commit 7a66925a81
21 changed files with 210 additions and 117 deletions

View File

@@ -13,9 +13,11 @@ use rustc_expand::base::{
}; };
use rustc_expand::module::DirOwnership; use rustc_expand::module::DirOwnership;
use rustc_lint_defs::BuiltinLintDiag; use rustc_lint_defs::BuiltinLintDiag;
use rustc_parse::parser::{ForceCollect, Parser}; use rustc_parse::lexer::StripTokens;
use rustc_parse::parser::ForceCollect;
use rustc_parse::{new_parser_from_file, unwrap_or_emit_fatal, utf8_error}; use rustc_parse::{new_parser_from_file, unwrap_or_emit_fatal, utf8_error};
use rustc_session::lint::builtin::INCOMPLETE_INCLUDE; use rustc_session::lint::builtin::INCOMPLETE_INCLUDE;
use rustc_session::parse::ParseSess;
use rustc_span::source_map::SourceMap; use rustc_span::source_map::SourceMap;
use rustc_span::{ByteSymbol, Pos, Span, Symbol}; use rustc_span::{ByteSymbol, Pos, Span, Symbol};
use smallvec::SmallVec; use smallvec::SmallVec;
@@ -114,39 +116,48 @@ pub(crate) fn expand_include<'cx>(
let ExpandResult::Ready(mac) = get_single_str_from_tts(cx, sp, tts, "include!") else { let ExpandResult::Ready(mac) = get_single_str_from_tts(cx, sp, tts, "include!") else {
return ExpandResult::Retry(()); return ExpandResult::Retry(());
}; };
let file = match mac { let path = match mac {
Ok(file) => file, Ok(path) => path,
Err(guar) => return ExpandResult::Ready(DummyResult::any(sp, guar)), Err(guar) => return ExpandResult::Ready(DummyResult::any(sp, guar)),
}; };
// The file will be added to the code map by the parser // The file will be added to the code map by the parser
let file = match resolve_path(&cx.sess, file.as_str(), sp) { let path = match resolve_path(&cx.sess, path.as_str(), sp) {
Ok(f) => f, Ok(path) => path,
Err(err) => { Err(err) => {
let guar = err.emit(); let guar = err.emit();
return ExpandResult::Ready(DummyResult::any(sp, guar)); return ExpandResult::Ready(DummyResult::any(sp, guar));
} }
}; };
let p = unwrap_or_emit_fatal(new_parser_from_file(cx.psess(), &file, Some(sp)));
// If in the included file we have e.g., `mod bar;`, // If in the included file we have e.g., `mod bar;`,
// then the path of `bar.rs` should be relative to the directory of `file`. // then the path of `bar.rs` should be relative to the directory of `path`.
// See https://github.com/rust-lang/rust/pull/69838/files#r395217057 for a discussion. // See https://github.com/rust-lang/rust/pull/69838/files#r395217057 for a discussion.
// `MacroExpander::fully_expand_fragment` later restores, so "stack discipline" is maintained. // `MacroExpander::fully_expand_fragment` later restores, so "stack discipline" is maintained.
let dir_path = file.parent().unwrap_or(&file).to_owned(); let dir_path = path.parent().unwrap_or(&path).to_owned();
cx.current_expansion.module = Rc::new(cx.current_expansion.module.with_dir_path(dir_path)); cx.current_expansion.module = Rc::new(cx.current_expansion.module.with_dir_path(dir_path));
cx.current_expansion.dir_ownership = DirOwnership::Owned { relative: None }; cx.current_expansion.dir_ownership = DirOwnership::Owned { relative: None };
struct ExpandInclude<'a> { struct ExpandInclude<'a> {
p: Parser<'a>, psess: &'a ParseSess,
path: PathBuf,
node_id: ast::NodeId, node_id: ast::NodeId,
span: Span,
} }
impl<'a> MacResult for ExpandInclude<'a> { impl<'a> MacResult for ExpandInclude<'a> {
fn make_expr(mut self: Box<ExpandInclude<'a>>) -> Option<Box<ast::Expr>> { fn make_expr(self: Box<ExpandInclude<'a>>) -> Option<Box<ast::Expr>> {
let expr = parse_expr(&mut self.p).ok()?; let mut p = unwrap_or_emit_fatal(new_parser_from_file(
if self.p.token != token::Eof { self.psess,
self.p.psess.buffer_lint( &self.path,
// Don't strip frontmatter for backward compatibility, `---` may be the start of a
// manifold negation. FIXME: Ideally, we wouldn't strip shebangs here either.
StripTokens::Shebang,
Some(self.span),
));
let expr = parse_expr(&mut p).ok()?;
if p.token != token::Eof {
p.psess.buffer_lint(
INCOMPLETE_INCLUDE, INCOMPLETE_INCLUDE,
self.p.token.span, p.token.span,
self.node_id, self.node_id,
BuiltinLintDiag::IncompleteInclude, BuiltinLintDiag::IncompleteInclude,
); );
@@ -154,24 +165,27 @@ pub(crate) fn expand_include<'cx>(
Some(expr) Some(expr)
} }
fn make_items(mut self: Box<ExpandInclude<'a>>) -> Option<SmallVec<[Box<ast::Item>; 1]>> { fn make_items(self: Box<ExpandInclude<'a>>) -> Option<SmallVec<[Box<ast::Item>; 1]>> {
let mut p = unwrap_or_emit_fatal(new_parser_from_file(
self.psess,
&self.path,
StripTokens::ShebangAndFrontmatter,
Some(self.span),
));
let mut ret = SmallVec::new(); let mut ret = SmallVec::new();
loop { loop {
match self.p.parse_item(ForceCollect::No) { match p.parse_item(ForceCollect::No) {
Err(err) => { Err(err) => {
err.emit(); err.emit();
break; break;
} }
Ok(Some(item)) => ret.push(item), Ok(Some(item)) => ret.push(item),
Ok(None) => { Ok(None) => {
if self.p.token != token::Eof { if p.token != token::Eof {
self.p p.dcx().emit_err(errors::ExpectedItem {
.dcx() span: p.token.span,
.create_err(errors::ExpectedItem { token: &pprust::token_to_string(&p.token),
span: self.p.token.span, });
token: &pprust::token_to_string(&self.p.token),
})
.emit();
} }
break; break;
@@ -182,7 +196,12 @@ pub(crate) fn expand_include<'cx>(
} }
} }
ExpandResult::Ready(Box::new(ExpandInclude { p, node_id: cx.current_expansion.lint_node_id })) ExpandResult::Ready(Box::new(ExpandInclude {
psess: cx.psess(),
path,
node_id: cx.current_expansion.lint_node_id,
span: sp,
}))
} }
/// Expand `include_str!($input)` to the content of the UTF-8-encoded file given by path `$input` as a string literal. /// Expand `include_str!($input)` to the content of the UTF-8-encoded file given by path `$input` as a string literal.

View File

@@ -51,6 +51,7 @@ use rustc_lint::unerased_lint_store;
use rustc_metadata::creader::MetadataLoader; use rustc_metadata::creader::MetadataLoader;
use rustc_metadata::locator; use rustc_metadata::locator;
use rustc_middle::ty::TyCtxt; use rustc_middle::ty::TyCtxt;
use rustc_parse::lexer::StripTokens;
use rustc_parse::{new_parser_from_file, new_parser_from_source_str, unwrap_or_emit_fatal}; use rustc_parse::{new_parser_from_file, new_parser_from_source_str, unwrap_or_emit_fatal};
use rustc_session::config::{ use rustc_session::config::{
CG_OPTIONS, CrateType, ErrorOutputType, Input, OptionDesc, OutFileName, OutputType, Sysroot, CG_OPTIONS, CrateType, ErrorOutputType, Input, OptionDesc, OutFileName, OutputType, Sysroot,
@@ -1288,10 +1289,15 @@ fn warn_on_confusing_output_filename_flag(
fn parse_crate_attrs<'a>(sess: &'a Session) -> PResult<'a, ast::AttrVec> { fn parse_crate_attrs<'a>(sess: &'a Session) -> PResult<'a, ast::AttrVec> {
let mut parser = unwrap_or_emit_fatal(match &sess.io.input { let mut parser = unwrap_or_emit_fatal(match &sess.io.input {
Input::File(file) => new_parser_from_file(&sess.psess, file, None), Input::File(file) => {
Input::Str { name, input } => { new_parser_from_file(&sess.psess, file, StripTokens::ShebangAndFrontmatter, None)
new_parser_from_source_str(&sess.psess, name.clone(), input.clone())
} }
Input::Str { name, input } => new_parser_from_source_str(
&sess.psess,
name.clone(),
input.clone(),
StripTokens::ShebangAndFrontmatter,
),
}); });
parser.parse_inner_attributes() parser.parse_inner_attributes()
} }

View File

@@ -4,6 +4,7 @@ use std::path::{self, Path, PathBuf};
use rustc_ast::{AttrVec, Attribute, Inline, Item, ModSpans}; use rustc_ast::{AttrVec, Attribute, Inline, Item, ModSpans};
use rustc_attr_parsing::validate_attr; use rustc_attr_parsing::validate_attr;
use rustc_errors::{Diag, ErrorGuaranteed}; use rustc_errors::{Diag, ErrorGuaranteed};
use rustc_parse::lexer::StripTokens;
use rustc_parse::{exp, new_parser_from_file, unwrap_or_emit_fatal}; use rustc_parse::{exp, new_parser_from_file, unwrap_or_emit_fatal};
use rustc_session::Session; use rustc_session::Session;
use rustc_session::parse::ParseSess; use rustc_session::parse::ParseSess;
@@ -67,8 +68,12 @@ pub(crate) fn parse_external_mod(
} }
// Actually parse the external file as a module. // Actually parse the external file as a module.
let mut parser = let mut parser = unwrap_or_emit_fatal(new_parser_from_file(
unwrap_or_emit_fatal(new_parser_from_file(&sess.psess, &mp.file_path, Some(span))); &sess.psess,
&mp.file_path,
StripTokens::ShebangAndFrontmatter,
Some(span),
));
let (inner_attrs, items, inner_span) = let (inner_attrs, items, inner_span) =
parser.parse_mod(exp!(Eof)).map_err(|err| ModError::ParserError(err))?; parser.parse_mod(exp!(Eof)).map_err(|err| ModError::ParserError(err))?;
attrs.extend(inner_attrs); attrs.extend(inner_attrs);

View File

@@ -8,7 +8,7 @@ use rustc_ast::util::literal::escape_byte_str_symbol;
use rustc_ast_pretty::pprust; use rustc_ast_pretty::pprust;
use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::fx::FxHashMap;
use rustc_errors::{Diag, ErrorGuaranteed, MultiSpan, PResult}; use rustc_errors::{Diag, ErrorGuaranteed, MultiSpan, PResult};
use rustc_parse::lexer::nfc_normalize; use rustc_parse::lexer::{StripTokens, nfc_normalize};
use rustc_parse::parser::Parser; use rustc_parse::parser::Parser;
use rustc_parse::{exp, new_parser_from_source_str, source_str_to_stream, unwrap_or_emit_fatal}; use rustc_parse::{exp, new_parser_from_source_str, source_str_to_stream, unwrap_or_emit_fatal};
use rustc_proc_macro::bridge::{ use rustc_proc_macro::bridge::{
@@ -485,8 +485,13 @@ impl server::FreeFunctions for Rustc<'_, '_> {
fn literal_from_str(&mut self, s: &str) -> Result<Literal<Self::Span, Self::Symbol>, ()> { fn literal_from_str(&mut self, s: &str) -> Result<Literal<Self::Span, Self::Symbol>, ()> {
let name = FileName::proc_macro_source_code(s); let name = FileName::proc_macro_source_code(s);
let mut parser =
unwrap_or_emit_fatal(new_parser_from_source_str(self.psess(), name, s.to_owned())); let mut parser = unwrap_or_emit_fatal(new_parser_from_source_str(
self.psess(),
name,
s.to_owned(),
StripTokens::Nothing,
));
let first_span = parser.token.span.data(); let first_span = parser.token.span.data();
let minus_present = parser.eat(exp!(Minus)); let minus_present = parser.eat(exp!(Minus));

View File

@@ -13,7 +13,8 @@ use rustc_lint::LintStore;
use rustc_middle::ty; use rustc_middle::ty;
use rustc_middle::ty::CurrentGcx; use rustc_middle::ty::CurrentGcx;
use rustc_middle::util::Providers; use rustc_middle::util::Providers;
use rustc_parse::new_parser_from_simple_source_str; use rustc_parse::lexer::StripTokens;
use rustc_parse::new_parser_from_source_str;
use rustc_parse::parser::attr::AllowLeadingUnsafe; use rustc_parse::parser::attr::AllowLeadingUnsafe;
use rustc_query_impl::QueryCtxt; use rustc_query_impl::QueryCtxt;
use rustc_query_system::query::print_query_stack; use rustc_query_system::query::print_query_stack;
@@ -68,7 +69,8 @@ pub(crate) fn parse_cfg(dcx: DiagCtxtHandle<'_>, cfgs: Vec<String>) -> Cfg {
}; };
} }
match new_parser_from_simple_source_str(&psess, filename, s.to_string()) { match new_parser_from_source_str(&psess, filename, s.to_string(), StripTokens::Nothing)
{
Ok(mut parser) => match parser.parse_meta_item(AllowLeadingUnsafe::No) { Ok(mut parser) => match parser.parse_meta_item(AllowLeadingUnsafe::No) {
Ok(meta_item) if parser.token == token::Eof => { Ok(meta_item) if parser.token == token::Eof => {
if meta_item.path.segments.len() != 1 { if meta_item.path.segments.len() != 1 {
@@ -166,13 +168,15 @@ pub(crate) fn parse_check_cfg(dcx: DiagCtxtHandle<'_>, specs: Vec<String>) -> Ch
error!("expected `cfg(name, values(\"value1\", \"value2\", ... \"valueN\"))`") error!("expected `cfg(name, values(\"value1\", \"value2\", ... \"valueN\"))`")
}; };
let mut parser = match new_parser_from_simple_source_str(&psess, filename, s.to_string()) { let mut parser =
Ok(parser) => parser, match new_parser_from_source_str(&psess, filename, s.to_string(), StripTokens::Nothing)
Err(errs) => { {
errs.into_iter().for_each(|err| err.cancel()); Ok(parser) => parser,
expected_error(); Err(errs) => {
} errs.into_iter().for_each(|err| err.cancel());
}; expected_error();
}
};
let meta_item = match parser.parse_meta_item(AllowLeadingUnsafe::No) { let meta_item = match parser.parse_meta_item(AllowLeadingUnsafe::No) {
Ok(meta_item) if parser.token == token::Eof => meta_item, Ok(meta_item) if parser.token == token::Eof => meta_item,

View File

@@ -27,6 +27,7 @@ use rustc_middle::arena::Arena;
use rustc_middle::dep_graph::DepsType; use rustc_middle::dep_graph::DepsType;
use rustc_middle::ty::{self, CurrentGcx, GlobalCtxt, RegisteredTools, TyCtxt}; use rustc_middle::ty::{self, CurrentGcx, GlobalCtxt, RegisteredTools, TyCtxt};
use rustc_middle::util::Providers; use rustc_middle::util::Providers;
use rustc_parse::lexer::StripTokens;
use rustc_parse::{new_parser_from_file, new_parser_from_source_str, unwrap_or_emit_fatal}; use rustc_parse::{new_parser_from_file, new_parser_from_source_str, unwrap_or_emit_fatal};
use rustc_passes::{abi_test, input_stats, layout_test}; use rustc_passes::{abi_test, input_stats, layout_test};
use rustc_resolve::{Resolver, ResolverOutputs}; use rustc_resolve::{Resolver, ResolverOutputs};
@@ -51,10 +52,18 @@ pub fn parse<'a>(sess: &'a Session) -> ast::Crate {
let mut krate = sess let mut krate = sess
.time("parse_crate", || { .time("parse_crate", || {
let mut parser = unwrap_or_emit_fatal(match &sess.io.input { let mut parser = unwrap_or_emit_fatal(match &sess.io.input {
Input::File(file) => new_parser_from_file(&sess.psess, file, None), Input::File(file) => new_parser_from_file(
Input::Str { input, name } => { &sess.psess,
new_parser_from_source_str(&sess.psess, name.clone(), input.clone()) file,
} StripTokens::ShebangAndFrontmatter,
None,
),
Input::Str { input, name } => new_parser_from_source_str(
&sess.psess,
name.clone(),
input.clone(),
StripTokens::ShebangAndFrontmatter,
),
}); });
parser.parse_crate_mod() parser.parse_crate_mod()
}) })

View File

@@ -45,7 +45,7 @@ pub(crate) struct UnmatchedDelim {
} }
/// Which tokens should be stripped before lexing the tokens. /// Which tokens should be stripped before lexing the tokens.
pub(crate) enum StripTokens { pub enum StripTokens {
/// Strip both shebang and frontmatter. /// Strip both shebang and frontmatter.
ShebangAndFrontmatter, ShebangAndFrontmatter,
/// Strip the shebang but not frontmatter. /// Strip the shebang but not frontmatter.

View File

@@ -54,29 +54,18 @@ pub fn unwrap_or_emit_fatal<T>(expr: Result<T, Vec<Diag<'_>>>) -> T {
} }
} }
/// Creates a new parser from a source string. On failure, the errors must be consumed via /// Creates a new parser from a source string.
/// `unwrap_or_emit_fatal`, `emit`, `cancel`, etc., otherwise a panic will occur when they are ///
/// dropped. /// On failure, the errors must be consumed via `unwrap_or_emit_fatal`, `emit`, `cancel`,
/// etc., otherwise a panic will occur when they are dropped.
pub fn new_parser_from_source_str( pub fn new_parser_from_source_str(
psess: &ParseSess, psess: &ParseSess,
name: FileName, name: FileName,
source: String, source: String,
strip_tokens: StripTokens,
) -> Result<Parser<'_>, Vec<Diag<'_>>> { ) -> Result<Parser<'_>, Vec<Diag<'_>>> {
let source_file = psess.source_map().new_source_file(name, source); let source_file = psess.source_map().new_source_file(name, source);
new_parser_from_source_file(psess, source_file, StripTokens::ShebangAndFrontmatter) new_parser_from_source_file(psess, source_file, strip_tokens)
}
/// Creates a new parser from a simple (no shebang, no frontmatter) source string.
///
/// On failure, the errors must be consumed via `unwrap_or_emit_fatal`, `emit`, `cancel`,
/// etc., otherwise a panic will occur when they are dropped.
pub fn new_parser_from_simple_source_str(
psess: &ParseSess,
name: FileName,
source: String,
) -> Result<Parser<'_>, Vec<Diag<'_>>> {
let source_file = psess.source_map().new_source_file(name, source);
new_parser_from_source_file(psess, source_file, StripTokens::Nothing)
} }
/// Creates a new parser from a filename. On failure, the errors must be consumed via /// Creates a new parser from a filename. On failure, the errors must be consumed via
@@ -87,6 +76,7 @@ pub fn new_parser_from_simple_source_str(
pub fn new_parser_from_file<'a>( pub fn new_parser_from_file<'a>(
psess: &'a ParseSess, psess: &'a ParseSess,
path: &Path, path: &Path,
strip_tokens: StripTokens,
sp: Option<Span>, sp: Option<Span>,
) -> Result<Parser<'a>, Vec<Diag<'a>>> { ) -> Result<Parser<'a>, Vec<Diag<'a>>> {
let sm = psess.source_map(); let sm = psess.source_map();
@@ -110,7 +100,7 @@ pub fn new_parser_from_file<'a>(
} }
err.emit(); err.emit();
}); });
new_parser_from_source_file(psess, source_file, StripTokens::ShebangAndFrontmatter) new_parser_from_source_file(psess, source_file, strip_tokens)
} }
pub fn utf8_error<E: EmissionGuarantee>( pub fn utf8_error<E: EmissionGuarantee>(
@@ -172,6 +162,9 @@ fn new_parser_from_source_file(
Ok(parser) Ok(parser)
} }
/// Given a source string, produces a sequence of token trees.
///
/// NOTE: This only strips shebangs, not frontmatter!
pub fn source_str_to_stream( pub fn source_str_to_stream(
psess: &ParseSess, psess: &ParseSess,
name: FileName, name: FileName,
@@ -179,13 +172,16 @@ pub fn source_str_to_stream(
override_span: Option<Span>, override_span: Option<Span>,
) -> Result<TokenStream, Vec<Diag<'_>>> { ) -> Result<TokenStream, Vec<Diag<'_>>> {
let source_file = psess.source_map().new_source_file(name, source); let source_file = psess.source_map().new_source_file(name, source);
// used mainly for `proc_macro` and the likes, not for our parsing purposes, so don't parse // FIXME(frontmatter): Consider stripping frontmatter in a future edition. We can't strip them
// frontmatters as frontmatters, but for compatibility reason still strip the shebang // in the current edition since that would be breaking.
// See also <https://github.com/rust-lang/rust/issues/145520>.
// Alternatively, stop stripping shebangs here, too, if T-lang and crater approve.
source_file_to_stream(psess, source_file, override_span, StripTokens::Shebang) source_file_to_stream(psess, source_file, override_span, StripTokens::Shebang)
} }
/// Given a source file, produces a sequence of token trees. Returns any buffered errors from /// Given a source file, produces a sequence of token trees.
/// parsing the token stream. ///
/// Returns any buffered errors from parsing the token stream.
fn source_file_to_stream<'psess>( fn source_file_to_stream<'psess>(
psess: &'psess ParseSess, psess: &'psess ParseSess,
source_file: Arc<SourceFile>, source_file: Arc<SourceFile>,

View File

@@ -22,6 +22,7 @@ use rustc_span::{
}; };
use termcolor::WriteColor; use termcolor::WriteColor;
use crate::lexer::StripTokens;
use crate::parser::{ForceCollect, Parser}; use crate::parser::{ForceCollect, Parser};
use crate::{new_parser_from_source_str, source_str_to_stream, unwrap_or_emit_fatal}; use crate::{new_parser_from_source_str, source_str_to_stream, unwrap_or_emit_fatal};
@@ -35,6 +36,7 @@ fn string_to_parser(psess: &ParseSess, source_str: String) -> Parser<'_> {
psess, psess,
PathBuf::from("bogofile").into(), PathBuf::from("bogofile").into(),
source_str, source_str,
StripTokens::Nothing,
)) ))
} }
@@ -2240,7 +2242,7 @@ fn parse_item_from_source_str(
source: String, source: String,
psess: &ParseSess, psess: &ParseSess,
) -> PResult<'_, Option<Box<ast::Item>>> { ) -> PResult<'_, Option<Box<ast::Item>>> {
unwrap_or_emit_fatal(new_parser_from_source_str(psess, name, source)) unwrap_or_emit_fatal(new_parser_from_source_str(psess, name, source, StripTokens::Nothing))
.parse_item(ForceCollect::No) .parse_item(ForceCollect::No)
} }
@@ -2520,7 +2522,8 @@ fn ttdelim_span() {
source: String, source: String,
psess: &ParseSess, psess: &ParseSess,
) -> PResult<'_, Box<ast::Expr>> { ) -> PResult<'_, Box<ast::Expr>> {
unwrap_or_emit_fatal(new_parser_from_source_str(psess, name, source)).parse_expr() unwrap_or_emit_fatal(new_parser_from_source_str(psess, name, source, StripTokens::Nothing))
.parse_expr()
} }
create_default_session_globals_then(|| { create_default_session_globals_then(|| {

View File

@@ -3,6 +3,7 @@ use rustc_ast::tokenstream::{TokenStream, TokenTree};
use rustc_ast_pretty::pprust::PrintState; use rustc_ast_pretty::pprust::PrintState;
use rustc_ast_pretty::pprust::state::State as Printer; use rustc_ast_pretty::pprust::state::State as Printer;
use rustc_middle::ty::TyCtxt; use rustc_middle::ty::TyCtxt;
use rustc_parse::lexer::StripTokens;
use rustc_session::parse::ParseSess; use rustc_session::parse::ParseSess;
use rustc_span::symbol::{Ident, Symbol, kw}; use rustc_span::symbol::{Ident, Symbol, kw};
use rustc_span::{FileName, Span}; use rustc_span::{FileName, Span};
@@ -64,14 +65,18 @@ fn snippet_equal_to_token(tcx: TyCtxt<'_>, matcher: &TokenTree) -> Option<String
// Create a Parser. // Create a Parser.
let psess = ParseSess::new(rustc_driver::DEFAULT_LOCALE_RESOURCES.to_vec()); let psess = ParseSess::new(rustc_driver::DEFAULT_LOCALE_RESOURCES.to_vec());
let file_name = FileName::macro_expansion_source_code(&snippet); let file_name = FileName::macro_expansion_source_code(&snippet);
let mut parser = let mut parser = match rustc_parse::new_parser_from_source_str(
match rustc_parse::new_parser_from_source_str(&psess, file_name, snippet.clone()) { &psess,
Ok(parser) => parser, file_name,
Err(errs) => { snippet.clone(),
errs.into_iter().for_each(|err| err.cancel()); StripTokens::Nothing,
return None; ) {
} Ok(parser) => parser,
}; Err(errs) => {
errs.into_iter().for_each(|err| err.cancel());
return None;
}
};
// Reparse a single token tree. // Reparse a single token tree.
if parser.token == token::Eof { if parser.token == token::Eof {

View File

@@ -10,6 +10,7 @@ use rustc_ast::tokenstream::TokenTree;
use rustc_ast::{self as ast, AttrStyle, HasAttrs, StmtKind}; use rustc_ast::{self as ast, AttrStyle, HasAttrs, StmtKind};
use rustc_errors::emitter::stderr_destination; use rustc_errors::emitter::stderr_destination;
use rustc_errors::{ColorConfig, DiagCtxtHandle}; use rustc_errors::{ColorConfig, DiagCtxtHandle};
use rustc_parse::lexer::StripTokens;
use rustc_parse::new_parser_from_source_str; use rustc_parse::new_parser_from_source_str;
use rustc_session::parse::ParseSess; use rustc_session::parse::ParseSess;
use rustc_span::edition::{DEFAULT_EDITION, Edition}; use rustc_span::edition::{DEFAULT_EDITION, Edition};
@@ -468,14 +469,16 @@ fn parse_source(
let dcx = DiagCtxt::new(Box::new(emitter)).disable_warnings(); let dcx = DiagCtxt::new(Box::new(emitter)).disable_warnings();
let psess = ParseSess::with_dcx(dcx, sm); let psess = ParseSess::with_dcx(dcx, sm);
let mut parser = match new_parser_from_source_str(&psess, filename, wrapped_source) { // Don't strip any tokens; it wouldn't matter anyway because the source is wrapped in a function.
Ok(p) => p, let mut parser =
Err(errs) => { match new_parser_from_source_str(&psess, filename, wrapped_source, StripTokens::Nothing) {
errs.into_iter().for_each(|err| err.cancel()); Ok(p) => p,
reset_error_count(&psess); Err(errs) => {
return Err(()); errs.into_iter().for_each(|err| err.cancel());
} reset_error_count(&psess);
}; return Err(());
}
};
fn push_to_s(s: &mut String, source: &str, span: rustc_span::Span, prev_span_hi: &mut usize) { fn push_to_s(s: &mut String, source: &str, span: rustc_span::Span, prev_span_hi: &mut usize) {
let extra_len = DOCTEST_CODE_WRAPPER.len(); let extra_len = DOCTEST_CODE_WRAPPER.len();

View File

@@ -8,6 +8,7 @@ use rustc_ast::{CoroutineKind, Fn, FnRetTy, Item, ItemKind};
use rustc_errors::emitter::HumanEmitter; use rustc_errors::emitter::HumanEmitter;
use rustc_errors::{Diag, DiagCtxt}; use rustc_errors::{Diag, DiagCtxt};
use rustc_lint::LateContext; use rustc_lint::LateContext;
use rustc_parse::lexer::StripTokens;
use rustc_parse::new_parser_from_source_str; use rustc_parse::new_parser_from_source_str;
use rustc_parse::parser::ForceCollect; use rustc_parse::parser::ForceCollect;
use rustc_session::parse::ParseSess; use rustc_session::parse::ParseSess;
@@ -49,13 +50,14 @@ pub fn check(
let sm = Arc::new(SourceMap::new(FilePathMapping::empty())); let sm = Arc::new(SourceMap::new(FilePathMapping::empty()));
let psess = ParseSess::with_dcx(dcx, sm); let psess = ParseSess::with_dcx(dcx, sm);
let mut parser = match new_parser_from_source_str(&psess, filename, code) { let mut parser =
Ok(p) => p, match new_parser_from_source_str(&psess, filename, code, StripTokens::ShebangAndFrontmatter) {
Err(errs) => { Ok(p) => p,
errs.into_iter().for_each(Diag::cancel); Err(errs) => {
return (false, test_attr_spans); errs.into_iter().for_each(Diag::cancel);
}, return (false, test_attr_spans);
}; },
};
let mut relevant_main_found = false; let mut relevant_main_found = false;
let mut eligible = true; let mut eligible = true;

View File

@@ -3,6 +3,7 @@ use std::path::{Path, PathBuf};
use rustc_ast::{ast, attr}; use rustc_ast::{ast, attr};
use rustc_errors::Diag; use rustc_errors::Diag;
use rustc_parse::lexer::StripTokens;
use rustc_parse::parser::Parser as RawParser; use rustc_parse::parser::Parser as RawParser;
use rustc_parse::{exp, new_parser_from_file, new_parser_from_source_str, unwrap_or_emit_fatal}; use rustc_parse::{exp, new_parser_from_file, new_parser_from_source_str, unwrap_or_emit_fatal};
use rustc_span::{Span, sym}; use rustc_span::{Span, sym};
@@ -64,11 +65,14 @@ impl<'a> ParserBuilder<'a> {
input: Input, input: Input,
) -> Result<RawParser<'a>, Vec<Diag<'a>>> { ) -> Result<RawParser<'a>, Vec<Diag<'a>>> {
match input { match input {
Input::File(ref file) => new_parser_from_file(psess, file, None), Input::File(ref file) => {
new_parser_from_file(psess, file, StripTokens::ShebangAndFrontmatter, None)
}
Input::Text(text) => new_parser_from_source_str( Input::Text(text) => new_parser_from_source_str(
psess, psess,
rustc_span::FileName::Custom("stdin".to_owned()), rustc_span::FileName::Custom("stdin".to_owned()),
text, text,
StripTokens::ShebangAndFrontmatter,
), ),
} }
} }
@@ -104,8 +108,12 @@ impl<'a> Parser<'a> {
span: Span, span: Span,
) -> Result<(ast::AttrVec, ThinVec<Box<ast::Item>>, Span), ParserError> { ) -> Result<(ast::AttrVec, ThinVec<Box<ast::Item>>, Span), ParserError> {
let result = catch_unwind(AssertUnwindSafe(|| { let result = catch_unwind(AssertUnwindSafe(|| {
let mut parser = let mut parser = unwrap_or_emit_fatal(new_parser_from_file(
unwrap_or_emit_fatal(new_parser_from_file(psess.inner(), path, Some(span))); psess.inner(),
path,
StripTokens::ShebangAndFrontmatter,
Some(span),
));
match parser.parse_mod(exp!(Eof)) { match parser.parse_mod(exp!(Eof)) {
Ok((a, i, spans)) => Some((a, i, spans.inner_span)), Ok((a, i, spans)) => Some((a, i, spans.inner_span)),
Err(e) => { Err(e) => {

View File

@@ -10,7 +10,7 @@ extern crate rustc_span;
use rustc_ast::ast::{AttrKind, Attribute, DUMMY_NODE_ID, Expr}; use rustc_ast::ast::{AttrKind, Attribute, DUMMY_NODE_ID, Expr};
use rustc_ast::mut_visit::{self, MutVisitor}; use rustc_ast::mut_visit::{self, MutVisitor};
use rustc_ast::node_id::NodeId; use rustc_ast::node_id::NodeId;
use rustc_ast::token::{self, Token}; use rustc_ast::token;
use rustc_ast::tokenstream::{AttrTokenStream, AttrTokenTree, LazyAttrTokenStream}; use rustc_ast::tokenstream::{AttrTokenStream, AttrTokenTree, LazyAttrTokenStream};
use rustc_errors::Diag; use rustc_errors::Diag;
use rustc_parse::parser::Recovery; use rustc_parse::parser::Recovery;
@@ -23,6 +23,7 @@ pub fn parse_expr(psess: &ParseSess, source_code: &str) -> Option<Box<Expr>> {
psess, psess,
FileName::anon_source_code(source_code), FileName::anon_source_code(source_code),
source_code.to_owned(), source_code.to_owned(),
rustc_parse::lexer::StripTokens::Nothing,
)); ));
let mut parser = parser.recovery(Recovery::Forbidden); let mut parser = parser.recovery(Recovery::Forbidden);

View File

@@ -16,7 +16,7 @@ extern crate rustc_span;
#[allow(unused_extern_crates)] #[allow(unused_extern_crates)]
extern crate rustc_driver; extern crate rustc_driver;
use rustc_parse::{new_parser_from_file, unwrap_or_emit_fatal}; use rustc_parse::{lexer::StripTokens, new_parser_from_file, unwrap_or_emit_fatal};
use rustc_session::parse::ParseSess; use rustc_session::parse::ParseSess;
use std::path::Path; use std::path::Path;
@@ -34,6 +34,11 @@ fn parse() {
let path = Path::new(file!()); let path = Path::new(file!());
let path = path.canonicalize().unwrap(); let path = path.canonicalize().unwrap();
let mut parser = unwrap_or_emit_fatal(new_parser_from_file(&psess, &path, None)); let mut parser = unwrap_or_emit_fatal(new_parser_from_file(
&psess,
&path,
StripTokens::ShebangAndFrontmatter,
None,
));
let _ = parser.parse_crate_mod(); let _ = parser.parse_crate_mod();
} }

View File

@@ -0,0 +1,4 @@
---
-
---
1

View File

@@ -1,8 +1,20 @@
extern crate proc_macro; extern crate proc_macro;
use proc_macro::TokenStream; use proc_macro::{Literal, TokenStream};
#[proc_macro] #[proc_macro]
pub fn check(_: TokenStream) -> TokenStream { pub fn check(_: TokenStream) -> TokenStream {
// In the following test cases, the `---` may look like the start of frontmatter but it is not!
// That's because it would be backward incompatible to interpret them as such in the latest
// stable edition. That's not only the case due to the feature gate error but also due to the
// fact that we "eagerly" emit errors on malformed frontmatter.
// issue: <https://github.com/rust-lang/rust/issues/145520>
_ = "---".parse::<TokenStream>();
// Just a sequence of regular Rust punctuation tokens.
assert_eq!(6, "---\n---".parse::<TokenStream>().unwrap().into_iter().count()); assert_eq!(6, "---\n---".parse::<TokenStream>().unwrap().into_iter().count());
// issue: <https://github.com/rust-lang/rust/issues/146132>
assert!("---".parse::<Literal>().is_err());
Default::default() Default::default()
} }

View File

@@ -0,0 +1,9 @@
// Check that an expr-ctxt `include` doesn't try to parse frontmatter and instead
// treats it as a regular Rust token sequence.
//@ check-pass
#![expect(double_negations)]
fn main() {
// issue: <https://github.com/rust-lang/rust/issues/145945>
const _: () = assert!(-1 == include!("auxiliary/expr.rs"));
}

View File

@@ -0,0 +1,10 @@
// Ensure that in item ctxts we can `include` files that contain frontmatter.
//@ check-pass
#![feature(frontmatter)]
include!("auxiliary/lib.rs");
fn main() {
foo(1);
}

View File

@@ -1,12 +0,0 @@
#![feature(frontmatter)]
//@ check-pass
include!("auxiliary/lib.rs");
// auxiliary/lib.rs contains a frontmatter. Ensure that we can use them in an
// `include!` macro.
fn main() {
foo(1);
}

View File

@@ -2,10 +2,9 @@
//@ proc-macro: makro.rs //@ proc-macro: makro.rs
//@ edition: 2021 //@ edition: 2021
// Check that a proc-macro doesn't try to parse frontmatter and instead treats
// it as a regular Rust token sequence. See `auxiliary/makro.rs` for details.
makro::check!(); makro::check!();
// checks that a proc-macro doesn't know or parse frontmatters at all and instead treats
// it as normal Rust code.
// see auxiliary/makro.rs for how it is tested.
fn main() {} fn main() {}