]> git.lizzy.rs Git - rust.git/blob - src/syntux/parser.rs
fix: resolve some parser related bugs
[rust.git] / src / syntux / parser.rs
1 use std::panic::{catch_unwind, AssertUnwindSafe};
2 use std::path::{Path, PathBuf};
3
4 use rustc_ast::ast;
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};
9
10 use crate::attr::first_attr_value_str_by_name;
11 use crate::syntux::session::ParseSess;
12 use crate::{Config, Input};
13
14 pub(crate) type DirectoryOwnership = rustc_expand::module::DirectoryOwnership;
15 pub(crate) type ModulePathSuccess = rustc_expand::module::ModulePathSuccess;
16
17 #[derive(Clone)]
18 pub(crate) struct Directory {
19     pub(crate) path: PathBuf,
20     pub(crate) ownership: DirectoryOwnership,
21 }
22
23 /// A parser for Rust source code.
24 pub(crate) struct Parser<'a> {
25     parser: RawParser<'a>,
26 }
27
28 /// A builder for the `Parser`.
29 #[derive(Default)]
30 pub(crate) struct ParserBuilder<'a> {
31     config: Option<&'a Config>,
32     sess: Option<&'a ParseSess>,
33     input: Option<Input>,
34     directory_ownership: Option<DirectoryOwnership>,
35 }
36
37 impl<'a> ParserBuilder<'a> {
38     pub(crate) fn input(mut self, input: Input) -> ParserBuilder<'a> {
39         self.input = Some(input);
40         self
41     }
42
43     pub(crate) fn sess(mut self, sess: &'a ParseSess) -> ParserBuilder<'a> {
44         self.sess = Some(sess);
45         self
46     }
47
48     pub(crate) fn config(mut self, config: &'a Config) -> ParserBuilder<'a> {
49         self.config = Some(config);
50         self
51     }
52
53     pub(crate) fn directory_ownership(
54         mut self,
55         directory_ownership: Option<DirectoryOwnership>,
56     ) -> ParserBuilder<'a> {
57         self.directory_ownership = directory_ownership;
58         self
59     }
60
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)?;
64
65         let parser = match Self::parser(sess.inner(), input) {
66             Ok(p) => p,
67             Err(db) => {
68                 if let Some(diagnostics) = db {
69                     sess.emit_diagnostics(diagnostics);
70                     return Err(ParserError::ParserCreationError);
71                 }
72                 return Err(ParserError::ParsePanicError);
73             }
74         };
75
76         Ok(Parser { parser })
77     }
78
79     fn parser(
80         sess: &'a rustc_session::parse::ParseSess,
81         input: Input,
82     ) -> Result<rustc_parse::parser::Parser<'a>, Option<Vec<Diagnostic>>> {
83         match input {
84             Input::File(ref file) => catch_unwind(AssertUnwindSafe(move || {
85                 new_parser_from_file(sess, file, None)
86             }))
87             .map_err(|_| None),
88             Input::Text(text) => rustc_parse::maybe_new_parser_from_source_str(
89                 sess,
90                 rustc_span::FileName::Custom("stdin".to_owned()),
91                 text,
92             )
93             .map_err(|db| Some(db)),
94         }
95     }
96 }
97
98 #[derive(Debug, PartialEq)]
99 pub(crate) enum ParserError {
100     NoParseSess,
101     NoInput,
102     ParserCreationError,
103     ParseError,
104     ParsePanicError,
105 }
106
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
113         // `/` to `\`.
114         #[cfg(windows)]
115         let path_string = path_string.replace("/", "\\");
116
117         Some(path.join(&*path_string))
118     }
119
120     pub(crate) fn parse_file_as_module(
121         sess: &'a ParseSess,
122         path: &Path,
123         span: Span,
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),
129                 Err(mut e) => {
130                     sess.emit_or_cancel_diagnostic(&mut e);
131                     if sess.can_reset_errors() {
132                         sess.reset_errors();
133                     }
134                     None
135                 }
136             }
137         }));
138         match result {
139             Ok(Some(m)) => {
140                 if !sess.has_errors() {
141                     return Ok(m);
142                 }
143
144                 if sess.can_reset_errors() {
145                     sess.reset_errors();
146                     return Ok(m);
147                 }
148                 Err(ParserError::ParseError)
149             }
150             Ok(None) => Err(ParserError::ParseError),
151             Err(..) if path.exists() => Err(ParserError::ParseError),
152             Err(_) => Err(ParserError::ParsePanicError),
153         }
154     }
155
156     pub(crate) fn parse_crate(
157         config: &'a Config,
158         input: Input,
159         directory_ownership: Option<DirectoryOwnership>,
160         sess: &'a ParseSess,
161     ) -> Result<ast::Crate, ParserError> {
162         let krate = Parser::parse_crate_inner(config, input, directory_ownership, sess)?;
163         if !sess.has_errors() {
164             return Ok(krate);
165         }
166
167         if sess.can_reset_errors() {
168             sess.reset_errors();
169             return Ok(krate);
170         }
171
172         Err(ParserError::ParseError)
173     }
174
175     fn parse_crate_inner(
176         config: &'a Config,
177         input: Input,
178         directory_ownership: Option<DirectoryOwnership>,
179         sess: &'a ParseSess,
180     ) -> Result<ast::Crate, ParserError> {
181         let mut parser = ParserBuilder::default()
182             .config(config)
183             .input(input)
184             .directory_ownership(directory_ownership)
185             .sess(sess)
186             .build()?;
187         parser.parse_crate_mod()
188     }
189
190     fn parse_crate_mod(&mut self) -> Result<ast::Crate, ParserError> {
191         let mut parser = AssertUnwindSafe(&mut self.parser);
192
193         match catch_unwind(move || parser.parse_crate_mod()) {
194             Ok(Ok(k)) => Ok(k),
195             Ok(Err(mut db)) => {
196                 db.emit();
197                 Err(ParserError::ParseError)
198             }
199             Err(_) => Err(ParserError::ParsePanicError),
200         }
201     }
202
203     pub(crate) fn parse_cfg_if(
204         sess: &'a ParseSess,
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!"),
211         }
212     }
213
214     fn parse_cfg_if_inner(
215         sess: &'a ParseSess,
216         mac: &'a ast::MacCall,
217     ) -> Result<Vec<ast::Item>, &'static str> {
218         let token_stream = mac.args.inner_tokens();
219         let mut parser =
220             rustc_parse::stream_to_parser(sess.inner(), token_stream.clone(), Some(""));
221
222         let mut items = vec![];
223         let mut process_if_cfg = true;
224
225         while parser.token.kind != TokenKind::Eof {
226             if process_if_cfg {
227                 if !parser.eat_keyword(kw::If) {
228                     return Err("Expected `if`");
229                 }
230                 parser
231                     .parse_attribute(false)
232                     .map_err(|_| "Failed to parse attributes")?;
233             }
234
235             if !parser.eat(&TokenKind::OpenDelim(DelimToken::Brace)) {
236                 return Err("Expected an opening brace");
237             }
238
239             while parser.token != TokenKind::CloseDelim(DelimToken::Brace)
240                 && parser.token.kind != TokenKind::Eof
241             {
242                 let item = match parser.parse_item() {
243                     Ok(Some(item_ptr)) => item_ptr.into_inner(),
244                     Ok(None) => continue,
245                     Err(mut err) => {
246                         err.cancel();
247                         parser.sess.span_diagnostic.reset_err_count();
248                         return Err(
249                             "Expected item inside cfg_if block, but failed to parse it as an item",
250                         );
251                     }
252                 };
253                 if let ast::ItemKind::Mod(..) = item.kind {
254                     items.push(item);
255                 }
256             }
257
258             if !parser.eat(&TokenKind::CloseDelim(DelimToken::Brace)) {
259                 return Err("Expected a closing brace");
260             }
261
262             if parser.eat(&TokenKind::Eof) {
263                 break;
264             }
265
266             if !parser.eat_keyword(kw::Else) {
267                 return Err("Expected `else`");
268             }
269
270             process_if_cfg = parser.token.is_keyword(kw::If);
271         }
272
273         Ok(items)
274     }
275 }