rust/compiler/rustc_parse/src/lib.rs

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

240 lines
7.8 KiB
Rust
Raw Normal View History

2019-02-08 13:53:55 +00:00
//! The main parser interface.
2012-11-29 00:20:41 +00:00
#![allow(internal_features)]
#![allow(rustc::diagnostic_outside_of_impl)]
#![allow(rustc::untranslatable_diagnostic)]
Implement token-based handling of attributes during expansion This PR modifies the macro expansion infrastructure to handle attributes in a fully token-based manner. As a result: * Derives macros no longer lose spans when their input is modified by eager cfg-expansion. This is accomplished by performing eager cfg-expansion on the token stream that we pass to the derive proc-macro * Inner attributes now preserve spans in all cases, including when we have multiple inner attributes in a row. This is accomplished through the following changes: * New structs `AttrAnnotatedTokenStream` and `AttrAnnotatedTokenTree` are introduced. These are very similar to a normal `TokenTree`, but they also track the position of attributes and attribute targets within the stream. They are built when we collect tokens during parsing. An `AttrAnnotatedTokenStream` is converted to a regular `TokenStream` when we invoke a macro. * Token capturing and `LazyTokenStream` are modified to work with `AttrAnnotatedTokenStream`. A new `ReplaceRange` type is introduced, which is created during the parsing of a nested AST node to make the 'outer' AST node aware of the attributes and attribute target stored deeper in the token stream. * When we need to perform eager cfg-expansion (either due to `#[derive]` or `#[cfg_eval]`), we tokenize and reparse our target, capturing additional information about the locations of `#[cfg]` and `#[cfg_attr]` attributes at any depth within the target. This is a performance optimization, allowing us to perform less work in the typical case where captured tokens never have eager cfg-expansion run.
2020-11-28 23:33:17 +00:00
#![feature(array_windows)]
#![feature(box_patterns)]
2021-08-16 15:29:49 +00:00
#![feature(if_let_guard)]
#![feature(iter_intersperse)]
#![feature(let_chains)]
2021-09-20 15:24:47 +00:00
#[macro_use]
extern crate tracing;
2020-04-27 17:56:11 +00:00
use rustc_ast as ast;
use rustc_ast::token;
use rustc_ast::tokenstream::TokenStream;
use rustc_ast::{AttrItem, Attribute, MetaItem};
use rustc_ast_pretty::pprust;
use rustc_data_structures::sync::Lrc;
use rustc_errors::{Diag, FatalError, PResult};
use rustc_session::parse::ParseSess;
use rustc_span::{FileName, SourceFile, Span};
2019-02-06 17:33:01 +00:00
2020-03-08 21:10:37 +00:00
use std::path::Path;
pub const MACRO_ARGUMENTS: Option<&str> = Some("macro arguments");
#[macro_use]
pub mod parser;
use parser::{make_unclosed_delims_error, Parser};
pub mod lexer;
pub mod validate_attr;
mod errors;
rustc_fluent_macro::fluent_messages! { "../messages.ftl" }
// A bunch of utility functions of the form `parse_<thing>_from_<source>`
2013-02-11 21:36:24 +00:00
// where <thing> includes crate, expr, item, stmt, tts, and one that
// uses a HOF to parse anything, and <source> includes file and
// `source_str`.
2013-02-11 21:36:24 +00:00
/// A variant of 'panictry!' that works on a `Vec<Diag>` instead of a single `Diag`.
2019-10-11 16:52:09 +00:00
macro_rules! panictry_buffer {
($e:expr) => {{
2019-10-11 16:52:09 +00:00
use std::result::Result::{Err, Ok};
match $e {
Ok(e) => e,
Err(errs) => {
for e in errs {
e.emit();
2019-10-11 16:52:09 +00:00
}
FatalError.raise()
}
}
}};
}
pub fn parse_crate_from_file<'a>(input: &Path, sess: &'a ParseSess) -> PResult<'a, ast::Crate> {
2020-03-21 22:10:10 +00:00
let mut parser = new_parser_from_file(sess, input, None);
parser.parse_crate_mod()
2012-11-29 00:20:41 +00:00
}
pub fn parse_crate_attrs_from_file<'a>(
input: &Path,
sess: &'a ParseSess,
) -> PResult<'a, ast::AttrVec> {
2020-03-21 22:10:10 +00:00
let mut parser = new_parser_from_file(sess, input, None);
parser.parse_inner_attributes()
}
pub fn parse_crate_from_source_str(
name: FileName,
source: String,
sess: &ParseSess,
2019-02-06 17:33:01 +00:00
) -> PResult<'_, ast::Crate> {
new_parser_from_source_str(sess, name, source).parse_crate_mod()
2012-11-29 00:20:41 +00:00
}
pub fn parse_crate_attrs_from_source_str(
name: FileName,
source: String,
sess: &ParseSess,
) -> PResult<'_, ast::AttrVec> {
new_parser_from_source_str(sess, name, source).parse_inner_attributes()
}
pub fn parse_stream_from_source_str(
name: FileName,
source: String,
sess: &ParseSess,
override_span: Option<Span>,
2019-03-04 20:59:43 +00:00
) -> TokenStream {
source_file_to_stream(sess, sess.source_map().new_source_file(name, source), override_span)
2012-11-29 00:20:41 +00:00
}
2019-02-08 13:53:55 +00:00
/// Creates a new parser from a source string.
pub fn new_parser_from_source_str(sess: &ParseSess, name: FileName, source: String) -> Parser<'_> {
panictry_buffer!(maybe_new_parser_from_source_str(sess, name, source))
2012-11-29 00:20:41 +00:00
}
2019-02-08 13:53:55 +00:00
/// Creates a new parser from a source string. Returns any buffered errors from lexing the initial
/// token stream; these must be consumed via `emit`, `cancel`, etc., otherwise a panic will occur
/// when they are dropped.
pub fn maybe_new_parser_from_source_str(
sess: &ParseSess,
name: FileName,
source: String,
) -> Result<Parser<'_>, Vec<Diag<'_>>> {
2020-03-08 12:36:20 +00:00
maybe_source_file_to_parser(sess, sess.source_map().new_source_file(name, source))
2012-11-29 00:20:41 +00:00
}
/// Creates a new parser, aborting if the file doesn't exist. If a span is given, that is used on
/// an error as the source of the problem.
2020-03-21 22:10:10 +00:00
pub fn new_parser_from_file<'a>(sess: &'a ParseSess, path: &Path, sp: Option<Span>) -> Parser<'a> {
let source_file = sess.source_map().load_file(path).unwrap_or_else(|e| {
let msg = format!("couldn't read {}: {}", path.display(), e);
let mut err = sess.dcx.struct_fatal(msg);
if let Some(sp) = sp {
err.span(sp);
}
err.emit();
});
2012-11-29 00:20:41 +00:00
panictry_buffer!(maybe_source_file_to_parser(sess, source_file))
2013-04-23 17:57:41 +00:00
}
/// Given a session and a `source_file`, return a parser. Returns any buffered errors from lexing
/// the initial token stream.
fn maybe_source_file_to_parser(
sess: &ParseSess,
source_file: Lrc<SourceFile>,
) -> Result<Parser<'_>, Vec<Diag<'_>>> {
let end_pos = source_file.end_position();
let stream = maybe_file_to_stream(sess, source_file, None)?;
let mut parser = stream_to_parser(sess, stream, None);
if parser.token == token::Eof {
2021-04-18 12:27:04 +00:00
parser.token.span = Span::new(end_pos, end_pos, parser.token.span.ctxt(), None);
}
Ok(parser)
2013-04-23 17:57:41 +00:00
}
// Base abstractions
2013-04-23 17:57:41 +00:00
/// Given a `source_file`, produces a sequence of token trees.
pub fn source_file_to_stream(
sess: &ParseSess,
source_file: Lrc<SourceFile>,
override_span: Option<Span>,
) -> TokenStream {
panictry_buffer!(maybe_file_to_stream(sess, source_file, override_span))
2013-04-23 17:57:41 +00:00
}
2019-02-08 13:53:55 +00:00
/// Given a source file, produces a sequence of token trees. Returns any buffered errors from
/// parsing the token stream.
fn maybe_file_to_stream<'sess>(
sess: &'sess ParseSess,
source_file: Lrc<SourceFile>,
override_span: Option<Span>,
) -> Result<TokenStream, Vec<Diag<'sess>>> {
let src = source_file.src.as_ref().unwrap_or_else(|| {
sess.dcx.bug(format!(
"cannot lex `source_file` without source: {}",
sess.source_map().filename_for_diagnostics(&source_file.name)
));
});
lexer::parse_token_trees(sess, src.as_str(), source_file.start_pos, override_span)
2013-04-23 17:57:41 +00:00
}
/// Given a stream and the `ParseSess`, produces a parser.
pub fn stream_to_parser<'a>(
sess: &'a ParseSess,
stream: TokenStream,
2019-05-22 05:17:53 +00:00
subparser_name: Option<&'static str>,
) -> Parser<'a> {
Parser::new(sess, stream, subparser_name)
2012-11-29 00:20:41 +00:00
}
2013-01-30 17:56:33 +00:00
2019-10-09 11:19:15 +00:00
/// Runs the given subparser `f` on the tokens of the given `attr`'s item.
2019-12-05 05:45:50 +00:00
pub fn parse_in<'a, T>(
2019-10-09 11:19:15 +00:00
sess: &'a ParseSess,
2019-12-05 05:45:50 +00:00
tts: TokenStream,
name: &'static str,
2019-10-09 11:19:15 +00:00
mut f: impl FnMut(&mut Parser<'a>) -> PResult<'a, T>,
) -> PResult<'a, T> {
let mut parser = Parser::new(sess, tts, Some(name));
2019-10-09 11:19:15 +00:00
let result = f(&mut parser)?;
if parser.token != token::Eof {
parser.unexpected()?;
}
Ok(result)
}
pub fn fake_token_stream_for_item(sess: &ParseSess, item: &ast::Item) -> TokenStream {
let source = pprust::item_to_string(item);
let filename = FileName::macro_expansion_source_code(&source);
parse_stream_from_source_str(filename, source, sess, Some(item.span))
}
pub fn fake_token_stream_for_crate(sess: &ParseSess, krate: &ast::Crate) -> TokenStream {
let source = pprust::crate_to_string_for_macros(krate);
let filename = FileName::macro_expansion_source_code(&source);
2022-03-03 23:45:25 +00:00
parse_stream_from_source_str(filename, source, sess, Some(krate.spans.inner_span))
}
pub fn parse_cfg_attr(
attr: &Attribute,
parse_sess: &ParseSess,
) -> Option<(MetaItem, Vec<(AttrItem, Span)>)> {
match attr.get_normal_item().args {
ast::AttrArgs::Delimited(ast::DelimArgs { dspan, delim, ref tokens })
if !tokens.is_empty() =>
{
crate::validate_attr::check_cfg_attr_bad_delim(parse_sess, dspan, delim);
match parse_in(parse_sess, tokens.clone(), "`cfg_attr` input", |p| p.parse_cfg_attr()) {
Ok(r) => return Some(r),
Make `DiagnosticBuilder::emit` consuming. This works for most of its call sites. This is nice, because `emit` very much makes sense as a consuming operation -- indeed, `DiagnosticBuilderState` exists to ensure no diagnostic is emitted twice, but it uses runtime checks. For the small number of call sites where a consuming emit doesn't work, the commit adds `DiagnosticBuilder::emit_without_consuming`. (This will be removed in subsequent commits.) Likewise, `emit_unless` becomes consuming. And `delay_as_bug` becomes consuming, while `delay_as_bug_without_consuming` is added (which will also be removed in subsequent commits.) All this requires significant changes to `DiagnosticBuilder`'s chaining methods. Currently `DiagnosticBuilder` method chaining uses a non-consuming `&mut self -> &mut Self` style, which allows chaining to be used when the chain ends in `emit()`, like so: ``` struct_err(msg).span(span).emit(); ``` But it doesn't work when producing a `DiagnosticBuilder` value, requiring this: ``` let mut err = self.struct_err(msg); err.span(span); err ``` This style of chaining won't work with consuming `emit` though. For that, we need to use to a `self -> Self` style. That also would allow `DiagnosticBuilder` production to be chained, e.g.: ``` self.struct_err(msg).span(span) ``` However, removing the `&mut self -> &mut Self` style would require that individual modifications of a `DiagnosticBuilder` go from this: ``` err.span(span); ``` to this: ``` err = err.span(span); ``` There are *many* such places. I have a high tolerance for tedious refactorings, but even I gave up after a long time trying to convert them all. Instead, this commit has it both ways: the existing `&mut self -> Self` chaining methods are kept, and new `self -> Self` chaining methods are added, all of which have a `_mv` suffix (short for "move"). Changes to the existing `forward!` macro lets this happen with very little additional boilerplate code. I chose to add the suffix to the new chaining methods rather than the existing ones, because the number of changes required is much smaller that way. This doubled chainging is a bit clumsy, but I think it is worthwhile because it allows a *lot* of good things to subsequently happen. In this commit, there are many `mut` qualifiers removed in places where diagnostics are emitted without being modified. In subsequent commits: - chaining can be used more, making the code more concise; - more use of chaining also permits the removal of redundant diagnostic APIs like `struct_err_with_code`, which can be replaced easily with `struct_err` + `code_mv`; - `emit_without_diagnostic` can be removed, which simplifies a lot of machinery, removing the need for `DiagnosticBuilderState`.
2024-01-03 01:17:35 +00:00
Err(e) => {
e.with_help(format!("the valid syntax is `{CFG_ATTR_GRAMMAR_HELP}`"))
.with_note(CFG_ATTR_NOTE_REF)
.emit();
}
}
}
_ => error_malformed_cfg_attr_missing(attr.span, parse_sess),
}
None
}
const CFG_ATTR_GRAMMAR_HELP: &str = "#[cfg_attr(condition, attribute, other_attribute, ...)]";
const CFG_ATTR_NOTE_REF: &str = "for more information, visit \
<https://doc.rust-lang.org/reference/conditional-compilation.html\
#the-cfg_attr-attribute>";
fn error_malformed_cfg_attr_missing(span: Span, parse_sess: &ParseSess) {
parse_sess.dcx.emit_err(errors::MalformedCfgAttr { span, sugg: CFG_ATTR_GRAMMAR_HELP });
}