1 use std::panic::{catch_unwind, AssertUnwindSafe};
2 use std::path::{Path, PathBuf};
5 use rustc_ast::token::{DelimToken, TokenKind};
6 use rustc_errors::Diagnostic;
7 use rustc_parse::{new_parser_from_file, parser::Parser as RawParser};
8 use rustc_span::{sym, symbol::kw, Span};
10 use crate::attr::first_attr_value_str_by_name;
11 use crate::syntux::session::ParseSess;
12 use crate::{Config, Input};
14 pub(crate) type DirectoryOwnership = rustc_expand::module::DirectoryOwnership;
15 pub(crate) type ModulePathSuccess = rustc_expand::module::ModulePathSuccess;
18 pub(crate) struct Directory {
19 pub(crate) path: PathBuf,
20 pub(crate) ownership: DirectoryOwnership,
23 /// A parser for Rust source code.
24 pub(crate) struct Parser<'a> {
25 parser: RawParser<'a>,
28 /// A builder for the `Parser`.
30 pub(crate) struct ParserBuilder<'a> {
31 config: Option<&'a Config>,
32 sess: Option<&'a ParseSess>,
34 directory_ownership: Option<DirectoryOwnership>,
37 impl<'a> ParserBuilder<'a> {
38 pub(crate) fn input(mut self, input: Input) -> ParserBuilder<'a> {
39 self.input = Some(input);
43 pub(crate) fn sess(mut self, sess: &'a ParseSess) -> ParserBuilder<'a> {
44 self.sess = Some(sess);
48 pub(crate) fn config(mut self, config: &'a Config) -> ParserBuilder<'a> {
49 self.config = Some(config);
53 pub(crate) fn directory_ownership(
55 directory_ownership: Option<DirectoryOwnership>,
56 ) -> ParserBuilder<'a> {
57 self.directory_ownership = directory_ownership;
61 pub(crate) fn build(self) -> Result<Parser<'a>, ParserError> {
62 let sess = self.sess.ok_or(ParserError::NoParseSess)?;
63 let input = self.input.ok_or(ParserError::NoInput)?;
65 let parser = match Self::parser(sess.inner(), input) {
68 if let Some(diagnostics) = db {
69 sess.emit_diagnostics(diagnostics);
70 return Err(ParserError::ParserCreationError);
72 return Err(ParserError::ParsePanicError);
80 sess: &'a rustc_session::parse::ParseSess,
82 ) -> Result<rustc_parse::parser::Parser<'a>, Option<Vec<Diagnostic>>> {
84 Input::File(ref file) => catch_unwind(AssertUnwindSafe(move || {
85 new_parser_from_file(sess, file, None)
88 Input::Text(text) => rustc_parse::maybe_new_parser_from_source_str(
90 rustc_span::FileName::Custom("stdin".to_owned()),
93 .map_err(|db| Some(db)),
98 #[derive(Debug, PartialEq)]
99 pub(crate) enum ParserError {
107 impl<'a> Parser<'a> {
108 pub(crate) fn submod_path_from_attr(attrs: &[ast::Attribute], path: &Path) -> Option<PathBuf> {
109 let path_string = first_attr_value_str_by_name(attrs, sym::path)?.as_str();
110 // On windows, the base path might have the form
111 // `\\?\foo\bar` in which case it does not tolerate
112 // mixed `/` and `\` separators, so canonicalize
115 let path_string = path_string.replace("/", "\\");
117 Some(path.join(&*path_string))
120 pub(crate) fn parse_file_as_module(
124 ) -> Result<(ast::Mod, Vec<ast::Attribute>), ParserError> {
125 let result = catch_unwind(AssertUnwindSafe(|| {
126 let mut parser = new_parser_from_file(sess.inner(), &path, Some(span));
127 match parser.parse_mod(&TokenKind::Eof, ast::Unsafe::No) {
128 Ok(result) => Some(result),
130 sess.emit_or_cancel_diagnostic(&mut e);
131 if sess.can_reset_errors() {
140 if !sess.has_errors() {
144 if sess.can_reset_errors() {
148 Err(ParserError::ParseError)
150 Ok(None) => Err(ParserError::ParseError),
151 Err(..) if path.exists() => Err(ParserError::ParseError),
152 Err(_) => Err(ParserError::ParsePanicError),
156 pub(crate) fn parse_crate(
159 directory_ownership: Option<DirectoryOwnership>,
161 ) -> Result<ast::Crate, ParserError> {
162 let krate = Parser::parse_crate_inner(config, input, directory_ownership, sess)?;
163 if !sess.has_errors() {
167 if sess.can_reset_errors() {
172 Err(ParserError::ParseError)
175 fn parse_crate_inner(
178 directory_ownership: Option<DirectoryOwnership>,
180 ) -> Result<ast::Crate, ParserError> {
181 let mut parser = ParserBuilder::default()
184 .directory_ownership(directory_ownership)
187 parser.parse_crate_mod()
190 fn parse_crate_mod(&mut self) -> Result<ast::Crate, ParserError> {
191 let mut parser = AssertUnwindSafe(&mut self.parser);
193 match catch_unwind(move || parser.parse_crate_mod()) {
197 Err(ParserError::ParseError)
199 Err(_) => Err(ParserError::ParsePanicError),
203 pub(crate) fn parse_cfg_if(
205 mac: &'a ast::MacCall,
206 ) -> Result<Vec<ast::Item>, &'static str> {
207 match catch_unwind(AssertUnwindSafe(|| Parser::parse_cfg_if_inner(sess, mac))) {
208 Ok(Ok(items)) => Ok(items),
209 Ok(err @ Err(_)) => err,
210 Err(..) => Err("failed to parse cfg_if!"),
214 fn parse_cfg_if_inner(
216 mac: &'a ast::MacCall,
217 ) -> Result<Vec<ast::Item>, &'static str> {
218 let token_stream = mac.args.inner_tokens();
220 rustc_parse::stream_to_parser(sess.inner(), token_stream.clone(), Some(""));
222 let mut items = vec![];
223 let mut process_if_cfg = true;
225 while parser.token.kind != TokenKind::Eof {
227 if !parser.eat_keyword(kw::If) {
228 return Err("Expected `if`");
231 .parse_attribute(false)
232 .map_err(|_| "Failed to parse attributes")?;
235 if !parser.eat(&TokenKind::OpenDelim(DelimToken::Brace)) {
236 return Err("Expected an opening brace");
239 while parser.token != TokenKind::CloseDelim(DelimToken::Brace)
240 && parser.token.kind != TokenKind::Eof
242 let item = match parser.parse_item() {
243 Ok(Some(item_ptr)) => item_ptr.into_inner(),
244 Ok(None) => continue,
247 parser.sess.span_diagnostic.reset_err_count();
249 "Expected item inside cfg_if block, but failed to parse it as an item",
253 if let ast::ItemKind::Mod(..) = item.kind {
258 if !parser.eat(&TokenKind::CloseDelim(DelimToken::Brace)) {
259 return Err("Expected a closing brace");
262 if parser.eat(&TokenKind::Eof) {
266 if !parser.eat_keyword(kw::Else) {
267 return Err("Expected `else`");
270 process_if_cfg = parser.token.is_keyword(kw::If);