1#![cfg_attr(test, feature(iter_order_by))]
5#![feature(debug_closure_helpers)]
6#![feature(default_field_values)]
7#![feature(deref_patterns)]
8#![feature(iter_intersperse)]
9#![recursion_limit = "256"]
10use std::path::{Path, PathBuf};
13use std::str::Utf8Error;
14use std::sync::Arc;
15
16use rustc_ast as ast;
17use rustc_ast::token;
18use rustc_ast::tokenstream::{DelimSpacing, DelimSpan, Spacing, TokenStream, TokenTree};
19use rustc_ast_pretty::pprust;
20use rustc_errors::{Diag, EmissionGuarantee, FatalError, PResult, pluralize};
21pub use rustc_lexer::UNICODE_VERSION;
22use rustc_session::parse::ParseSess;
23use rustc_span::edit_distance::find_best_match_for_name;
24use rustc_span::source_map::SourceMap;
25use rustc_span::{FileName, SourceFile, Span, Symbol};
26
27pub const MACRO_ARGUMENTS: Option<&str> = Some("macro arguments");
28
29#[macro_use]
30pub mod parser;
31use parser::Parser;
32
33use crate::lexer::StripTokens;
34
35pub mod lexer;
36
37mod diagnostics;
38
39const _: () = {
41 let rustc_lexer = rustc_lexer::UNICODE_VERSION;
42 let rustc_span = rustc_span::UNICODE_VERSION;
43 let normalization = unicode_normalization::UNICODE_VERSION;
44 let width = unicode_width::UNICODE_VERSION;
45
46 if rustc_lexer.0 != rustc_span.0
47 || rustc_lexer.1 != rustc_span.1
48 || rustc_lexer.2 != rustc_span.2
49 {
50 {
::core::panicking::panic_fmt(format_args!("rustc_lexer and rustc_span must use the same Unicode version, `rustc_lexer::UNICODE_VERSION` and `rustc_span::UNICODE_VERSION` are different."));
};panic!(
51 "rustc_lexer and rustc_span must use the same Unicode version, \
52 `rustc_lexer::UNICODE_VERSION` and `rustc_span::UNICODE_VERSION` are \
53 different."
54 );
55 }
56
57 if rustc_lexer.0 != normalization.0
58 || rustc_lexer.1 != normalization.1
59 || rustc_lexer.2 != normalization.2
60 {
61 {
::core::panicking::panic_fmt(format_args!("rustc_lexer and unicode-normalization must use the same Unicode version, `rustc_lexer::UNICODE_VERSION` and `unicode_normalization::UNICODE_VERSION` are different."));
};panic!(
62 "rustc_lexer and unicode-normalization must use the same Unicode version, \
63 `rustc_lexer::UNICODE_VERSION` and `unicode_normalization::UNICODE_VERSION` are \
64 different."
65 );
66 }
67
68 if rustc_lexer.0 != width.0 || rustc_lexer.1 != width.1 || rustc_lexer.2 != width.2 {
69 {
::core::panicking::panic_fmt(format_args!("rustc_lexer and unicode-width must use the same Unicode version, `rustc_lexer::UNICODE_VERSION` and `unicode_width::UNICODE_VERSION` are different."));
};panic!(
70 "rustc_lexer and unicode-width must use the same Unicode version, \
71 `rustc_lexer::UNICODE_VERSION` and `unicode_width::UNICODE_VERSION` are \
72 different."
73 );
74 }
75};
76
77pub fn unwrap_or_emit_fatal<T>(expr: Result<T, Vec<Diag<'_>>>) -> T {
79 match expr {
80 Ok(expr) => expr,
81 Err(errs) => {
82 for err in errs {
83 err.emit();
84 }
85 FatalError.raise()
86 }
87 }
88}
89
90pub fn new_parser_from_source_str(
95 psess: &ParseSess,
96 name: FileName,
97 source: String,
98 strip_tokens: StripTokens,
99) -> Result<Parser<'_>, Vec<Diag<'_>>> {
100 let source_file = psess.source_map().new_source_file(name, source);
101 new_parser_from_source_file(psess, source_file, strip_tokens)
102}
103
104pub fn new_parser_from_file<'a>(
112 psess: &'a ParseSess,
113 path: &Path,
114 strip_tokens: StripTokens,
115 sp: Option<Span>,
116) -> Result<Parser<'a>, Vec<Diag<'a>>> {
117 let sm = psess.source_map();
118 let source_file = sm.load_file(path).unwrap_or_else(|e| {
119 use std::io::ErrorKind;
120
121 let msg = match e.kind() {
122 ErrorKind::NotFound => ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("couldn\'t find file `{0}`",
path.display()))
})format!("couldn't find file `{}`", path.display()),
123 ErrorKind::PermissionDenied => {
124 ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("permission denied when opening file `{0}`",
path.display()))
})format!("permission denied when opening file `{}`", path.display())
125 }
126 ErrorKind::IsADirectory => ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("`{0}` is a directory",
path.display()))
})format!("`{}` is a directory", path.display()),
127 _ => ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("couldn\'t read `{0}`: {1}",
path.display(), e))
})format!("couldn't read `{}`: {}", path.display(), e),
128 };
129
130 let mut err = psess.dcx().struct_fatal(msg);
131
132 if e.kind() == ErrorKind::NotFound {
133 if let Some(file_name) = path.file_name().and_then(|n| n.to_str()) {
134 let parent = match path.parent() {
135 Some(p) if !p.as_os_str().is_empty() => p,
136 _ => Path::new("."),
137 };
138 if let Ok(entries) = std::fs::read_dir(parent) {
139 let candidates: Vec<Symbol> = entries
140 .flatten()
141 .filter_map(|entry| entry.file_name().to_str().map(Symbol::intern))
142 .collect();
143 let lookup = Symbol::intern(file_name);
144 if let Some(suggestion) = find_best_match_for_name(&candidates, lookup, None) {
145 let suggested_path = if parent == Path::new(".") {
146 suggestion.as_str().to_string()
147 } else {
148 parent.join(suggestion.as_str()).display().to_string()
149 };
150 err.help(::alloc::__export::must_use({
::alloc::fmt::format(format_args!("you might have meant to open `{0}`",
suggested_path))
})format!("you might have meant to open `{}`", suggested_path));
151 }
152 }
153 }
154 }
155 if let Ok(contents) = std::fs::read(path)
156 && let Err(utf8err) = std::str::from_utf8(&contents)
157 {
158 utf8_error(sm, &path.display().to_string(), sp, &mut err, utf8err, &contents);
159 }
160 if let Some(sp) = sp {
161 err.span(sp);
162 }
163 err.emit()
164 });
165 new_parser_from_source_file(psess, source_file, strip_tokens)
166}
167
168pub fn utf8_error<E: EmissionGuarantee>(
169 sm: &SourceMap,
170 path: &str,
171 sp: Option<Span>,
172 err: &mut Diag<'_, E>,
173 utf8err: Utf8Error,
174 contents: &[u8],
175) {
176 let start = utf8err.valid_up_to();
178 let note = ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("invalid utf-8 at byte `{0}`",
start))
})format!("invalid utf-8 at byte `{start}`");
179 let msg = if let Some(len) = utf8err.error_len() {
180 ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("byte{1} `{0}` {2} not valid utf-8",
if len == 1 {
::alloc::__export::must_use({
::alloc::fmt::format(format_args!("{0:?}", contents[start]))
})
} else {
::alloc::__export::must_use({
::alloc::fmt::format(format_args!("{0:?}",
&contents[start..start + len]))
})
}, if len == 1 { "" } else { "s" },
if len == 1 { "is" } else { "are" }))
})format!(
181 "byte{s} `{bytes}` {are} not valid utf-8",
182 bytes = if len == 1 {
183 format!("{:?}", contents[start])
184 } else {
185 format!("{:?}", &contents[start..start + len])
186 },
187 s = pluralize!(len),
188 are = if len == 1 { "is" } else { "are" },
189 )
190 } else {
191 note.clone()
192 };
193 let contents = String::from_utf8_lossy(contents).to_string();
194
195 let filename = FileName::Real(
198 sm.path_mapping().to_real_filename(sm.working_dir(), PathBuf::from(path).as_path()),
199 );
200 let source = sm.new_source_file(filename, contents);
201
202 if start as u32 > source.normalized_source_len.0 {
204 err.note(note);
205 return;
206 }
207
208 let span = Span::with_root_ctxt(
209 source.normalized_byte_pos(start as u32),
210 source.normalized_byte_pos(start as u32),
211 );
212 if span.is_dummy() {
213 err.note(note);
214 } else {
215 if sp.is_some() {
216 err.span_note(span, msg);
217 } else {
218 err.span(span);
219 err.span_label(span, msg);
220 }
221 }
222}
223
224fn new_parser_from_source_file(
227 psess: &ParseSess,
228 source_file: Arc<SourceFile>,
229 strip_tokens: StripTokens,
230) -> Result<Parser<'_>, Vec<Diag<'_>>> {
231 let end_pos = source_file.end_position();
232 let stream = source_file_to_stream(psess, source_file, None, strip_tokens)?;
233 let mut parser = Parser::new(psess, stream, None);
234 if parser.token == token::Eof {
235 parser.token.span = Span::new(end_pos, end_pos, parser.token.span.ctxt(), None);
236 }
237 Ok(parser)
238}
239
240pub fn source_str_to_stream(
244 psess: &ParseSess,
245 name: FileName,
246 source: String,
247 override_span: Option<Span>,
248) -> Result<TokenStream, Vec<Diag<'_>>> {
249 let source_file = psess.source_map().new_source_file(name, source);
250 source_file_to_stream(psess, source_file, override_span, StripTokens::Shebang)
255}
256
257fn source_file_to_stream<'psess>(
261 psess: &'psess ParseSess,
262 source_file: Arc<SourceFile>,
263 override_span: Option<Span>,
264 strip_tokens: StripTokens,
265) -> Result<TokenStream, Vec<Diag<'psess>>> {
266 let src = source_file.src.as_ref().unwrap_or_else(|| {
267 psess.dcx().bug(::alloc::__export::must_use({
::alloc::fmt::format(format_args!("cannot lex `source_file` without source: {0}",
psess.source_map().filename_for_diagnostics(&source_file.name)))
})format!(
268 "cannot lex `source_file` without source: {}",
269 psess.source_map().filename_for_diagnostics(&source_file.name)
270 ));
271 });
272
273 lexer::lex_token_trees(psess, src.as_str(), source_file.start_pos, override_span, strip_tokens)
274}
275
276pub fn parse_in<'a, T>(
278 psess: &'a ParseSess,
279 tts: TokenStream,
280 name: &'static str,
281 mut f: impl FnMut(&mut Parser<'a>) -> PResult<'a, T>,
282) -> PResult<'a, T> {
283 let mut parser = Parser::new(psess, tts, Some(name));
284 let result = f(&mut parser)?;
285 if parser.token != token::Eof {
286 parser.unexpected()?;
287 }
288 Ok(result)
289}
290
291pub fn fake_token_stream_for_item(
292 psess: &ParseSess,
293 item: &ast::Item,
294 attr_to_exclude: Option<&ast::Attribute>,
295) -> TokenStream {
296 if let Some(tokens) = fake_token_stream_for_file_mod(psess, item, attr_to_exclude) {
297 return tokens;
298 }
299
300 let source = pprust::item_to_string(item);
301 let filename = FileName::macro_expansion_source_code(&source);
302 unwrap_or_emit_fatal(source_str_to_stream(psess, filename, source, Some(item.span)))
303}
304
305fn fake_token_stream_for_file_mod(
306 psess: &ParseSess,
307 item: &ast::Item,
308 attr_to_exclude: Option<&ast::Attribute>,
309) -> Option<TokenStream> {
310 let ast::ItemKind::Mod(_, _, ast::ModKind::Loaded(_, ast::Inline::No { .. }, spans)) =
311 &item.kind
312 else {
313 return None;
314 };
315
316 let attr = attr_to_exclude.expect("file modules must have an attribute to exclude");
317 {
match (&attr.style, &ast::AttrStyle::Inner) {
(left_val, right_val) => {
if !(*left_val == *right_val) {
let kind = ::core::panicking::AssertKind::Eq;
::core::panicking::assert_failed(kind, &*left_val,
&*right_val, ::core::option::Option::None);
}
}
}
};assert_eq!(attr.style, ast::AttrStyle::Inner);
318
319 let mut body_tts = Vec::new();
320 body_tts.extend(lex_token_trees_for_span(psess, spans.inner_span.until(attr.span))?);
321 body_tts.extend(lex_token_trees_for_span(
322 psess,
323 attr.span.between(spans.inner_span.shrink_to_hi()),
324 )?);
325
326 let mut wrapper_tts = Vec::new();
327 for attr in item.attrs.iter().filter(|attr| attr.style == ast::AttrStyle::Outer) {
328 wrapper_tts.extend(attr.token_trees());
329 }
330 wrapper_tts.extend(lex_token_trees_for_span(psess, item.span)?);
331 let Some(TokenTree::Token(semi, _)) = wrapper_tts.pop() else {
332 return None;
333 };
334 if semi.kind != token::Semi {
335 return None;
336 }
337 wrapper_tts.push(TokenTree::Delimited(
338 DelimSpan::from_single(semi.span),
339 DelimSpacing::new(Spacing::Alone, Spacing::Alone),
340 token::Delimiter::Brace,
341 TokenStream::new(body_tts),
342 ));
343
344 Some(TokenStream::new(wrapper_tts))
345}
346
347fn lex_token_trees_for_span(
348 psess: &ParseSess,
349 span: Span,
350) -> Option<impl Iterator<Item = TokenTree>> {
351 let src = psess.source_map().span_to_snippet(span).ok()?;
352 let stream = match lexer::lex_token_trees(psess, &src, span.lo(), None, StripTokens::Nothing) {
353 Ok(stream) => stream,
354 Err(errs) => {
355 errs.into_iter().for_each(|err| err.cancel());
356 return None;
357 }
358 };
359 Some((0..).map_while(move |index| stream.get(index).cloned()))
360}
361
362pub fn fake_token_stream_for_foreign_item(
363 psess: &ParseSess,
364 item: &ast::ForeignItem,
365) -> TokenStream {
366 let source = pprust::foreign_item_to_string(item);
367 let filename = FileName::macro_expansion_source_code(&source);
368 unwrap_or_emit_fatal(source_str_to_stream(psess, filename, source, Some(item.span)))
369}
370
371pub fn fake_token_stream_for_crate(psess: &ParseSess, krate: &ast::Crate) -> TokenStream {
372 let source = pprust::crate_to_string_for_macros(krate);
373 let filename = FileName::macro_expansion_source_code(&source);
374 unwrap_or_emit_fatal(source_str_to_stream(
375 psess,
376 filename,
377 source,
378 Some(krate.spans.inner_span),
379 ))
380}