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::Diag;
use rustc_parse::parser::Parser as RawParser;
use rustc_parse::{new_parser_from_file, new_parser_from_source_str, unwrap_or_emit_fatal};
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,
}
pub(crate) struct Parser<'a> {
parser: RawParser<'a>,
}
#[derive(Default)]
pub(crate) struct ParserBuilder<'a> {
psess: 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 psess(mut self, psess: &'a ParseSess) -> ParserBuilder<'a> {
self.psess = Some(psess);
self
}
pub(crate) fn build(self) -> Result<Parser<'a>, ParserError> {
let psess = self.psess.ok_or(ParserError::NoParseSess)?;
let input = self.input.ok_or(ParserError::NoInput)?;
let parser = match Self::parser(psess.inner(), input) {
Ok(p) => p,
Err(diagnostics) => {
psess.emit_diagnostics(diagnostics);
return Err(ParserError::ParserCreationError);
}
};
Ok(Parser { parser })
}
fn parser(
psess: &'a rustc_session::parse::ParseSess,
input: Input,
) -> Result<RawParser<'a>, Vec<Diag<'a>>> {
match input {
Input::File(ref file) => new_parser_from_file(psess, file, None),
Input::Text(text) => new_parser_from_source_str(
psess,
rustc_span::FileName::Custom("stdin".to_owned()),
text,
),
}
}
}
#[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();
#[cfg(windows)]
let path_str = path_str.replace("/", "\\");
Some(path.join(path_str))
}
pub(crate) fn parse_file_as_module(
psess: &'a ParseSess,
path: &Path,
span: Span,
) -> Result<(ast::AttrVec, ThinVec<ptr::P<ast::Item>>, Span), ParserError> {
let result = catch_unwind(AssertUnwindSafe(|| {
let mut parser =
unwrap_or_emit_fatal(new_parser_from_file(psess.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 psess.can_reset_errors() {
psess.reset_errors();
}
None
}
}
}));
match result {
Ok(Some(m)) if !psess.has_errors() => Ok(m),
Ok(Some(m)) if psess.can_reset_errors() => {
psess.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,
psess: &'a ParseSess,
) -> Result<ast::Crate, ParserError> {
let krate = Parser::parse_crate_inner(input, psess)?;
if !psess.has_errors() {
return Ok(krate);
}
if psess.can_reset_errors() {
psess.reset_errors();
return Ok(krate);
}
Err(ParserError::ParseError)
}
fn parse_crate_inner(input: Input, psess: &'a ParseSess) -> Result<ast::Crate, ParserError> {
ParserBuilder::default()
.input(input)
.psess(psess)
.build()?
.parse_crate_mod()
}
fn parse_crate_mod(&mut self) -> Result<ast::Crate, ParserError> {
let mut parser = AssertUnwindSafe(&mut self.parser);
let err = Err(ParserError::ParsePanicError);
match catch_unwind(move || parser.parse_crate_mod()) {
Ok(Ok(k)) => Ok(k),
Ok(Err(db)) => {
db.emit();
err
}
Err(_) => err,
}
}
}