use std::panic::{AssertUnwindSafe, catch_unwind}; 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::{Span, sym}; use thin_vec::ThinVec; use crate::Input; use crate::parse::session::ParseSess; 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> { psess: Option<&'a ParseSess>, input: Option, } 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, 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, Vec>> { 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 { 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( psess: &'a ParseSess, path: &Path, span: Span, ) -> Result<(ast::AttrVec, ThinVec>, 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 { 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 { ParserBuilder::default() .input(input) .psess(psess) .build()? .parse_crate_mod() } fn parse_crate_mod(&mut self) -> Result { 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, } } }