rustc_parse/
lib.rs

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

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy