]>
Commit | Line | Data |
---|---|---|
9fa01778 XL |
1 | //! The main parser interface. |
2 | ||
cdc7bbd5 | 3 | #![feature(array_windows)] |
5e7ed085 | 4 | #![feature(box_patterns)] |
94222f64 | 5 | #![feature(if_let_guard)] |
487cf647 | 6 | #![feature(iter_intersperse)] |
5e7ed085 | 7 | #![feature(let_chains)] |
5e7ed085 | 8 | #![feature(never_type)] |
064997fb | 9 | #![feature(rustc_attrs)] |
6a06907d | 10 | #![recursion_limit = "256"] |
60c5eb7d | 11 | |
c295e0f8 XL |
12 | #[macro_use] |
13 | extern crate tracing; | |
14 | ||
3dfed10e | 15 | use rustc_ast as ast; |
923072b8 FG |
16 | use rustc_ast::token; |
17 | use rustc_ast::tokenstream::TokenStream; | |
487cf647 | 18 | use rustc_ast::{AttrItem, Attribute, MetaItem}; |
923072b8 | 19 | use rustc_ast_pretty::pprust; |
e74abb32 | 20 | use rustc_data_structures::sync::Lrc; |
94222f64 | 21 | use rustc_errors::{Applicability, Diagnostic, FatalError, Level, PResult}; |
74b04a01 | 22 | use rustc_session::parse::ParseSess; |
5869c6ff | 23 | use rustc_span::{FileName, SourceFile, Span}; |
9346a6ac | 24 | |
ba9703b0 | 25 | use std::path::Path; |
223e47cc | 26 | |
29967ef6 | 27 | pub const MACRO_ARGUMENTS: Option<&str> = Some("macro arguments"); |
9346a6ac | 28 | |
1a4d82fc | 29 | #[macro_use] |
223e47cc | 30 | pub mod parser; |
dfeec247 | 31 | use parser::{emit_unclosed_delims, make_unclosed_delims_error, Parser}; |
1a4d82fc | 32 | pub mod lexer; |
60c5eb7d | 33 | pub mod validate_attr; |
476ff2be | 34 | |
2b03887a FG |
35 | mod errors; |
36 | ||
e1599b0c | 37 | // A bunch of utility functions of the form `parse_<thing>_from_<source>` |
223e47cc LB |
38 | // where <thing> includes crate, expr, item, stmt, tts, and one that |
39 | // uses a HOF to parse anything, and <source> includes file and | |
e1599b0c | 40 | // `source_str`. |
223e47cc | 41 | |
2b03887a FG |
42 | /// A variant of 'panictry!' that works on a `Vec<Diagnostic>` instead of a single |
43 | /// `DiagnosticBuilder`. | |
e74abb32 | 44 | macro_rules! panictry_buffer { |
dfeec247 | 45 | ($handler:expr, $e:expr) => {{ |
60c5eb7d | 46 | use rustc_errors::FatalError; |
dfeec247 | 47 | use std::result::Result::{Err, Ok}; |
e74abb32 XL |
48 | match $e { |
49 | Ok(e) => e, | |
50 | Err(errs) => { | |
5e7ed085 FG |
51 | for mut e in errs { |
52 | $handler.emit_diagnostic(&mut e); | |
e74abb32 XL |
53 | } |
54 | FatalError.raise() | |
55 | } | |
56 | } | |
dfeec247 | 57 | }}; |
e74abb32 XL |
58 | } |
59 | ||
c30ab7b3 | 60 | pub fn parse_crate_from_file<'a>(input: &Path, sess: &'a ParseSess) -> PResult<'a, ast::Crate> { |
ba9703b0 | 61 | let mut parser = new_parser_from_file(sess, input, None); |
54a0048b | 62 | parser.parse_crate_mod() |
223e47cc LB |
63 | } |
64 | ||
dfeec247 XL |
65 | pub fn parse_crate_attrs_from_file<'a>( |
66 | input: &Path, | |
67 | sess: &'a ParseSess, | |
f2b60f7d | 68 | ) -> PResult<'a, ast::AttrVec> { |
ba9703b0 | 69 | let mut parser = new_parser_from_file(sess, input, None); |
54a0048b | 70 | parser.parse_inner_attributes() |
1a4d82fc JJ |
71 | } |
72 | ||
dfeec247 XL |
73 | pub fn parse_crate_from_source_str( |
74 | name: FileName, | |
75 | source: String, | |
76 | sess: &ParseSess, | |
77 | ) -> PResult<'_, ast::Crate> { | |
c30ab7b3 | 78 | new_parser_from_source_str(sess, name, source).parse_crate_mod() |
223e47cc LB |
79 | } |
80 | ||
dfeec247 XL |
81 | pub fn parse_crate_attrs_from_source_str( |
82 | name: FileName, | |
83 | source: String, | |
84 | sess: &ParseSess, | |
f2b60f7d | 85 | ) -> PResult<'_, ast::AttrVec> { |
c30ab7b3 | 86 | new_parser_from_source_str(sess, name, source).parse_inner_attributes() |
1a4d82fc JJ |
87 | } |
88 | ||
9fa01778 XL |
89 | pub fn parse_stream_from_source_str( |
90 | name: FileName, | |
91 | source: String, | |
92 | sess: &ParseSess, | |
93 | override_span: Option<Span>, | |
94 | ) -> TokenStream { | |
dfeec247 XL |
95 | let (stream, mut errors) = |
96 | source_file_to_stream(sess, sess.source_map().new_source_file(name, source), override_span); | |
e74abb32 | 97 | emit_unclosed_delims(&mut errors, &sess); |
9fa01778 | 98 | stream |
223e47cc LB |
99 | } |
100 | ||
9fa01778 XL |
101 | /// Creates a new parser from a source string. |
102 | pub fn new_parser_from_source_str(sess: &ParseSess, name: FileName, source: String) -> Parser<'_> { | |
a1dfa0c6 XL |
103 | panictry_buffer!(&sess.span_diagnostic, maybe_new_parser_from_source_str(sess, name, source)) |
104 | } | |
105 | ||
9fa01778 | 106 | /// Creates a new parser from a source string. Returns any buffered errors from lexing the initial |
a1dfa0c6 | 107 | /// token stream. |
dfeec247 XL |
108 | pub fn maybe_new_parser_from_source_str( |
109 | sess: &ParseSess, | |
110 | name: FileName, | |
111 | source: String, | |
112 | ) -> Result<Parser<'_>, Vec<Diagnostic>> { | |
ba9703b0 | 113 | maybe_source_file_to_parser(sess, sess.source_map().new_source_file(name, source)) |
223e47cc LB |
114 | } |
115 | ||
e1599b0c | 116 | /// Creates a new parser, handling errors as appropriate if the file doesn't exist. |
1b1a35ee | 117 | /// If a span is given, that is used on an error as the source of the problem. |
ba9703b0 XL |
118 | pub fn new_parser_from_file<'a>(sess: &'a ParseSess, path: &Path, sp: Option<Span>) -> Parser<'a> { |
119 | source_file_to_parser(sess, file_to_source_file(sess, path, sp)) | |
223e47cc LB |
120 | } |
121 | ||
064997fb | 122 | /// Given a session and a `source_file`, returns a parser. |
9fa01778 | 123 | fn source_file_to_parser(sess: &ParseSess, source_file: Lrc<SourceFile>) -> Parser<'_> { |
dfeec247 | 124 | panictry_buffer!(&sess.span_diagnostic, maybe_source_file_to_parser(sess, source_file)) |
a1dfa0c6 XL |
125 | } |
126 | ||
064997fb | 127 | /// Given a session and a `source_file`, return a parser. Returns any buffered errors from lexing the |
a1dfa0c6 | 128 | /// initial token stream. |
9fa01778 XL |
129 | fn maybe_source_file_to_parser( |
130 | sess: &ParseSess, | |
131 | source_file: Lrc<SourceFile>, | |
132 | ) -> Result<Parser<'_>, Vec<Diagnostic>> { | |
b7449926 | 133 | let end_pos = source_file.end_pos; |
9fa01778 | 134 | let (stream, unclosed_delims) = maybe_file_to_stream(sess, source_file, None)?; |
dc9dc135 | 135 | let mut parser = stream_to_parser(sess, stream, None); |
9fa01778 | 136 | parser.unclosed_delims = unclosed_delims; |
74b04a01 | 137 | if parser.token == token::Eof { |
c295e0f8 | 138 | parser.token.span = Span::new(end_pos, end_pos, parser.token.span.ctxt(), None); |
c1a9b12d SL |
139 | } |
140 | ||
a1dfa0c6 | 141 | Ok(parser) |
970d7e83 LB |
142 | } |
143 | ||
e1599b0c | 144 | // Base abstractions |
970d7e83 | 145 | |
0731742a XL |
146 | /// Given a session and a path and an optional span (for error reporting), |
147 | /// add the path to the session's source_map and return the new source_file or | |
148 | /// error when a file can't be read. | |
dfeec247 XL |
149 | fn try_file_to_source_file( |
150 | sess: &ParseSess, | |
151 | path: &Path, | |
152 | spanopt: Option<Span>, | |
153 | ) -> Result<Lrc<SourceFile>, Diagnostic> { | |
154 | sess.source_map().load_file(path).map_err(|e| { | |
0731742a XL |
155 | let msg = format!("couldn't read {}: {}", path.display(), e); |
156 | let mut diag = Diagnostic::new(Level::Fatal, &msg); | |
157 | if let Some(sp) = spanopt { | |
158 | diag.set_span(sp); | |
159 | } | |
160 | diag | |
161 | }) | |
162 | } | |
163 | ||
970d7e83 | 164 | /// Given a session and a path and an optional span (for error reporting), |
e1599b0c | 165 | /// adds the path to the session's `source_map` and returns the new `source_file`. |
dfeec247 | 166 | fn file_to_source_file(sess: &ParseSess, path: &Path, spanopt: Option<Span>) -> Lrc<SourceFile> { |
0731742a | 167 | match try_file_to_source_file(sess, path, spanopt) { |
b7449926 | 168 | Ok(source_file) => source_file, |
5e7ed085 FG |
169 | Err(mut d) => { |
170 | sess.span_diagnostic.emit_diagnostic(&mut d); | |
0731742a | 171 | FatalError.raise(); |
223e47cc LB |
172 | } |
173 | } | |
174 | } | |
175 | ||
e1599b0c | 176 | /// Given a `source_file`, produces a sequence of token trees. |
9fa01778 XL |
177 | pub fn source_file_to_stream( |
178 | sess: &ParseSess, | |
179 | source_file: Lrc<SourceFile>, | |
180 | override_span: Option<Span>, | |
181 | ) -> (TokenStream, Vec<lexer::UnmatchedBrace>) { | |
a1dfa0c6 XL |
182 | panictry_buffer!(&sess.span_diagnostic, maybe_file_to_stream(sess, source_file, override_span)) |
183 | } | |
184 | ||
9fa01778 | 185 | /// Given a source file, produces a sequence of token trees. Returns any buffered errors from |
48663c56 | 186 | /// parsing the token stream. |
9fa01778 XL |
187 | pub fn maybe_file_to_stream( |
188 | sess: &ParseSess, | |
189 | source_file: Lrc<SourceFile>, | |
190 | override_span: Option<Span>, | |
191 | ) -> Result<(TokenStream, Vec<lexer::UnmatchedBrace>), Vec<Diagnostic>> { | |
1b1a35ee | 192 | let src = source_file.src.as_ref().unwrap_or_else(|| { |
17df50a5 XL |
193 | sess.span_diagnostic.bug(&format!( |
194 | "cannot lex `source_file` without source: {}", | |
94222f64 | 195 | sess.source_map().filename_for_diagnostics(&source_file.name) |
17df50a5 | 196 | )); |
1b1a35ee XL |
197 | }); |
198 | ||
199 | let (token_trees, unmatched_braces) = | |
200 | lexer::parse_token_trees(sess, src.as_str(), source_file.start_pos, override_span); | |
a1dfa0c6 | 201 | |
48663c56 XL |
202 | match token_trees { |
203 | Ok(stream) => Ok((stream, unmatched_braces)), | |
a1dfa0c6 XL |
204 | Err(err) => { |
205 | let mut buffer = Vec::with_capacity(1); | |
206 | err.buffer(&mut buffer); | |
9fa01778 | 207 | // Not using `emit_unclosed_delims` to use `db.buffer` |
48663c56 | 208 | for unmatched in unmatched_braces { |
e74abb32 XL |
209 | if let Some(err) = make_unclosed_delims_error(unmatched, &sess) { |
210 | err.buffer(&mut buffer); | |
9fa01778 | 211 | } |
9fa01778 | 212 | } |
a1dfa0c6 XL |
213 | Err(buffer) |
214 | } | |
215 | } | |
970d7e83 LB |
216 | } |
217 | ||
e1599b0c | 218 | /// Given a stream and the `ParseSess`, produces a parser. |
dc9dc135 XL |
219 | pub fn stream_to_parser<'a>( |
220 | sess: &'a ParseSess, | |
221 | stream: TokenStream, | |
222 | subparser_name: Option<&'static str>, | |
223 | ) -> Parser<'a> { | |
ba9703b0 | 224 | Parser::new(sess, stream, false, subparser_name) |
1a4d82fc JJ |
225 | } |
226 | ||
e74abb32 | 227 | /// Runs the given subparser `f` on the tokens of the given `attr`'s item. |
60c5eb7d | 228 | pub fn parse_in<'a, T>( |
e74abb32 | 229 | sess: &'a ParseSess, |
60c5eb7d XL |
230 | tts: TokenStream, |
231 | name: &'static str, | |
e74abb32 XL |
232 | mut f: impl FnMut(&mut Parser<'a>) -> PResult<'a, T>, |
233 | ) -> PResult<'a, T> { | |
ba9703b0 | 234 | let mut parser = Parser::new(sess, tts, false, Some(name)); |
e74abb32 XL |
235 | let result = f(&mut parser)?; |
236 | if parser.token != token::Eof { | |
237 | parser.unexpected()?; | |
238 | } | |
239 | Ok(result) | |
8faf50e0 XL |
240 | } |
241 | ||
923072b8 FG |
242 | pub fn fake_token_stream_for_item(sess: &ParseSess, item: &ast::Item) -> TokenStream { |
243 | let source = pprust::item_to_string(item); | |
5869c6ff | 244 | let filename = FileName::macro_expansion_source_code(&source); |
923072b8 | 245 | parse_stream_from_source_str(filename, source, sess, Some(item.span)) |
3dfed10e | 246 | } |
94222f64 | 247 | |
a2a8927a XL |
248 | pub fn fake_token_stream_for_crate(sess: &ParseSess, krate: &ast::Crate) -> TokenStream { |
249 | let source = pprust::crate_to_string_for_macros(krate); | |
250 | let filename = FileName::macro_expansion_source_code(&source); | |
5e7ed085 | 251 | parse_stream_from_source_str(filename, source, sess, Some(krate.spans.inner_span)) |
a2a8927a XL |
252 | } |
253 | ||
94222f64 XL |
254 | pub fn parse_cfg_attr( |
255 | attr: &Attribute, | |
256 | parse_sess: &ParseSess, | |
257 | ) -> Option<(MetaItem, Vec<(AttrItem, Span)>)> { | |
258 | match attr.get_normal_item().args { | |
487cf647 FG |
259 | ast::AttrArgs::Delimited(ast::DelimArgs { dspan, delim, ref tokens }) |
260 | if !tokens.is_empty() => | |
261 | { | |
94222f64 XL |
262 | let msg = "wrong `cfg_attr` delimiters"; |
263 | crate::validate_attr::check_meta_bad_delim(parse_sess, dspan, delim, msg); | |
487cf647 | 264 | match parse_in(parse_sess, tokens.clone(), "`cfg_attr` input", |p| p.parse_cfg_attr()) { |
94222f64 XL |
265 | Ok(r) => return Some(r), |
266 | Err(mut e) => { | |
267 | e.help(&format!("the valid syntax is `{}`", CFG_ATTR_GRAMMAR_HELP)) | |
268 | .note(CFG_ATTR_NOTE_REF) | |
269 | .emit(); | |
270 | } | |
271 | } | |
272 | } | |
273 | _ => error_malformed_cfg_attr_missing(attr.span, parse_sess), | |
274 | } | |
275 | None | |
276 | } | |
277 | ||
278 | const CFG_ATTR_GRAMMAR_HELP: &str = "#[cfg_attr(condition, attribute, other_attribute, ...)]"; | |
279 | const CFG_ATTR_NOTE_REF: &str = "for more information, visit \ | |
280 | <https://doc.rust-lang.org/reference/conditional-compilation.html\ | |
281 | #the-cfg_attr-attribute>"; | |
282 | ||
283 | fn error_malformed_cfg_attr_missing(span: Span, parse_sess: &ParseSess) { | |
284 | parse_sess | |
285 | .span_diagnostic | |
286 | .struct_span_err(span, "malformed `cfg_attr` attribute input") | |
287 | .span_suggestion( | |
288 | span, | |
289 | "missing condition and attribute", | |
923072b8 | 290 | CFG_ATTR_GRAMMAR_HELP, |
94222f64 XL |
291 | Applicability::HasPlaceholders, |
292 | ) | |
293 | .note(CFG_ATTR_NOTE_REF) | |
294 | .emit(); | |
295 | } |