]>
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}; |
9ffffee4 FG |
22 | use rustc_errors::{DiagnosticMessage, SubdiagnosticMessage}; |
23 | use rustc_macros::fluent_messages; | |
74b04a01 | 24 | use rustc_session::parse::ParseSess; |
5869c6ff | 25 | use rustc_span::{FileName, SourceFile, Span}; |
9346a6ac | 26 | |
ba9703b0 | 27 | use std::path::Path; |
223e47cc | 28 | |
29967ef6 | 29 | pub const MACRO_ARGUMENTS: Option<&str> = Some("macro arguments"); |
9346a6ac | 30 | |
1a4d82fc | 31 | #[macro_use] |
223e47cc | 32 | pub mod parser; |
9ffffee4 | 33 | use parser::{make_unclosed_delims_error, Parser}; |
1a4d82fc | 34 | pub mod lexer; |
60c5eb7d | 35 | pub mod validate_attr; |
476ff2be | 36 | |
2b03887a FG |
37 | mod errors; |
38 | ||
353b0b11 | 39 | fluent_messages! { "../messages.ftl" } |
9ffffee4 | 40 | |
e1599b0c | 41 | // A bunch of utility functions of the form `parse_<thing>_from_<source>` |
223e47cc LB |
42 | // where <thing> includes crate, expr, item, stmt, tts, and one that |
43 | // uses a HOF to parse anything, and <source> includes file and | |
e1599b0c | 44 | // `source_str`. |
223e47cc | 45 | |
2b03887a FG |
46 | /// A variant of 'panictry!' that works on a `Vec<Diagnostic>` instead of a single |
47 | /// `DiagnosticBuilder`. | |
e74abb32 | 48 | macro_rules! panictry_buffer { |
dfeec247 | 49 | ($handler:expr, $e:expr) => {{ |
60c5eb7d | 50 | use rustc_errors::FatalError; |
dfeec247 | 51 | use std::result::Result::{Err, Ok}; |
e74abb32 XL |
52 | match $e { |
53 | Ok(e) => e, | |
54 | Err(errs) => { | |
5e7ed085 FG |
55 | for mut e in errs { |
56 | $handler.emit_diagnostic(&mut e); | |
e74abb32 XL |
57 | } |
58 | FatalError.raise() | |
59 | } | |
60 | } | |
dfeec247 | 61 | }}; |
e74abb32 XL |
62 | } |
63 | ||
c30ab7b3 | 64 | pub fn parse_crate_from_file<'a>(input: &Path, sess: &'a ParseSess) -> PResult<'a, ast::Crate> { |
ba9703b0 | 65 | let mut parser = new_parser_from_file(sess, input, None); |
54a0048b | 66 | parser.parse_crate_mod() |
223e47cc LB |
67 | } |
68 | ||
dfeec247 XL |
69 | pub fn parse_crate_attrs_from_file<'a>( |
70 | input: &Path, | |
71 | sess: &'a ParseSess, | |
f2b60f7d | 72 | ) -> PResult<'a, ast::AttrVec> { |
ba9703b0 | 73 | let mut parser = new_parser_from_file(sess, input, None); |
54a0048b | 74 | parser.parse_inner_attributes() |
1a4d82fc JJ |
75 | } |
76 | ||
dfeec247 XL |
77 | pub fn parse_crate_from_source_str( |
78 | name: FileName, | |
79 | source: String, | |
80 | sess: &ParseSess, | |
81 | ) -> PResult<'_, ast::Crate> { | |
c30ab7b3 | 82 | new_parser_from_source_str(sess, name, source).parse_crate_mod() |
223e47cc LB |
83 | } |
84 | ||
dfeec247 XL |
85 | pub fn parse_crate_attrs_from_source_str( |
86 | name: FileName, | |
87 | source: String, | |
88 | sess: &ParseSess, | |
f2b60f7d | 89 | ) -> PResult<'_, ast::AttrVec> { |
c30ab7b3 | 90 | new_parser_from_source_str(sess, name, source).parse_inner_attributes() |
1a4d82fc JJ |
91 | } |
92 | ||
9fa01778 XL |
93 | pub fn parse_stream_from_source_str( |
94 | name: FileName, | |
95 | source: String, | |
96 | sess: &ParseSess, | |
97 | override_span: Option<Span>, | |
98 | ) -> TokenStream { | |
9ffffee4 | 99 | source_file_to_stream(sess, sess.source_map().new_source_file(name, source), override_span) |
223e47cc LB |
100 | } |
101 | ||
9fa01778 XL |
102 | /// Creates a new parser from a source string. |
103 | pub fn new_parser_from_source_str(sess: &ParseSess, name: FileName, source: String) -> Parser<'_> { | |
a1dfa0c6 XL |
104 | panictry_buffer!(&sess.span_diagnostic, maybe_new_parser_from_source_str(sess, name, source)) |
105 | } | |
106 | ||
9fa01778 | 107 | /// Creates a new parser from a source string. Returns any buffered errors from lexing the initial |
a1dfa0c6 | 108 | /// token stream. |
dfeec247 XL |
109 | pub fn maybe_new_parser_from_source_str( |
110 | sess: &ParseSess, | |
111 | name: FileName, | |
112 | source: String, | |
113 | ) -> Result<Parser<'_>, Vec<Diagnostic>> { | |
ba9703b0 | 114 | maybe_source_file_to_parser(sess, sess.source_map().new_source_file(name, source)) |
223e47cc LB |
115 | } |
116 | ||
e1599b0c | 117 | /// Creates a new parser, handling errors as appropriate if the file doesn't exist. |
1b1a35ee | 118 | /// If a span is given, that is used on an error as the source of the problem. |
ba9703b0 XL |
119 | pub fn new_parser_from_file<'a>(sess: &'a ParseSess, path: &Path, sp: Option<Span>) -> Parser<'a> { |
120 | source_file_to_parser(sess, file_to_source_file(sess, path, sp)) | |
223e47cc LB |
121 | } |
122 | ||
064997fb | 123 | /// Given a session and a `source_file`, returns a parser. |
9fa01778 | 124 | fn source_file_to_parser(sess: &ParseSess, source_file: Lrc<SourceFile>) -> Parser<'_> { |
dfeec247 | 125 | panictry_buffer!(&sess.span_diagnostic, maybe_source_file_to_parser(sess, source_file)) |
a1dfa0c6 XL |
126 | } |
127 | ||
064997fb | 128 | /// Given a session and a `source_file`, return a parser. Returns any buffered errors from lexing the |
a1dfa0c6 | 129 | /// initial token stream. |
9fa01778 XL |
130 | fn maybe_source_file_to_parser( |
131 | sess: &ParseSess, | |
132 | source_file: Lrc<SourceFile>, | |
133 | ) -> Result<Parser<'_>, Vec<Diagnostic>> { | |
b7449926 | 134 | let end_pos = source_file.end_pos; |
9ffffee4 | 135 | let stream = maybe_file_to_stream(sess, source_file, None)?; |
dc9dc135 | 136 | let mut parser = stream_to_parser(sess, stream, None); |
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>, | |
9ffffee4 | 181 | ) -> TokenStream { |
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>, | |
9ffffee4 | 191 | ) -> Result<TokenStream, 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 | ||
9ffffee4 | 199 | lexer::parse_token_trees(sess, src.as_str(), source_file.start_pos, override_span) |
970d7e83 LB |
200 | } |
201 | ||
e1599b0c | 202 | /// Given a stream and the `ParseSess`, produces a parser. |
dc9dc135 XL |
203 | pub fn stream_to_parser<'a>( |
204 | sess: &'a ParseSess, | |
205 | stream: TokenStream, | |
206 | subparser_name: Option<&'static str>, | |
207 | ) -> Parser<'a> { | |
ba9703b0 | 208 | Parser::new(sess, stream, false, subparser_name) |
1a4d82fc JJ |
209 | } |
210 | ||
e74abb32 | 211 | /// Runs the given subparser `f` on the tokens of the given `attr`'s item. |
60c5eb7d | 212 | pub fn parse_in<'a, T>( |
e74abb32 | 213 | sess: &'a ParseSess, |
60c5eb7d XL |
214 | tts: TokenStream, |
215 | name: &'static str, | |
e74abb32 XL |
216 | mut f: impl FnMut(&mut Parser<'a>) -> PResult<'a, T>, |
217 | ) -> PResult<'a, T> { | |
ba9703b0 | 218 | let mut parser = Parser::new(sess, tts, false, Some(name)); |
e74abb32 XL |
219 | let result = f(&mut parser)?; |
220 | if parser.token != token::Eof { | |
221 | parser.unexpected()?; | |
222 | } | |
223 | Ok(result) | |
8faf50e0 XL |
224 | } |
225 | ||
923072b8 FG |
226 | pub fn fake_token_stream_for_item(sess: &ParseSess, item: &ast::Item) -> TokenStream { |
227 | let source = pprust::item_to_string(item); | |
5869c6ff | 228 | let filename = FileName::macro_expansion_source_code(&source); |
923072b8 | 229 | parse_stream_from_source_str(filename, source, sess, Some(item.span)) |
3dfed10e | 230 | } |
94222f64 | 231 | |
a2a8927a XL |
232 | pub fn fake_token_stream_for_crate(sess: &ParseSess, krate: &ast::Crate) -> TokenStream { |
233 | let source = pprust::crate_to_string_for_macros(krate); | |
234 | let filename = FileName::macro_expansion_source_code(&source); | |
5e7ed085 | 235 | parse_stream_from_source_str(filename, source, sess, Some(krate.spans.inner_span)) |
a2a8927a XL |
236 | } |
237 | ||
94222f64 XL |
238 | pub fn parse_cfg_attr( |
239 | attr: &Attribute, | |
240 | parse_sess: &ParseSess, | |
241 | ) -> Option<(MetaItem, Vec<(AttrItem, Span)>)> { | |
242 | match attr.get_normal_item().args { | |
487cf647 FG |
243 | ast::AttrArgs::Delimited(ast::DelimArgs { dspan, delim, ref tokens }) |
244 | if !tokens.is_empty() => | |
245 | { | |
94222f64 XL |
246 | let msg = "wrong `cfg_attr` delimiters"; |
247 | crate::validate_attr::check_meta_bad_delim(parse_sess, dspan, delim, msg); | |
487cf647 | 248 | match parse_in(parse_sess, tokens.clone(), "`cfg_attr` input", |p| p.parse_cfg_attr()) { |
94222f64 XL |
249 | Ok(r) => return Some(r), |
250 | Err(mut e) => { | |
251 | e.help(&format!("the valid syntax is `{}`", CFG_ATTR_GRAMMAR_HELP)) | |
252 | .note(CFG_ATTR_NOTE_REF) | |
253 | .emit(); | |
254 | } | |
255 | } | |
256 | } | |
257 | _ => error_malformed_cfg_attr_missing(attr.span, parse_sess), | |
258 | } | |
259 | None | |
260 | } | |
261 | ||
262 | const CFG_ATTR_GRAMMAR_HELP: &str = "#[cfg_attr(condition, attribute, other_attribute, ...)]"; | |
263 | const CFG_ATTR_NOTE_REF: &str = "for more information, visit \ | |
264 | <https://doc.rust-lang.org/reference/conditional-compilation.html\ | |
265 | #the-cfg_attr-attribute>"; | |
266 | ||
267 | fn error_malformed_cfg_attr_missing(span: Span, parse_sess: &ParseSess) { | |
268 | parse_sess | |
269 | .span_diagnostic | |
270 | .struct_span_err(span, "malformed `cfg_attr` attribute input") | |
271 | .span_suggestion( | |
272 | span, | |
273 | "missing condition and attribute", | |
923072b8 | 274 | CFG_ATTR_GRAMMAR_HELP, |
94222f64 XL |
275 | Applicability::HasPlaceholders, |
276 | ) | |
277 | .note(CFG_ATTR_NOTE_REF) | |
278 | .emit(); | |
279 | } |