1#![allow(internal_features)]
5#![allow(rustc::diagnostic_outside_of_impl)]
6#![allow(rustc::untranslatable_diagnostic)]
7#![feature(assert_matches)]
8#![feature(box_patterns)]
9#![feature(debug_closure_helpers)]
10#![feature(if_let_guard)]
11#![feature(iter_intersperse)]
12#![feature(string_from_utf8_lossy_owned)]
13#![recursion_limit = "256"]
14use std::path::{Path, PathBuf};
17use std::str::Utf8Error;
18use std::sync::Arc;
19
20use rustc_ast as ast;
21use rustc_ast::tokenstream::TokenStream;
22use rustc_ast::{AttrItem, Attribute, MetaItemInner, token};
23use rustc_ast_pretty::pprust;
24use rustc_errors::{Diag, EmissionGuarantee, FatalError, PResult, pluralize};
25use rustc_session::parse::ParseSess;
26use rustc_span::source_map::SourceMap;
27use rustc_span::{FileName, SourceFile, Span};
28pub use unicode_normalization::UNICODE_VERSION as UNICODE_NORMALIZATION_VERSION;
29
30pub const MACRO_ARGUMENTS: Option<&str> = Some("macro arguments");
31
32#[macro_use]
33pub mod parser;
34use parser::Parser;
35pub mod lexer;
36pub mod validate_attr;
37
38mod errors;
39
40rustc_fluent_macro::fluent_messages! { "../messages.ftl" }
41
42pub fn unwrap_or_emit_fatal<T>(expr: Result<T, Vec<Diag<'_>>>) -> T {
44 match expr {
45 Ok(expr) => expr,
46 Err(errs) => {
47 for err in errs {
48 err.emit();
49 }
50 FatalError.raise()
51 }
52 }
53}
54
55pub fn new_parser_from_source_str(
59 psess: &ParseSess,
60 name: FileName,
61 source: String,
62) -> Result<Parser<'_>, Vec<Diag<'_>>> {
63 let source_file = psess.source_map().new_source_file(name, source);
64 new_parser_from_source_file(psess, source_file)
65}
66
67pub fn new_parser_from_file<'a>(
73 psess: &'a ParseSess,
74 path: &Path,
75 sp: Option<Span>,
76) -> Result<Parser<'a>, Vec<Diag<'a>>> {
77 let sm = psess.source_map();
78 let source_file = sm.load_file(path).unwrap_or_else(|e| {
79 let msg = format!("couldn't read `{}`: {}", path.display(), e);
80 let mut err = psess.dcx().struct_fatal(msg);
81 if let Ok(contents) = std::fs::read(path)
82 && let Err(utf8err) = String::from_utf8(contents.clone())
83 {
84 utf8_error(
85 sm,
86 &path.display().to_string(),
87 sp,
88 &mut err,
89 utf8err.utf8_error(),
90 &contents,
91 );
92 }
93 if let Some(sp) = sp {
94 err.span(sp);
95 }
96 err.emit();
97 });
98 new_parser_from_source_file(psess, source_file)
99}
100
101pub fn utf8_error<E: EmissionGuarantee>(
102 sm: &SourceMap,
103 path: &str,
104 sp: Option<Span>,
105 err: &mut Diag<'_, E>,
106 utf8err: Utf8Error,
107 contents: &[u8],
108) {
109 let start = utf8err.valid_up_to();
111 let note = format!("invalid utf-8 at byte `{start}`");
112 let msg = if let Some(len) = utf8err.error_len() {
113 format!(
114 "byte{s} `{bytes}` {are} not valid utf-8",
115 bytes = if len == 1 {
116 format!("{:?}", contents[start])
117 } else {
118 format!("{:?}", &contents[start..start + len])
119 },
120 s = pluralize!(len),
121 are = if len == 1 { "is" } else { "are" },
122 )
123 } else {
124 note.clone()
125 };
126 let contents = String::from_utf8_lossy(contents).to_string();
127 let source = sm.new_source_file(PathBuf::from(path).into(), contents);
128 let span = Span::with_root_ctxt(
129 source.normalized_byte_pos(start as u32),
130 source.normalized_byte_pos(start as u32),
131 );
132 if span.is_dummy() {
133 err.note(note);
134 } else {
135 if sp.is_some() {
136 err.span_note(span, msg);
137 } else {
138 err.span(span);
139 err.span_label(span, msg);
140 }
141 }
142}
143
144fn new_parser_from_source_file(
147 psess: &ParseSess,
148 source_file: Arc<SourceFile>,
149) -> Result<Parser<'_>, Vec<Diag<'_>>> {
150 let end_pos = source_file.end_position();
151 let stream = source_file_to_stream(psess, source_file, None)?;
152 let mut parser = Parser::new(psess, stream, None);
153 if parser.token == token::Eof {
154 parser.token.span = Span::new(end_pos, end_pos, parser.token.span.ctxt(), None);
155 }
156 Ok(parser)
157}
158
159pub fn source_str_to_stream(
160 psess: &ParseSess,
161 name: FileName,
162 source: String,
163 override_span: Option<Span>,
164) -> Result<TokenStream, Vec<Diag<'_>>> {
165 let source_file = psess.source_map().new_source_file(name, source);
166 source_file_to_stream(psess, source_file, override_span)
167}
168
169fn source_file_to_stream<'psess>(
172 psess: &'psess ParseSess,
173 source_file: Arc<SourceFile>,
174 override_span: Option<Span>,
175) -> Result<TokenStream, Vec<Diag<'psess>>> {
176 let src = source_file.src.as_ref().unwrap_or_else(|| {
177 psess.dcx().bug(format!(
178 "cannot lex `source_file` without source: {}",
179 psess.source_map().filename_for_diagnostics(&source_file.name)
180 ));
181 });
182
183 lexer::lex_token_trees(psess, src.as_str(), source_file.start_pos, override_span)
184}
185
186pub fn parse_in<'a, T>(
188 psess: &'a ParseSess,
189 tts: TokenStream,
190 name: &'static str,
191 mut f: impl FnMut(&mut Parser<'a>) -> PResult<'a, T>,
192) -> PResult<'a, T> {
193 let mut parser = Parser::new(psess, tts, Some(name));
194 let result = f(&mut parser)?;
195 if parser.token != token::Eof {
196 parser.unexpected()?;
197 }
198 Ok(result)
199}
200
201pub fn fake_token_stream_for_item(psess: &ParseSess, item: &ast::Item) -> TokenStream {
202 let source = pprust::item_to_string(item);
203 let filename = FileName::macro_expansion_source_code(&source);
204 unwrap_or_emit_fatal(source_str_to_stream(psess, filename, source, Some(item.span)))
205}
206
207pub fn fake_token_stream_for_crate(psess: &ParseSess, krate: &ast::Crate) -> TokenStream {
208 let source = pprust::crate_to_string_for_macros(krate);
209 let filename = FileName::macro_expansion_source_code(&source);
210 unwrap_or_emit_fatal(source_str_to_stream(
211 psess,
212 filename,
213 source,
214 Some(krate.spans.inner_span),
215 ))
216}
217
218pub fn parse_cfg_attr(
219 cfg_attr: &Attribute,
220 psess: &ParseSess,
221) -> Option<(MetaItemInner, Vec<(AttrItem, Span)>)> {
222 const CFG_ATTR_GRAMMAR_HELP: &str = "#[cfg_attr(condition, attribute, other_attribute, ...)]";
223 const CFG_ATTR_NOTE_REF: &str = "for more information, visit \
224 <https://doc.rust-lang.org/reference/conditional-compilation.html#the-cfg_attr-attribute>";
225
226 match cfg_attr.get_normal_item().args {
227 ast::AttrArgs::Delimited(ast::DelimArgs { dspan, delim, ref tokens })
228 if !tokens.is_empty() =>
229 {
230 crate::validate_attr::check_cfg_attr_bad_delim(psess, dspan, delim);
231 match parse_in(psess, tokens.clone(), "`cfg_attr` input", |p| p.parse_cfg_attr()) {
232 Ok(r) => return Some(r),
233 Err(e) => {
234 e.with_help(format!("the valid syntax is `{CFG_ATTR_GRAMMAR_HELP}`"))
235 .with_note(CFG_ATTR_NOTE_REF)
236 .emit();
237 }
238 }
239 }
240 _ => {
241 psess.dcx().emit_err(errors::MalformedCfgAttr {
242 span: cfg_attr.span,
243 sugg: CFG_ATTR_GRAMMAR_HELP,
244 });
245 }
246 }
247 None
248}