]> git.lizzy.rs Git - rust.git/blob - src/tools/rustfmt/src/syntux/parser.rs
Rollup merge of #86984 - Smittyvb:ipv4-octal-zero, r=m-ou-se
[rust.git] / src / tools / rustfmt / src / syntux / parser.rs
1 use std::panic::{catch_unwind, AssertUnwindSafe};
2 use std::path::{Path, PathBuf};
3
4 use rustc_ast::token::{DelimToken, TokenKind};
5 use rustc_ast::{ast, ptr};
6 use rustc_errors::Diagnostic;
7 use rustc_parse::{
8     new_parser_from_file,
9     parser::{ForceCollect, Parser as RawParser},
10 };
11 use rustc_span::{sym, symbol::kw, Span};
12
13 use crate::attr::first_attr_value_str_by_name;
14 use crate::syntux::session::ParseSess;
15 use crate::Input;
16
17 pub(crate) type DirectoryOwnership = rustc_expand::module::DirOwnership;
18 pub(crate) type ModulePathSuccess = rustc_expand::module::ModulePathSuccess;
19 pub(crate) type ModError<'a> = rustc_expand::module::ModError<'a>;
20
21 #[derive(Clone)]
22 pub(crate) struct Directory {
23     pub(crate) path: PathBuf,
24     pub(crate) ownership: DirectoryOwnership,
25 }
26
27 /// A parser for Rust source code.
28 pub(crate) struct Parser<'a> {
29     parser: RawParser<'a>,
30 }
31
32 /// A builder for the `Parser`.
33 #[derive(Default)]
34 pub(crate) struct ParserBuilder<'a> {
35     sess: Option<&'a ParseSess>,
36     input: Option<Input>,
37 }
38
39 impl<'a> ParserBuilder<'a> {
40     pub(crate) fn input(mut self, input: Input) -> ParserBuilder<'a> {
41         self.input = Some(input);
42         self
43     }
44
45     pub(crate) fn sess(mut self, sess: &'a ParseSess) -> ParserBuilder<'a> {
46         self.sess = Some(sess);
47         self
48     }
49
50     pub(crate) fn build(self) -> Result<Parser<'a>, ParserError> {
51         let sess = self.sess.ok_or(ParserError::NoParseSess)?;
52         let input = self.input.ok_or(ParserError::NoInput)?;
53
54         let parser = match Self::parser(sess.inner(), input) {
55             Ok(p) => p,
56             Err(db) => {
57                 if let Some(diagnostics) = db {
58                     sess.emit_diagnostics(diagnostics);
59                     return Err(ParserError::ParserCreationError);
60                 }
61                 return Err(ParserError::ParsePanicError);
62             }
63         };
64
65         Ok(Parser { parser })
66     }
67
68     fn parser(
69         sess: &'a rustc_session::parse::ParseSess,
70         input: Input,
71     ) -> Result<rustc_parse::parser::Parser<'a>, Option<Vec<Diagnostic>>> {
72         match input {
73             Input::File(ref file) => catch_unwind(AssertUnwindSafe(move || {
74                 new_parser_from_file(sess, file, None)
75             }))
76             .map_err(|_| None),
77             Input::Text(text) => rustc_parse::maybe_new_parser_from_source_str(
78                 sess,
79                 rustc_span::FileName::Custom("stdin".to_owned()),
80                 text,
81             )
82             .map_err(Some),
83         }
84     }
85 }
86
87 #[derive(Debug, PartialEq)]
88 pub(crate) enum ParserError {
89     NoParseSess,
90     NoInput,
91     ParserCreationError,
92     ParseError,
93     ParsePanicError,
94 }
95
96 impl<'a> Parser<'a> {
97     pub(crate) fn submod_path_from_attr(attrs: &[ast::Attribute], path: &Path) -> Option<PathBuf> {
98         let path_string = first_attr_value_str_by_name(attrs, sym::path)?.as_str();
99         // On windows, the base path might have the form
100         // `\\?\foo\bar` in which case it does not tolerate
101         // mixed `/` and `\` separators, so canonicalize
102         // `/` to `\`.
103         #[cfg(windows)]
104         let path_string = path_string.replace("/", "\\");
105
106         Some(path.join(&*path_string))
107     }
108
109     pub(crate) fn parse_file_as_module(
110         sess: &'a ParseSess,
111         path: &Path,
112         span: Span,
113     ) -> Result<(Vec<ast::Attribute>, Vec<ptr::P<ast::Item>>, Span), ParserError> {
114         let result = catch_unwind(AssertUnwindSafe(|| {
115             let mut parser = new_parser_from_file(sess.inner(), &path, Some(span));
116             match parser.parse_mod(&TokenKind::Eof) {
117                 Ok(result) => Some(result),
118                 Err(mut e) => {
119                     sess.emit_or_cancel_diagnostic(&mut e);
120                     if sess.can_reset_errors() {
121                         sess.reset_errors();
122                     }
123                     None
124                 }
125             }
126         }));
127         match result {
128             Ok(Some(m)) => {
129                 if !sess.has_errors() {
130                     return Ok(m);
131                 }
132
133                 if sess.can_reset_errors() {
134                     sess.reset_errors();
135                     return Ok(m);
136                 }
137                 Err(ParserError::ParseError)
138             }
139             Ok(None) => Err(ParserError::ParseError),
140             Err(..) if path.exists() => Err(ParserError::ParseError),
141             Err(_) => Err(ParserError::ParsePanicError),
142         }
143     }
144
145     pub(crate) fn parse_crate(
146         input: Input,
147         sess: &'a ParseSess,
148     ) -> Result<ast::Crate, ParserError> {
149         let krate = Parser::parse_crate_inner(input, sess)?;
150         if !sess.has_errors() {
151             return Ok(krate);
152         }
153
154         if sess.can_reset_errors() {
155             sess.reset_errors();
156             return Ok(krate);
157         }
158
159         Err(ParserError::ParseError)
160     }
161
162     fn parse_crate_inner(input: Input, sess: &'a ParseSess) -> Result<ast::Crate, ParserError> {
163         ParserBuilder::default()
164             .input(input)
165             .sess(sess)
166             .build()?
167             .parse_crate_mod()
168     }
169
170     fn parse_crate_mod(&mut self) -> Result<ast::Crate, ParserError> {
171         let mut parser = AssertUnwindSafe(&mut self.parser);
172
173         match catch_unwind(move || parser.parse_crate_mod()) {
174             Ok(Ok(k)) => Ok(k),
175             Ok(Err(mut db)) => {
176                 db.emit();
177                 Err(ParserError::ParseError)
178             }
179             Err(_) => Err(ParserError::ParsePanicError),
180         }
181     }
182
183     pub(crate) fn parse_cfg_if(
184         sess: &'a ParseSess,
185         mac: &'a ast::MacCall,
186     ) -> Result<Vec<ast::Item>, &'static str> {
187         match catch_unwind(AssertUnwindSafe(|| Parser::parse_cfg_if_inner(sess, mac))) {
188             Ok(Ok(items)) => Ok(items),
189             Ok(err @ Err(_)) => err,
190             Err(..) => Err("failed to parse cfg_if!"),
191         }
192     }
193
194     fn parse_cfg_if_inner(
195         sess: &'a ParseSess,
196         mac: &'a ast::MacCall,
197     ) -> Result<Vec<ast::Item>, &'static str> {
198         let token_stream = mac.args.inner_tokens();
199         let mut parser = rustc_parse::stream_to_parser(sess.inner(), token_stream, Some(""));
200
201         let mut items = vec![];
202         let mut process_if_cfg = true;
203
204         while parser.token.kind != TokenKind::Eof {
205             if process_if_cfg {
206                 if !parser.eat_keyword(kw::If) {
207                     return Err("Expected `if`");
208                 }
209                 // Inner attributes are not actually syntactically permitted here, but we don't
210                 // care about inner vs outer attributes in this position. Our purpose with this
211                 // special case parsing of cfg_if macros is to ensure we can correctly resolve
212                 // imported modules that may have a custom `path` defined.
213                 //
214                 // As such, we just need to advance the parser past the attribute and up to
215                 // to the opening brace.
216                 // See also https://github.com/rust-lang/rust/pull/79433
217                 parser
218                     .parse_attribute(rustc_parse::parser::attr::InnerAttrPolicy::Permitted)
219                     .map_err(|_| "Failed to parse attributes")?;
220             }
221
222             if !parser.eat(&TokenKind::OpenDelim(DelimToken::Brace)) {
223                 return Err("Expected an opening brace");
224             }
225
226             while parser.token != TokenKind::CloseDelim(DelimToken::Brace)
227                 && parser.token.kind != TokenKind::Eof
228             {
229                 let item = match parser.parse_item(ForceCollect::No) {
230                     Ok(Some(item_ptr)) => item_ptr.into_inner(),
231                     Ok(None) => continue,
232                     Err(mut err) => {
233                         err.cancel();
234                         parser.sess.span_diagnostic.reset_err_count();
235                         return Err(
236                             "Expected item inside cfg_if block, but failed to parse it as an item",
237                         );
238                     }
239                 };
240                 if let ast::ItemKind::Mod(..) = item.kind {
241                     items.push(item);
242                 }
243             }
244
245             if !parser.eat(&TokenKind::CloseDelim(DelimToken::Brace)) {
246                 return Err("Expected a closing brace");
247             }
248
249             if parser.eat(&TokenKind::Eof) {
250                 break;
251             }
252
253             if !parser.eat_keyword(kw::Else) {
254                 return Err("Expected `else`");
255             }
256
257             process_if_cfg = parser.token.is_keyword(kw::If);
258         }
259
260         Ok(items)
261     }
262 }