This works for most of its call sites. This is nice, because `emit` very
much makes sense as a consuming operation -- indeed,
`DiagnosticBuilderState` exists to ensure no diagnostic is emitted
twice, but it uses runtime checks.
For the small number of call sites where a consuming emit doesn't work,
the commit adds `DiagnosticBuilder::emit_without_consuming`. (This will
be removed in subsequent commits.)
Likewise, `emit_unless` becomes consuming. And `delay_as_bug` becomes
consuming, while `delay_as_bug_without_consuming` is added (which will
also be removed in subsequent commits.)
All this requires significant changes to `DiagnosticBuilder`'s chaining
methods. Currently `DiagnosticBuilder` method chaining uses a
non-consuming `&mut self -> &mut Self` style, which allows chaining to
be used when the chain ends in `emit()`, like so:
```
struct_err(msg).span(span).emit();
```
But it doesn't work when producing a `DiagnosticBuilder` value,
requiring this:
```
let mut err = self.struct_err(msg);
err.span(span);
err
```
This style of chaining won't work with consuming `emit` though. For
that, we need to use to a `self -> Self` style. That also would allow
`DiagnosticBuilder` production to be chained, e.g.:
```
self.struct_err(msg).span(span)
```
However, removing the `&mut self -> &mut Self` style would require that
individual modifications of a `DiagnosticBuilder` go from this:
```
err.span(span);
```
to this:
```
err = err.span(span);
```
There are *many* such places. I have a high tolerance for tedious
refactorings, but even I gave up after a long time trying to convert
them all.
Instead, this commit has it both ways: the existing `&mut self -> Self`
chaining methods are kept, and new `self -> Self` chaining methods are
added, all of which have a `_mv` suffix (short for "move"). Changes to
the existing `forward!` macro lets this happen with very little
additional boilerplate code. I chose to add the suffix to the new
chaining methods rather than the existing ones, because the number of
changes required is much smaller that way.
This doubled chainging is a bit clumsy, but I think it is worthwhile
because it allows a *lot* of good things to subsequently happen. In this
commit, there are many `mut` qualifiers removed in places where
diagnostics are emitted without being modified. In subsequent commits:
- chaining can be used more, making the code more concise;
- more use of chaining also permits the removal of redundant diagnostic
APIs like `struct_err_with_code`, which can be replaced easily with
`struct_err` + `code_mv`;
- `emit_without_diagnostic` can be removed, which simplifies a lot of
machinery, removing the need for `DiagnosticBuilderState`.
175 lines
5.2 KiB
Rust
175 lines
5.2 KiB
Rust
use std::panic::{catch_unwind, AssertUnwindSafe};
|
|
use std::path::{Path, PathBuf};
|
|
|
|
use rustc_ast::token::TokenKind;
|
|
use rustc_ast::{ast, attr, ptr};
|
|
use rustc_errors::Diagnostic;
|
|
use rustc_parse::{new_parser_from_file, parser::Parser as RawParser};
|
|
use rustc_span::{sym, Span};
|
|
use thin_vec::ThinVec;
|
|
|
|
use crate::parse::session::ParseSess;
|
|
use crate::Input;
|
|
|
|
pub(crate) type DirectoryOwnership = rustc_expand::module::DirOwnership;
|
|
pub(crate) type ModulePathSuccess = rustc_expand::module::ModulePathSuccess;
|
|
pub(crate) type ModError<'a> = rustc_expand::module::ModError<'a>;
|
|
|
|
#[derive(Clone)]
|
|
pub(crate) struct Directory {
|
|
pub(crate) path: PathBuf,
|
|
pub(crate) ownership: DirectoryOwnership,
|
|
}
|
|
|
|
/// A parser for Rust source code.
|
|
pub(crate) struct Parser<'a> {
|
|
parser: RawParser<'a>,
|
|
}
|
|
|
|
/// A builder for the `Parser`.
|
|
#[derive(Default)]
|
|
pub(crate) struct ParserBuilder<'a> {
|
|
sess: Option<&'a ParseSess>,
|
|
input: Option<Input>,
|
|
}
|
|
|
|
impl<'a> ParserBuilder<'a> {
|
|
pub(crate) fn input(mut self, input: Input) -> ParserBuilder<'a> {
|
|
self.input = Some(input);
|
|
self
|
|
}
|
|
|
|
pub(crate) fn sess(mut self, sess: &'a ParseSess) -> ParserBuilder<'a> {
|
|
self.sess = Some(sess);
|
|
self
|
|
}
|
|
|
|
pub(crate) fn build(self) -> Result<Parser<'a>, ParserError> {
|
|
let sess = self.sess.ok_or(ParserError::NoParseSess)?;
|
|
let input = self.input.ok_or(ParserError::NoInput)?;
|
|
|
|
let parser = match Self::parser(sess.inner(), input) {
|
|
Ok(p) => p,
|
|
Err(db) => {
|
|
if let Some(diagnostics) = db {
|
|
sess.emit_diagnostics(diagnostics);
|
|
return Err(ParserError::ParserCreationError);
|
|
}
|
|
return Err(ParserError::ParsePanicError);
|
|
}
|
|
};
|
|
|
|
Ok(Parser { parser })
|
|
}
|
|
|
|
fn parser(
|
|
sess: &'a rustc_session::parse::ParseSess,
|
|
input: Input,
|
|
) -> Result<rustc_parse::parser::Parser<'a>, Option<Vec<Diagnostic>>> {
|
|
match input {
|
|
Input::File(ref file) => catch_unwind(AssertUnwindSafe(move || {
|
|
new_parser_from_file(sess, file, None)
|
|
}))
|
|
.map_err(|_| None),
|
|
Input::Text(text) => rustc_parse::maybe_new_parser_from_source_str(
|
|
sess,
|
|
rustc_span::FileName::Custom("stdin".to_owned()),
|
|
text,
|
|
)
|
|
.map_err(Some),
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Debug, PartialEq)]
|
|
pub(crate) enum ParserError {
|
|
NoParseSess,
|
|
NoInput,
|
|
ParserCreationError,
|
|
ParseError,
|
|
ParsePanicError,
|
|
}
|
|
|
|
impl<'a> Parser<'a> {
|
|
pub(crate) fn submod_path_from_attr(attrs: &[ast::Attribute], path: &Path) -> Option<PathBuf> {
|
|
let path_sym = attr::first_attr_value_str_by_name(attrs, sym::path)?;
|
|
let path_str = path_sym.as_str();
|
|
|
|
// On windows, the base path might have the form
|
|
// `\\?\foo\bar` in which case it does not tolerate
|
|
// mixed `/` and `\` separators, so canonicalize
|
|
// `/` to `\`.
|
|
#[cfg(windows)]
|
|
let path_str = path_str.replace("/", "\\");
|
|
|
|
Some(path.join(path_str))
|
|
}
|
|
|
|
pub(crate) fn parse_file_as_module(
|
|
sess: &'a ParseSess,
|
|
path: &Path,
|
|
span: Span,
|
|
) -> Result<(ast::AttrVec, ThinVec<ptr::P<ast::Item>>, Span), ParserError> {
|
|
let result = catch_unwind(AssertUnwindSafe(|| {
|
|
let mut parser = new_parser_from_file(sess.inner(), path, Some(span));
|
|
match parser.parse_mod(&TokenKind::Eof) {
|
|
Ok((a, i, spans)) => Some((a, i, spans.inner_span)),
|
|
Err(e) => {
|
|
e.emit();
|
|
if sess.can_reset_errors() {
|
|
sess.reset_errors();
|
|
}
|
|
None
|
|
}
|
|
}
|
|
}));
|
|
match result {
|
|
Ok(Some(m)) if !sess.has_errors() => Ok(m),
|
|
Ok(Some(m)) if sess.can_reset_errors() => {
|
|
sess.reset_errors();
|
|
Ok(m)
|
|
}
|
|
Ok(_) => Err(ParserError::ParseError),
|
|
Err(..) if path.exists() => Err(ParserError::ParseError),
|
|
Err(_) => Err(ParserError::ParsePanicError),
|
|
}
|
|
}
|
|
|
|
pub(crate) fn parse_crate(
|
|
input: Input,
|
|
sess: &'a ParseSess,
|
|
) -> Result<ast::Crate, ParserError> {
|
|
let krate = Parser::parse_crate_inner(input, sess)?;
|
|
if !sess.has_errors() {
|
|
return Ok(krate);
|
|
}
|
|
|
|
if sess.can_reset_errors() {
|
|
sess.reset_errors();
|
|
return Ok(krate);
|
|
}
|
|
|
|
Err(ParserError::ParseError)
|
|
}
|
|
|
|
fn parse_crate_inner(input: Input, sess: &'a ParseSess) -> Result<ast::Crate, ParserError> {
|
|
ParserBuilder::default()
|
|
.input(input)
|
|
.sess(sess)
|
|
.build()?
|
|
.parse_crate_mod()
|
|
}
|
|
|
|
fn parse_crate_mod(&mut self) -> Result<ast::Crate, ParserError> {
|
|
let mut parser = AssertUnwindSafe(&mut self.parser);
|
|
|
|
match catch_unwind(move || parser.parse_crate_mod()) {
|
|
Ok(Ok(k)) => Ok(k),
|
|
Ok(Err(db)) => {
|
|
db.emit();
|
|
Err(ParserError::ParseError)
|
|
}
|
|
Err(_) => Err(ParserError::ParsePanicError),
|
|
}
|
|
}
|
|
}
|