Skip to content

Commit

Permalink
Make top-level rustc_parse functions fallible.
Browse files Browse the repository at this point in the history
Currently we have an awkward mix of fallible and infallible functions:
```
       new_parser_from_source_str
 maybe_new_parser_from_source_str
       new_parser_from_file
(maybe_new_parser_from_file)        // missing
      (new_parser_from_source_file) // missing
 maybe_new_parser_from_source_file
       source_str_to_stream
 maybe_source_file_to_stream
```
We could add the two missing functions, but instead this commit removes
of all the infallible ones and renames the fallible ones leaving us with
these which are all fallible:
```
new_parser_from_source_str
new_parser_from_file
new_parser_from_source_file
source_str_to_stream
source_file_to_stream
```
This requires making `unwrap_or_emit_fatal` public so callers of
formerly infallible functions can still work.

This does make some of the call sites slightly more verbose, but I think
it's worth it for the simpler API. Also, there are two `catch_unwind`
calls and one `catch_fatal_errors` call in this diff that become
removable thanks this change. (I will do that in a follow-up PR.)
  • Loading branch information
nnethercote committed May 31, 2024
1 parent 3b61803 commit b7a73c6
Show file tree
Hide file tree
Showing 16 changed files with 105 additions and 75 deletions.
5 changes: 3 additions & 2 deletions compiler/rustc_builtin_macros/src/cmdline_attrs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,17 @@ use crate::errors;
use rustc_ast::attr::mk_attr;
use rustc_ast::token;
use rustc_ast::{self as ast, AttrItem, AttrStyle};
use rustc_parse::{new_parser_from_source_str, unwrap_or_emit_fatal};
use rustc_session::parse::ParseSess;
use rustc_span::FileName;

pub fn inject(krate: &mut ast::Crate, psess: &ParseSess, attrs: &[String]) {
for raw_attr in attrs {
let mut parser = rustc_parse::new_parser_from_source_str(
let mut parser = unwrap_or_emit_fatal(new_parser_from_source_str(
psess,
FileName::cli_crate_attr_source_code(raw_attr),
raw_attr.clone(),
);
));

let start_span = parser.token.span;
let AttrItem { path, args, tokens: _ } = match parser.parse_attr_item(false) {
Expand Down
4 changes: 2 additions & 2 deletions compiler/rustc_builtin_macros/src/source_util.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@ use rustc_expand::base::{
};
use rustc_expand::module::DirOwnership;
use rustc_lint_defs::BuiltinLintDiag;
use rustc_parse::new_parser_from_file;
use rustc_parse::parser::{ForceCollect, Parser};
use rustc_parse::{new_parser_from_file, unwrap_or_emit_fatal};
use rustc_session::lint::builtin::INCOMPLETE_INCLUDE;
use rustc_span::source_map::SourceMap;
use rustc_span::symbol::Symbol;
Expand Down Expand Up @@ -126,7 +126,7 @@ pub(crate) fn expand_include<'cx>(
return ExpandResult::Ready(DummyResult::any(sp, guar));
}
};
let p = new_parser_from_file(cx.psess(), &file, Some(sp));
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;`,
// then the path of `bar.rs` should be relative to the directory of `file`.
Expand Down
6 changes: 3 additions & 3 deletions compiler/rustc_driver_impl/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ use rustc_interface::{interface, Queries};
use rustc_lint::unerased_lint_store;
use rustc_metadata::creader::MetadataLoader;
use rustc_metadata::locator;
use rustc_parse::{new_parser_from_file, new_parser_from_source_str};
use rustc_parse::{new_parser_from_file, new_parser_from_source_str, unwrap_or_emit_fatal};
use rustc_session::config::{nightly_options, CG_OPTIONS, Z_OPTIONS};
use rustc_session::config::{ErrorOutputType, Input, OutFileName, OutputType};
use rustc_session::getopts::{self, Matches};
Expand Down Expand Up @@ -1261,12 +1261,12 @@ pub fn handle_options(early_dcx: &EarlyDiagCtxt, args: &[String]) -> Option<geto
}

fn parse_crate_attrs<'a>(sess: &'a Session) -> PResult<'a, ast::AttrVec> {
let mut parser = 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::Str { name, input } => {
new_parser_from_source_str(&sess.psess, name.clone(), input.clone())
}
};
});
parser.parse_inner_attributes()
}

Expand Down
5 changes: 3 additions & 2 deletions compiler/rustc_expand/src/module.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ use crate::errors::{
use rustc_ast::ptr::P;
use rustc_ast::{token, AttrVec, Attribute, Inline, Item, ModSpans};
use rustc_errors::{Diag, ErrorGuaranteed};
use rustc_parse::new_parser_from_file;
use rustc_parse::validate_attr;
use rustc_parse::{new_parser_from_file, unwrap_or_emit_fatal};
use rustc_session::parse::ParseSess;
use rustc_session::Session;
use rustc_span::symbol::{sym, Ident};
Expand Down Expand Up @@ -66,7 +66,8 @@ pub(crate) fn parse_external_mod(
}

// Actually parse the external file as a module.
let mut parser = new_parser_from_file(&sess.psess, &mp.file_path, Some(span));
let mut parser =
unwrap_or_emit_fatal(new_parser_from_file(&sess.psess, &mp.file_path, Some(span)));
let (inner_attrs, items, inner_span) =
parser.parse_mod(&token::Eof).map_err(|err| ModError::ParserError(err))?;
attrs.extend(inner_attrs);
Expand Down
9 changes: 5 additions & 4 deletions compiler/rustc_expand/src/proc_macro_server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ use rustc_data_structures::sync::Lrc;
use rustc_errors::{Diag, ErrorGuaranteed, MultiSpan, PResult};
use rustc_parse::lexer::nfc_normalize;
use rustc_parse::parser::Parser;
use rustc_parse::source_str_to_stream;
use rustc_parse::{new_parser_from_source_str, source_str_to_stream, unwrap_or_emit_fatal};
use rustc_session::parse::ParseSess;
use rustc_span::def_id::CrateNum;
use rustc_span::symbol::{self, sym, Symbol};
Expand Down Expand Up @@ -468,7 +468,8 @@ impl server::FreeFunctions for Rustc<'_, '_> {

fn literal_from_str(&mut self, s: &str) -> Result<Literal<Self::Span, Self::Symbol>, ()> {
let name = FileName::proc_macro_source_code(s);
let mut parser = rustc_parse::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()));

let first_span = parser.token.span.data();
let minus_present = parser.eat(&token::BinOp(token::Minus));
Expand Down Expand Up @@ -540,12 +541,12 @@ impl server::TokenStream for Rustc<'_, '_> {
}

fn from_str(&mut self, src: &str) -> Self::TokenStream {
source_str_to_stream(
unwrap_or_emit_fatal(source_str_to_stream(
self.psess(),
FileName::proc_macro_source_code(src),
src.to_string(),
Some(self.call_site),
)
))
}

fn to_string(&mut self, stream: &Self::TokenStream) -> String {
Expand Down
6 changes: 3 additions & 3 deletions compiler/rustc_interface/src/interface.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ use rustc_lint::LintStore;
use rustc_middle::ty;
use rustc_middle::ty::CurrentGcx;
use rustc_middle::util::Providers;
use rustc_parse::maybe_new_parser_from_source_str;
use rustc_parse::new_parser_from_source_str;
use rustc_query_impl::QueryCtxt;
use rustc_query_system::query::print_query_stack;
use rustc_session::config::{self, Cfg, CheckCfg, ExpectedValues, Input, OutFileName};
Expand Down Expand Up @@ -67,7 +67,7 @@ pub(crate) fn parse_cfg(dcx: &DiagCtxt, cfgs: Vec<String>) -> Cfg {
};
}

match maybe_new_parser_from_source_str(&psess, filename, s.to_string()) {
match new_parser_from_source_str(&psess, filename, s.to_string()) {
Ok(mut parser) => match parser.parse_meta_item() {
Ok(meta_item) if parser.token == token::Eof => {
if meta_item.path.segments.len() != 1 {
Expand Down Expand Up @@ -166,7 +166,7 @@ pub(crate) fn parse_check_cfg(dcx: &DiagCtxt, specs: Vec<String>) -> CheckCfg {
error!("expected `cfg(name, values(\"value1\", \"value2\", ... \"valueN\"))`")
};

let mut parser = match maybe_new_parser_from_source_str(&psess, filename, s.to_string()) {
let mut parser = match new_parser_from_source_str(&psess, filename, s.to_string()) {
Ok(parser) => parser,
Err(errs) => {
errs.into_iter().for_each(|err| err.cancel());
Expand Down
8 changes: 5 additions & 3 deletions compiler/rustc_interface/src/passes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,9 @@ use rustc_middle::arena::Arena;
use rustc_middle::dep_graph::DepGraph;
use rustc_middle::ty::{self, GlobalCtxt, RegisteredTools, TyCtxt};
use rustc_middle::util::Providers;
use rustc_parse::{new_parser_from_file, new_parser_from_source_str, validate_attr};
use rustc_parse::{
new_parser_from_file, new_parser_from_source_str, unwrap_or_emit_fatal, validate_attr,
};
use rustc_passes::{abi_test, hir_stats, layout_test};
use rustc_resolve::Resolver;
use rustc_session::code_stats::VTableSizeInfo;
Expand All @@ -43,12 +45,12 @@ use tracing::{info, instrument};

pub fn parse<'a>(sess: &'a Session) -> PResult<'a, ast::Crate> {
let krate = sess.time("parse_crate", || {
let mut parser = 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::Str { input, name } => {
new_parser_from_source_str(&sess.psess, name.clone(), input.clone())
}
};
});
parser.parse_crate_mod()
})?;

Expand Down
54 changes: 30 additions & 24 deletions compiler/rustc_parse/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ mod errors;
rustc_fluent_macro::fluent_messages! { "../messages.ftl" }

// Unwrap the result if `Ok`, otherwise emit the diagnostics and abort.
fn unwrap_or_emit_fatal<T>(expr: Result<T, Vec<Diag<'_>>>) -> T {
pub fn unwrap_or_emit_fatal<T>(expr: Result<T, Vec<Diag<'_>>>) -> T {
match expr {
Ok(expr) => expr,
Err(errs) => {
Expand All @@ -47,25 +47,28 @@ fn unwrap_or_emit_fatal<T>(expr: Result<T, Vec<Diag<'_>>>) -> T {
}
}

/// Creates a new parser from a source string.
pub fn new_parser_from_source_str(psess: &ParseSess, name: FileName, source: String) -> Parser<'_> {
unwrap_or_emit_fatal(maybe_new_parser_from_source_str(psess, name, source))
}

/// Creates a new parser from a source string. Returns any buffered errors from lexing the initial
/// token stream; these must be consumed via `emit`, `cancel`, etc., otherwise a panic will occur
/// when they are dropped.
pub fn maybe_new_parser_from_source_str(
/// Creates a new parser from a 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_source_str(
psess: &ParseSess,
name: FileName,
source: String,
) -> Result<Parser<'_>, Vec<Diag<'_>>> {
maybe_new_parser_from_source_file(psess, 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)
}

/// Creates a new parser, aborting if the file doesn't exist. If a span is given, that is used on
/// an error as the source of the problem.
pub fn new_parser_from_file<'a>(psess: &'a ParseSess, path: &Path, sp: Option<Span>) -> Parser<'a> {
/// Creates a new parser from a filename. On failure, the errors must be consumed via
/// `unwrap_or_emit_fatal`, `emit`, `cancel`, etc., otherwise a panic will occur when they are
/// dropped.
///
/// If a span is given, that is used on an error as the source of the problem.
pub fn new_parser_from_file<'a>(
psess: &'a ParseSess,
path: &Path,
sp: Option<Span>,
) -> Result<Parser<'a>, Vec<Diag<'a>>> {
let source_file = psess.source_map().load_file(path).unwrap_or_else(|e| {
let msg = format!("couldn't read {}: {}", path.display(), e);
let mut err = psess.dcx.struct_fatal(msg);
Expand All @@ -74,23 +77,21 @@ pub fn new_parser_from_file<'a>(psess: &'a ParseSess, path: &Path, sp: Option<Sp
}
err.emit();
});

unwrap_or_emit_fatal(maybe_new_parser_from_source_file(psess, source_file))
new_parser_from_source_file(psess, source_file)
}

/// Given a session and a `source_file`, return a parser. Returns any buffered errors from lexing
/// the initial token stream.
fn maybe_new_parser_from_source_file(
fn new_parser_from_source_file(
psess: &ParseSess,
source_file: Lrc<SourceFile>,
) -> Result<Parser<'_>, Vec<Diag<'_>>> {
let end_pos = source_file.end_position();
let stream = maybe_source_file_to_stream(psess, source_file, None)?;
let stream = source_file_to_stream(psess, source_file, None)?;
let mut parser = Parser::new(psess, stream, None);
if parser.token == token::Eof {
parser.token.span = Span::new(end_pos, end_pos, parser.token.span.ctxt(), None);
}

Ok(parser)
}

Expand All @@ -99,14 +100,14 @@ pub fn source_str_to_stream(
name: FileName,
source: String,
override_span: Option<Span>,
) -> TokenStream {
) -> Result<TokenStream, Vec<Diag<'_>>> {
let source_file = psess.source_map().new_source_file(name, source);
unwrap_or_emit_fatal(maybe_source_file_to_stream(psess, source_file, override_span))
source_file_to_stream(psess, source_file, override_span)
}

/// Given a source file, produces a sequence of token trees. Returns any buffered errors from
/// parsing the token stream.
fn maybe_source_file_to_stream<'psess>(
fn source_file_to_stream<'psess>(
psess: &'psess ParseSess,
source_file: Lrc<SourceFile>,
override_span: Option<Span>,
Expand Down Expand Up @@ -139,13 +140,18 @@ pub fn parse_in<'a, T>(
pub fn fake_token_stream_for_item(psess: &ParseSess, item: &ast::Item) -> TokenStream {
let source = pprust::item_to_string(item);
let filename = FileName::macro_expansion_source_code(&source);
source_str_to_stream(psess, filename, source, Some(item.span))
unwrap_or_emit_fatal(source_str_to_stream(psess, filename, source, Some(item.span)))
}

pub fn fake_token_stream_for_crate(psess: &ParseSess, krate: &ast::Crate) -> TokenStream {
let source = pprust::crate_to_string_for_macros(krate);
let filename = FileName::macro_expansion_source_code(&source);
source_str_to_stream(psess, filename, source, Some(krate.spans.inner_span))
unwrap_or_emit_fatal(source_str_to_stream(
psess,
filename,
source,
Some(krate.spans.inner_span),
))
}

pub fn parse_cfg_attr(
Expand Down
22 changes: 17 additions & 5 deletions compiler/rustc_parse/src/parser/tests.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
use crate::parser::ForceCollect;
use crate::{new_parser_from_source_str, parser::Parser, source_str_to_stream};
use crate::{
new_parser_from_source_str, parser::Parser, source_str_to_stream, unwrap_or_emit_fatal,
};
use ast::token::IdentIsRaw;
use rustc_ast::ptr::P;
use rustc_ast::token::{self, Delimiter, Token};
Expand Down Expand Up @@ -29,7 +31,11 @@ fn psess() -> ParseSess {

/// Map string to parser (via tts).
fn string_to_parser(psess: &ParseSess, source_str: String) -> Parser<'_> {
new_parser_from_source_str(psess, PathBuf::from("bogofile").into(), source_str)
unwrap_or_emit_fatal(new_parser_from_source_str(
psess,
PathBuf::from("bogofile").into(),
source_str,
))
}

fn create_test_handler() -> (DiagCtxt, Lrc<SourceMap>, Arc<Mutex<Vec<u8>>>) {
Expand Down Expand Up @@ -82,7 +88,12 @@ where
/// Maps a string to tts, using a made-up filename.
pub(crate) fn string_to_stream(source_str: String) -> TokenStream {
let psess = psess();
source_str_to_stream(&psess, PathBuf::from("bogofile").into(), source_str, None)
unwrap_or_emit_fatal(source_str_to_stream(
&psess,
PathBuf::from("bogofile").into(),
source_str,
None,
))
}

/// Parses a string, returns a crate.
Expand Down Expand Up @@ -1064,7 +1075,8 @@ fn parse_item_from_source_str(
source: String,
psess: &ParseSess,
) -> PResult<'_, Option<P<ast::Item>>> {
new_parser_from_source_str(psess, name, source).parse_item(ForceCollect::No)
unwrap_or_emit_fatal(new_parser_from_source_str(psess, name, source))
.parse_item(ForceCollect::No)
}

// Produces a `rustc_span::span`.
Expand Down Expand Up @@ -1345,7 +1357,7 @@ fn ttdelim_span() {
source: String,
psess: &ParseSess,
) -> PResult<'_, P<ast::Expr>> {
new_parser_from_source_str(psess, name, source).parse_expr()
unwrap_or_emit_fatal(new_parser_from_source_str(psess, name, source)).parse_expr()
}

create_default_session_globals_then(|| {
Expand Down
2 changes: 1 addition & 1 deletion src/librustdoc/clean/render_macro_matchers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ fn snippet_equal_to_token(tcx: TyCtxt<'_>, matcher: &TokenTree) -> Option<String
let psess = ParseSess::new(rustc_driver::DEFAULT_LOCALE_RESOURCES.to_vec());
let file_name = source_map.span_to_filename(span);
let mut parser =
match rustc_parse::maybe_new_parser_from_source_str(&psess, file_name, snippet.clone()) {
match rustc_parse::new_parser_from_source_str(&psess, file_name, snippet.clone()) {
Ok(parser) => parser,
Err(errs) => {
errs.into_iter().for_each(|err| err.cancel());
Expand Down
23 changes: 11 additions & 12 deletions src/librustdoc/doctest.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ use rustc_interface::interface;
use rustc_middle::hir::map::Map;
use rustc_middle::hir::nested_filter;
use rustc_middle::ty::TyCtxt;
use rustc_parse::maybe_new_parser_from_source_str;
use rustc_parse::new_parser_from_source_str;
use rustc_parse::parser::attr::InnerAttrPolicy;
use rustc_resolve::rustdoc::span_of_fragments;
use rustc_session::config::{self, CrateType, ErrorOutputType};
Expand Down Expand Up @@ -638,7 +638,7 @@ pub(crate) fn make_test(
let mut found_extern_crate = crate_name.is_none();
let mut found_macro = false;

let mut parser = match maybe_new_parser_from_source_str(&psess, filename, source) {
let mut parser = match new_parser_from_source_str(&psess, filename, source) {
Ok(p) => p,
Err(errs) => {
errs.into_iter().for_each(|err| err.cancel());
Expand Down Expand Up @@ -818,16 +818,15 @@ fn check_if_attr_is_complete(source: &str, edition: Edition) -> bool {

let dcx = DiagCtxt::new(Box::new(emitter)).disable_warnings();
let psess = ParseSess::with_dcx(dcx, sm);
let mut parser =
match maybe_new_parser_from_source_str(&psess, filename, source.to_owned()) {
Ok(p) => p,
Err(errs) => {
errs.into_iter().for_each(|err| err.cancel());
// If there is an unclosed delimiter, an error will be returned by the
// tokentrees.
return false;
}
};
let mut parser = match new_parser_from_source_str(&psess, filename, source.to_owned()) {
Ok(p) => p,
Err(errs) => {
errs.into_iter().for_each(|err| err.cancel());
// If there is an unclosed delimiter, an error will be returned by the
// tokentrees.
return false;
}
};
// If a parsing error happened, it's very likely that the attribute is incomplete.
if let Err(e) = parser.parse_attribute(InnerAttrPolicy::Permitted) {
e.cancel();
Expand Down
Loading

0 comments on commit b7a73c6

Please sign in to comment.