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