diff options
Diffstat (limited to 'compiler/rustc_expand')
-rw-r--r-- | compiler/rustc_expand/Cargo.toml | 1 | ||||
-rw-r--r-- | compiler/rustc_expand/src/base.rs | 9 | ||||
-rw-r--r-- | compiler/rustc_expand/src/build.rs | 11 | ||||
-rw-r--r-- | compiler/rustc_expand/src/config.rs | 23 | ||||
-rw-r--r-- | compiler/rustc_expand/src/expand.rs | 35 | ||||
-rw-r--r-- | compiler/rustc_expand/src/lib.rs | 1 | ||||
-rw-r--r-- | compiler/rustc_expand/src/mbe/diagnostics.rs | 5 | ||||
-rw-r--r-- | compiler/rustc_expand/src/mbe/macro_check.rs | 4 | ||||
-rw-r--r-- | compiler/rustc_expand/src/mbe/macro_parser.rs | 16 | ||||
-rw-r--r-- | compiler/rustc_expand/src/mbe/macro_rules.rs | 56 | ||||
-rw-r--r-- | compiler/rustc_expand/src/mbe/metavar_expr.rs | 23 | ||||
-rw-r--r-- | compiler/rustc_expand/src/mbe/quoted.rs | 44 | ||||
-rw-r--r-- | compiler/rustc_expand/src/mbe/transcribe.rs | 15 | ||||
-rw-r--r-- | compiler/rustc_expand/src/parse/tests.rs | 21 | ||||
-rw-r--r-- | compiler/rustc_expand/src/placeholders.rs | 3 | ||||
-rw-r--r-- | compiler/rustc_expand/src/proc_macro.rs | 4 | ||||
-rw-r--r-- | compiler/rustc_expand/src/proc_macro_server.rs | 6 | ||||
-rw-r--r-- | compiler/rustc_expand/src/tests.rs | 76 |
18 files changed, 209 insertions, 144 deletions
diff --git a/compiler/rustc_expand/Cargo.toml b/compiler/rustc_expand/Cargo.toml index 2dae0e3f5..02da5b5dc 100644 --- a/compiler/rustc_expand/Cargo.toml +++ b/compiler/rustc_expand/Cargo.toml @@ -27,3 +27,4 @@ rustc_span = { path = "../rustc_span" } smallvec = { version = "1.8.1", features = ["union", "may_dangle"] } thin-vec = "0.2.12" tracing = "0.1" +termcolor = "1.2" diff --git a/compiler/rustc_expand/src/base.rs b/compiler/rustc_expand/src/base.rs index 8a251ea29..c4d2a374f 100644 --- a/compiler/rustc_expand/src/base.rs +++ b/compiler/rustc_expand/src/base.rs @@ -18,6 +18,7 @@ use rustc_errors::{ Applicability, DiagnosticBuilder, DiagnosticMessage, ErrorGuaranteed, IntoDiagnostic, MultiSpan, PResult, }; +use rustc_feature::Features; use rustc_lint_defs::builtin::PROC_MACRO_BACK_COMPAT; use rustc_lint_defs::{BufferedEarlyLint, BuiltinLintDiagnostics, RegisteredTools}; use rustc_parse::{self, parser, MACRO_ARGUMENTS}; @@ -767,6 +768,7 @@ impl SyntaxExtension { /// and other properties converted from attributes. pub fn new( sess: &Session, + features: &Features, kind: SyntaxExtensionKind, span: Span, helper_attrs: Vec<Symbol>, @@ -816,7 +818,7 @@ impl SyntaxExtension { allow_internal_unstable: (!allow_internal_unstable.is_empty()) .then(|| allow_internal_unstable.into()), stability: stability.map(|(s, _)| s), - deprecation: attr::find_deprecation(&sess, attrs).map(|(d, _)| d), + deprecation: attr::find_deprecation(&sess, features, attrs).map(|(d, _)| d), helper_attrs, edition, builtin_name, @@ -957,6 +959,7 @@ pub trait LintStoreExpand { fn pre_expansion_lint( &self, sess: &Session, + features: &Features, registered_tools: &RegisteredTools, node_id: NodeId, attrs: &[Attribute], @@ -1147,7 +1150,7 @@ impl<'a> ExtCtxt<'a> { pub fn span_warn<S: Into<MultiSpan>>(&self, sp: S, msg: impl Into<DiagnosticMessage>) { self.sess.parse_sess.span_diagnostic.span_warn(sp, msg); } - pub fn span_bug<S: Into<MultiSpan>>(&self, sp: S, msg: impl Into<DiagnosticMessage>) -> ! { + pub fn span_bug<S: Into<MultiSpan>>(&self, sp: S, msg: impl Into<String>) -> ! { self.sess.parse_sess.span_diagnostic.span_bug(sp, msg); } pub fn trace_macros_diag(&mut self) { @@ -1366,7 +1369,7 @@ pub fn parse_macro_name_and_helper_attrs( return None; } let Some(trait_attr) = list[0].meta_item() else { - diag.emit_err(errors::NotAMetaItem {span: list[0].span()}); + diag.emit_err(errors::NotAMetaItem { span: list[0].span() }); return None; }; let trait_ident = match trait_attr.ident() { diff --git a/compiler/rustc_expand/src/build.rs b/compiler/rustc_expand/src/build.rs index 264f30fb1..7de469944 100644 --- a/compiler/rustc_expand/src/build.rs +++ b/compiler/rustc_expand/src/build.rs @@ -643,7 +643,16 @@ impl<'a> ExtCtxt<'a> { span, name, AttrVec::new(), - ast::ItemKind::Const(ast::ConstItem { defaultness, ty, expr: Some(expr) }.into()), + ast::ItemKind::Const( + ast::ConstItem { + defaultness, + // FIXME(generic_const_items): Pass the generics as a parameter. + generics: ast::Generics::default(), + ty, + expr: Some(expr), + } + .into(), + ), ) } diff --git a/compiler/rustc_expand/src/config.rs b/compiler/rustc_expand/src/config.rs index 3e43eae00..8658cea13 100644 --- a/compiler/rustc_expand/src/config.rs +++ b/compiler/rustc_expand/src/config.rs @@ -313,9 +313,10 @@ impl<'a> StripUnconfigured<'a> { /// the attribute is incorrect. pub(crate) fn expand_cfg_attr(&self, attr: &Attribute, recursive: bool) -> Vec<Attribute> { let Some((cfg_predicate, expanded_attrs)) = - rustc_parse::parse_cfg_attr(attr, &self.sess.parse_sess) else { - return vec![]; - }; + rustc_parse::parse_cfg_attr(attr, &self.sess.parse_sess) + else { + return vec![]; + }; // Lint on zero attributes in source. if expanded_attrs.is_empty() { @@ -364,17 +365,21 @@ impl<'a> StripUnconfigured<'a> { // Use the `#` in `#[cfg_attr(pred, attr)]` as the `#` token // for `attr` when we expand it to `#[attr]` - let mut orig_trees = orig_tokens.into_trees(); - let TokenTree::Token(pound_token @ Token { kind: TokenKind::Pound, .. }, _) = orig_trees.next().unwrap() else { - panic!("Bad tokens for attribute {:?}", attr); + let mut orig_trees = orig_tokens.trees(); + let TokenTree::Token(pound_token @ Token { kind: TokenKind::Pound, .. }, _) = + orig_trees.next().unwrap().clone() + else { + panic!("Bad tokens for attribute {attr:?}"); }; let pound_span = pound_token.span; let mut trees = vec![AttrTokenTree::Token(pound_token, Spacing::Alone)]; if attr.style == AttrStyle::Inner { // For inner attributes, we do the same thing for the `!` in `#![some_attr]` - let TokenTree::Token(bang_token @ Token { kind: TokenKind::Not, .. }, _) = orig_trees.next().unwrap() else { - panic!("Bad tokens for attribute {:?}", attr); + let TokenTree::Token(bang_token @ Token { kind: TokenKind::Not, .. }, _) = + orig_trees.next().unwrap().clone() + else { + panic!("Bad tokens for attribute {attr:?}"); }; trees.push(AttrTokenTree::Token(bang_token, Spacing::Alone)); } @@ -385,7 +390,7 @@ impl<'a> StripUnconfigured<'a> { Delimiter::Bracket, item.tokens .as_ref() - .unwrap_or_else(|| panic!("Missing tokens for {:?}", item)) + .unwrap_or_else(|| panic!("Missing tokens for {item:?}")) .to_attr_token_stream(), ); trees.push(bracket_group); diff --git a/compiler/rustc_expand/src/expand.rs b/compiler/rustc_expand/src/expand.rs index 9850723a8..34d16bf00 100644 --- a/compiler/rustc_expand/src/expand.rs +++ b/compiler/rustc_expand/src/expand.rs @@ -651,7 +651,8 @@ impl<'a, 'b> MacroExpander<'a, 'b> { ExpandResult::Ready(match invoc.kind { InvocationKind::Bang { mac, .. } => match ext { SyntaxExtensionKind::Bang(expander) => { - let Ok(tok_result) = expander.expand(self.cx, span, mac.args.tokens.clone()) else { + let Ok(tok_result) = expander.expand(self.cx, span, mac.args.tokens.clone()) + else { return ExpandResult::Ready(fragment_kind.dummy(span)); }; self.parse_ast_fragment(tok_result, fragment_kind, &mac.path, span) @@ -704,7 +705,8 @@ impl<'a, 'b> MacroExpander<'a, 'b> { self.cx.emit_err(UnsupportedKeyValue { span }); } let inner_tokens = attr_item.args.inner_tokens(); - let Ok(tok_result) = expander.expand(self.cx, span, inner_tokens, tokens) else { + let Ok(tok_result) = expander.expand(self.cx, span, inner_tokens, tokens) + else { return ExpandResult::Ready(fragment_kind.dummy(span)); }; self.parse_ast_fragment(tok_result, fragment_kind, &attr_item.path, span) @@ -794,14 +796,14 @@ impl<'a, 'b> MacroExpander<'a, 'b> { | Annotatable::FieldDef(..) | Annotatable::Variant(..) => panic!("unexpected annotatable"), }; - if self.cx.ecfg.proc_macro_hygiene() { + if self.cx.ecfg.features.proc_macro_hygiene { return; } feature_err( &self.cx.sess.parse_sess, sym::proc_macro_hygiene, span, - format!("custom attributes cannot be applied to {}", kind), + format!("custom attributes cannot be applied to {kind}"), ) .emit(); } @@ -832,7 +834,7 @@ impl<'a, 'b> MacroExpander<'a, 'b> { } } - if !self.cx.ecfg.proc_macro_hygiene() { + if !self.cx.ecfg.features.proc_macro_hygiene { annotatable .visit_with(&mut GateProcMacroInput { parse_sess: &self.cx.sess.parse_sess }); } @@ -1087,9 +1089,7 @@ impl InvocationCollectorNode for P<ast::Item> { // Work around borrow checker not seeing through `P`'s deref. let (ident, span, mut attrs) = (node.ident, node.span, mem::take(&mut node.attrs)); - let ItemKind::Mod(_, mod_kind) = &mut node.kind else { - unreachable!() - }; + let ItemKind::Mod(_, mod_kind) = &mut node.kind else { unreachable!() }; let ecx = &mut collector.cx; let (file_path, dir_path, dir_ownership) = match mod_kind { @@ -1122,6 +1122,7 @@ impl InvocationCollectorNode for P<ast::Item> { if let Some(lint_store) = ecx.lint_store { lint_store.pre_expansion_lint( ecx.sess, + ecx.ecfg.features, ecx.resolver.registered_tools(), ecx.current_expansion.lint_node_id, &attrs, @@ -1580,7 +1581,7 @@ impl<'a, 'b> InvocationCollector<'a, 'b> { fn cfg(&self) -> StripUnconfigured<'_> { StripUnconfigured { sess: &self.cx.sess, - features: self.cx.ecfg.features, + features: Some(self.cx.ecfg.features), config_tokens: false, lint_node_id: self.cx.current_expansion.lint_node_id, } @@ -1676,7 +1677,7 @@ impl<'a, 'b> InvocationCollector<'a, 'b> { // Detect use of feature-gated or invalid attributes on macro invocations // since they will not be detected after macro expansion. fn check_attributes(&self, attrs: &[ast::Attribute], call: &ast::MacCall) { - let features = self.cx.ecfg.features.unwrap(); + let features = self.cx.ecfg.features; let mut attrs = attrs.iter().peekable(); let mut span: Option<Span> = None; while let Some(attr) = attrs.next() { @@ -1707,7 +1708,7 @@ impl<'a, 'b> InvocationCollector<'a, 'b> { &UNUSED_ATTRIBUTES, attr.span, self.cx.current_expansion.lint_node_id, - format!("unused attribute `{}`", attr_name), + format!("unused attribute `{attr_name}`"), BuiltinLintDiagnostics::UnusedBuiltinAttribute { attr_name, macro_name: pprust::path_to_string(&call.path), @@ -1976,7 +1977,7 @@ impl<'a, 'b> MutVisitor for InvocationCollector<'a, 'b> { pub struct ExpansionConfig<'feat> { pub crate_name: String, - pub features: Option<&'feat Features>, + pub features: &'feat Features, pub recursion_limit: Limit, pub trace_mac: bool, /// If false, strip `#[test]` nodes @@ -1987,11 +1988,11 @@ pub struct ExpansionConfig<'feat> { pub proc_macro_backtrace: bool, } -impl<'feat> ExpansionConfig<'feat> { - pub fn default(crate_name: String) -> ExpansionConfig<'static> { +impl ExpansionConfig<'_> { + pub fn default(crate_name: String, features: &Features) -> ExpansionConfig<'_> { ExpansionConfig { crate_name, - features: None, + features, recursion_limit: Limit::new(1024), trace_mac: false, should_test: false, @@ -1999,8 +2000,4 @@ impl<'feat> ExpansionConfig<'feat> { proc_macro_backtrace: false, } } - - fn proc_macro_hygiene(&self) -> bool { - self.features.is_some_and(|features| features.proc_macro_hygiene) - } } diff --git a/compiler/rustc_expand/src/lib.rs b/compiler/rustc_expand/src/lib.rs index 83a5043b0..c4a9b2ace 100644 --- a/compiler/rustc_expand/src/lib.rs +++ b/compiler/rustc_expand/src/lib.rs @@ -11,6 +11,7 @@ #![feature(try_blocks)] #![recursion_limit = "256"] #![deny(rustc::untranslatable_diagnostic)] +#![cfg_attr(not(bootstrap), allow(internal_features))] #[macro_use] extern crate rustc_macros; diff --git a/compiler/rustc_expand/src/mbe/diagnostics.rs b/compiler/rustc_expand/src/mbe/diagnostics.rs index 3593bed2d..e06037564 100644 --- a/compiler/rustc_expand/src/mbe/diagnostics.rs +++ b/compiler/rustc_expand/src/mbe/diagnostics.rs @@ -42,7 +42,8 @@ pub(super) fn failed_to_match_macro<'cx>( return result; } - let Some(BestFailure { token, msg: label, remaining_matcher, .. }) = tracker.best_failure else { + let Some(BestFailure { token, msg: label, remaining_matcher, .. }) = tracker.best_failure + else { return DummyResult::any(sp); }; @@ -256,7 +257,7 @@ pub(super) fn emit_frag_parse_err( e.span_suggestion_verbose( site_span, "surround the macro invocation with `{}` to interpret the expansion as a statement", - format!("{{ {}; }}", snippet), + format!("{{ {snippet}; }}"), Applicability::MaybeIncorrect, ); } diff --git a/compiler/rustc_expand/src/mbe/macro_check.rs b/compiler/rustc_expand/src/mbe/macro_check.rs index 34f998274..95f5bb2d2 100644 --- a/compiler/rustc_expand/src/mbe/macro_check.rs +++ b/compiler/rustc_expand/src/mbe/macro_check.rs @@ -593,7 +593,7 @@ fn check_ops_is_prefix( return; } } - buffer_lint(sess, span.into(), node_id, format!("unknown macro variable `{}`", name)); + buffer_lint(sess, span.into(), node_id, format!("unknown macro variable `{name}`")); } /// Returns whether `binder_ops` is a prefix of `occurrence_ops`. @@ -626,7 +626,7 @@ fn ops_is_prefix( if i >= occurrence_ops.len() { let mut span = MultiSpan::from_span(span); span.push_span_label(binder.span, "expected repetition"); - let message = format!("variable '{}' is still repeating at this depth", name); + let message = format!("variable '{name}' is still repeating at this depth"); buffer_lint(sess, span, node_id, message); return; } diff --git a/compiler/rustc_expand/src/mbe/macro_parser.rs b/compiler/rustc_expand/src/mbe/macro_parser.rs index f0e67cfd5..7e85beaad 100644 --- a/compiler/rustc_expand/src/mbe/macro_parser.rs +++ b/compiler/rustc_expand/src/mbe/macro_parser.rs @@ -81,7 +81,7 @@ use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::sync::Lrc; use rustc_errors::ErrorGuaranteed; use rustc_lint_defs::pluralize; -use rustc_parse::parser::{NtOrTt, Parser}; +use rustc_parse::parser::{ParseNtResult, Parser}; use rustc_span::symbol::Ident; use rustc_span::symbol::MacroRulesNormalizedIdent; use rustc_span::Span; @@ -156,7 +156,7 @@ impl Display for MatcherLoc { MatcherLoc::MetaVarDecl { bind, kind, .. } => { write!(f, "meta-variable `${bind}")?; if let Some(kind) = kind { - write!(f, ":{}", kind)?; + write!(f, ":{kind}")?; } write!(f, "`")?; Ok(()) @@ -692,8 +692,8 @@ impl TtParser { Ok(nt) => nt, }; let m = match nt { - NtOrTt::Nt(nt) => MatchedNonterminal(Lrc::new(nt)), - NtOrTt::Tt(tt) => MatchedTokenTree(tt), + ParseNtResult::Nt(nt) => MatchedNonterminal(Lrc::new(nt)), + ParseNtResult::Tt(tt) => MatchedTokenTree(tt), }; mp.push_match(next_metavar, seq_depth, m); mp.idx += 1; @@ -723,7 +723,7 @@ impl TtParser { .iter() .map(|mp| match &matcher[mp.idx] { MatcherLoc::MetaVarDecl { bind, kind: Some(kind), .. } => { - format!("{} ('{}')", kind, bind) + format!("{kind} ('{bind}')") } _ => unreachable!(), }) @@ -736,8 +736,8 @@ impl TtParser { "local ambiguity when calling macro `{}`: multiple parsing options: {}", self.macro_name, match self.next_mps.len() { - 0 => format!("built-in NTs {}.", nts), - n => format!("built-in NTs {} or {n} other option{s}.", nts, s = pluralize!(n)), + 0 => format!("built-in NTs {nts}."), + n => format!("built-in NTs {nts} or {n} other option{s}.", s = pluralize!(n)), } ), ) @@ -757,7 +757,7 @@ impl TtParser { match ret_val.entry(MacroRulesNormalizedIdent::new(bind)) { Vacant(spot) => spot.insert(res.next().unwrap()), Occupied(..) => { - return Error(span, format!("duplicated bind name: {}", bind)); + return Error(span, format!("duplicated bind name: {bind}")); } }; } else { diff --git a/compiler/rustc_expand/src/mbe/macro_rules.rs b/compiler/rustc_expand/src/mbe/macro_rules.rs index 42cc0a6b1..a5959d68f 100644 --- a/compiler/rustc_expand/src/mbe/macro_rules.rs +++ b/compiler/rustc_expand/src/mbe/macro_rules.rs @@ -16,6 +16,7 @@ use rustc_ast_pretty::pprust; use rustc_attr::{self as attr, TransparencyError}; use rustc_data_structures::fx::{FxHashMap, FxIndexMap}; use rustc_errors::{Applicability, ErrorGuaranteed}; +use rustc_feature::Features; use rustc_lint_defs::builtin::{ RUST_2021_INCOMPATIBLE_OR_PATTERNS, SEMICOLON_IN_EXPRESSIONS_FROM_MACROS, }; @@ -249,7 +250,7 @@ fn expand_macro<'cx>( trace_macros_note(&mut cx.expansions, sp, msg); } - let p = Parser::new(sess, tts, false, None); + let p = Parser::new(sess, tts, None); if is_local { cx.resolver.record_macro_rule_usage(node_id, i); @@ -257,7 +258,7 @@ fn expand_macro<'cx>( // Let the context choose how to interpret the result. // Weird, but useful for X-macros. - return Box::new(ParserAnyMacro { + Box::new(ParserAnyMacro { parser: p, // Pass along the original expansion site and the name of the macro @@ -269,18 +270,17 @@ fn expand_macro<'cx>( is_trailing_mac: cx.current_expansion.is_trailing_mac, arm_span, is_local, - }); + }) } Err(CanRetry::No(_)) => { debug!("Will not retry matching as an error was emitted already"); - return DummyResult::any(sp); + DummyResult::any(sp) } Err(CanRetry::Yes) => { - // Retry and emit a better error below. + // Retry and emit a better error. + diagnostics::failed_to_match_macro(cx, sp, def_span, name, arg, lhses) } } - - diagnostics::failed_to_match_macro(cx, sp, def_span, name, arg, lhses) } pub(super) enum CanRetry { @@ -376,6 +376,7 @@ pub(super) fn try_match_macro<'matcher, T: Tracker<'matcher>>( /// Converts a macro item into a syntax extension. pub fn compile_declarative_macro( sess: &Session, + features: &Features, def: &ast::Item, edition: Edition, ) -> (SyntaxExtension, Vec<(usize, Span)>) { @@ -383,6 +384,7 @@ pub fn compile_declarative_macro( let mk_syn_ext = |expander| { SyntaxExtension::new( sess, + features, SyntaxExtensionKind::LegacyBang(expander), def.span, Vec::new(), @@ -447,7 +449,7 @@ pub fn compile_declarative_macro( let create_parser = || { let body = macro_def.body.tokens.clone(); - Parser::new(&sess.parse_sess, body, true, rustc_parse::MACRO_ARGUMENTS) + Parser::new(&sess.parse_sess, body, rustc_parse::MACRO_ARGUMENTS) }; let parser = create_parser(); @@ -457,8 +459,8 @@ pub fn compile_declarative_macro( match tt_parser.parse_tt(&mut Cow::Owned(parser), &argument_gram, &mut NoopTracker) { Success(m) => m, Failure(()) => { - // The fast `NoopTracker` doesn't have any info on failure, so we need to retry it with another one - // that gives us the information we need. + // The fast `NoopTracker` doesn't have any info on failure, so we need to retry it + // with another one that gives us the information we need. // For this we need to reclone the macro body as the previous parser consumed it. let retry_parser = create_parser(); @@ -500,11 +502,11 @@ pub fn compile_declarative_macro( .map(|m| { if let MatchedTokenTree(tt) = m { let tt = mbe::quoted::parse( - TokenStream::new(vec![tt.clone()]), + &TokenStream::new(vec![tt.clone()]), true, &sess.parse_sess, def.id, - sess.features_untracked(), + features, edition, ) .pop() @@ -524,11 +526,11 @@ pub fn compile_declarative_macro( .map(|m| { if let MatchedTokenTree(tt) = m { return mbe::quoted::parse( - TokenStream::new(vec![tt.clone()]), + &TokenStream::new(vec![tt.clone()]), false, &sess.parse_sess, def.id, - sess.features_untracked(), + features, edition, ) .pop() @@ -554,7 +556,7 @@ pub fn compile_declarative_macro( let (transparency, transparency_error) = attr::find_transparency(&def.attrs, macro_rules); match transparency_error { Some(TransparencyError::UnknownTransparency(value, span)) => { - diag.span_err(span, format!("unknown macro transparency: `{}`", value)); + diag.span_err(span, format!("unknown macro transparency: `{value}`")); } Some(TransparencyError::MultipleTransparencyAttrs(old_span, new_span)) => { diag.span_err(vec![old_span, new_span], "multiple macro transparency attributes"); @@ -1197,10 +1199,10 @@ fn check_matcher_core<'tt>( may_be = may_be ), ); - err.span_label(sp, format!("not allowed after `{}` fragments", kind)); + err.span_label(sp, format!("not allowed after `{kind}` fragments")); if kind == NonterminalKind::PatWithOr - && sess.edition.rust_2021() + && sess.edition.at_least_rust_2021() && next_token.is_token(&BinOp(token::BinOpToken::Or)) { let suggestion = quoted_tt_to_string(&TokenTree::MetaVarDecl( @@ -1221,8 +1223,7 @@ fn check_matcher_core<'tt>( &[] => {} &[t] => { err.note(format!( - "only {} is allowed after `{}` fragments", - t, kind, + "only {t} is allowed after `{kind}` fragments", )); } ts => { @@ -1327,7 +1328,7 @@ fn is_in_follow(tok: &mbe::TokenTree, kind: NonterminalKind) -> IsInFollow { _ => IsInFollow::No(TOKENS), } } - NonterminalKind::PatWithOr { .. } => { + NonterminalKind::PatWithOr => { const TOKENS: &[&str] = &["`=>`", "`,`", "`=`", "`if`", "`in`"]; match tok { TokenTree::Token(token) => match token.kind { @@ -1407,9 +1408,9 @@ fn is_in_follow(tok: &mbe::TokenTree, kind: NonterminalKind) -> IsInFollow { fn quoted_tt_to_string(tt: &mbe::TokenTree) -> String { match tt { mbe::TokenTree::Token(token) => pprust::token_to_string(&token).into(), - mbe::TokenTree::MetaVar(_, name) => format!("${}", name), - mbe::TokenTree::MetaVarDecl(_, name, Some(kind)) => format!("${}:{}", name, kind), - mbe::TokenTree::MetaVarDecl(_, name, None) => format!("${}:", name), + mbe::TokenTree::MetaVar(_, name) => format!("${name}"), + mbe::TokenTree::MetaVarDecl(_, name, Some(kind)) => format!("${name}:{kind}"), + mbe::TokenTree::MetaVarDecl(_, name, None) => format!("${name}:"), _ => panic!( "{}", "unexpected mbe::TokenTree::{Sequence or Delimited} \ @@ -1418,6 +1419,11 @@ fn quoted_tt_to_string(tt: &mbe::TokenTree) -> String { } } -pub(super) fn parser_from_cx(sess: &ParseSess, tts: TokenStream, recovery: Recovery) -> Parser<'_> { - Parser::new(sess, tts, true, rustc_parse::MACRO_ARGUMENTS).recovery(recovery) +pub(super) fn parser_from_cx( + sess: &ParseSess, + mut tts: TokenStream, + recovery: Recovery, +) -> Parser<'_> { + tts.desugar_doc_comments(); + Parser::new(sess, tts, rustc_parse::MACRO_ARGUMENTS).recovery(recovery) } diff --git a/compiler/rustc_expand/src/mbe/metavar_expr.rs b/compiler/rustc_expand/src/mbe/metavar_expr.rs index 6e9196150..7c37aadc6 100644 --- a/compiler/rustc_expand/src/mbe/metavar_expr.rs +++ b/compiler/rustc_expand/src/mbe/metavar_expr.rs @@ -93,7 +93,17 @@ fn parse_count<'sess>( span: Span, ) -> PResult<'sess, MetaVarExpr> { let ident = parse_ident(iter, sess, span)?; - let depth = if try_eat_comma(iter) { Some(parse_depth(iter, sess, span)?) } else { None }; + let depth = if try_eat_comma(iter) { + if iter.look_ahead(0).is_none() { + return Err(sess.span_diagnostic.struct_span_err( + span, + "`count` followed by a comma must have an associated index indicating its depth", + )); + } + Some(parse_depth(iter, sess, span)?) + } else { + None + }; Ok(MetaVarExpr::Count(ident, depth)) } @@ -104,13 +114,10 @@ fn parse_depth<'sess>( span: Span, ) -> PResult<'sess, usize> { let Some(tt) = iter.next() else { return Ok(0) }; - let TokenTree::Token(token::Token { - kind: token::TokenKind::Literal(lit), .. - }, _) = tt else { - return Err(sess.span_diagnostic.struct_span_err( - span, - "meta-variable expression depth must be a literal" - )); + let TokenTree::Token(token::Token { kind: token::TokenKind::Literal(lit), .. }, _) = tt else { + return Err(sess + .span_diagnostic + .struct_span_err(span, "meta-variable expression depth must be a literal")); }; if let Ok(lit_kind) = LitKind::from_token_lit(*lit) && let LitKind::Int(n_u128, LitIntType::Unsuffixed) = lit_kind diff --git a/compiler/rustc_expand/src/mbe/quoted.rs b/compiler/rustc_expand/src/mbe/quoted.rs index 40bfa3715..6546199f5 100644 --- a/compiler/rustc_expand/src/mbe/quoted.rs +++ b/compiler/rustc_expand/src/mbe/quoted.rs @@ -36,7 +36,7 @@ const VALID_FRAGMENT_NAMES_MSG: &str = "valid fragment specifiers are \ /// /// A collection of `self::TokenTree`. There may also be some errors emitted to `sess`. pub(super) fn parse( - input: tokenstream::TokenStream, + input: &tokenstream::TokenStream, parsing_patterns: bool, sess: &ParseSess, node_id: NodeId, @@ -48,7 +48,7 @@ pub(super) fn parse( // For each token tree in `input`, parse the token into a `self::TokenTree`, consuming // additional trees if need be. - let mut trees = input.into_trees(); + let mut trees = input.trees(); while let Some(tree) = trees.next() { // Given the parsed tree, if there is a metavar and we are expecting matchers, actually // parse out the matcher (i.e., in `$id:ident` this would parse the `:` and `ident`). @@ -56,7 +56,7 @@ pub(super) fn parse( match tree { TokenTree::MetaVar(start_sp, ident) if parsing_patterns => { let span = match trees.next() { - Some(tokenstream::TokenTree::Token(Token { kind: token::Colon, span }, _)) => { + Some(&tokenstream::TokenTree::Token(Token { kind: token::Colon, span }, _)) => { match trees.next() { Some(tokenstream::TokenTree::Token(token, _)) => match token.ident() { Some((frag, _)) => { @@ -96,10 +96,10 @@ pub(super) fn parse( } _ => token.span, }, - tree => tree.as_ref().map_or(span, tokenstream::TokenTree::span), + tree => tree.map_or(span, tokenstream::TokenTree::span), } } - tree => tree.as_ref().map_or(start_sp, tokenstream::TokenTree::span), + tree => tree.map_or(start_sp, tokenstream::TokenTree::span), }; result.push(TokenTree::MetaVarDecl(span, ident, None)); @@ -134,9 +134,9 @@ fn maybe_emit_macro_metavar_expr_feature(features: &Features, sess: &ParseSess, /// - `parsing_patterns`: same as [parse]. /// - `sess`: the parsing session. Any errors will be emitted to this session. /// - `features`: language features so we can do feature gating. -fn parse_tree( - tree: tokenstream::TokenTree, - outer_trees: &mut impl Iterator<Item = tokenstream::TokenTree>, +fn parse_tree<'a>( + tree: &'a tokenstream::TokenTree, + outer_trees: &mut impl Iterator<Item = &'a tokenstream::TokenTree>, parsing_patterns: bool, sess: &ParseSess, node_id: NodeId, @@ -146,13 +146,13 @@ fn parse_tree( // Depending on what `tree` is, we could be parsing different parts of a macro match tree { // `tree` is a `$` token. Look at the next token in `trees` - tokenstream::TokenTree::Token(Token { kind: token::Dollar, span }, _) => { + &tokenstream::TokenTree::Token(Token { kind: token::Dollar, span }, _) => { // FIXME: Handle `Invisible`-delimited groups in a more systematic way // during parsing. let mut next = outer_trees.next(); - let mut trees: Box<dyn Iterator<Item = tokenstream::TokenTree>>; + let mut trees: Box<dyn Iterator<Item = &tokenstream::TokenTree>>; if let Some(tokenstream::TokenTree::Delimited(_, Delimiter::Invisible, tts)) = next { - trees = Box::new(tts.into_trees()); + trees = Box::new(tts.trees()); next = trees.next(); } else { trees = Box::new(outer_trees); @@ -160,7 +160,7 @@ fn parse_tree( match next { // `tree` is followed by a delimited set of token trees. - Some(tokenstream::TokenTree::Delimited(delim_span, delim, tts)) => { + Some(&tokenstream::TokenTree::Delimited(delim_span, delim, ref tts)) => { if parsing_patterns { if delim != Delimiter::Parenthesis { span_dollar_dollar_or_metavar_in_the_lhs_err( @@ -194,7 +194,7 @@ fn parse_tree( Delimiter::Parenthesis => {} _ => { let tok = pprust::token_kind_to_string(&token::OpenDelim(delim)); - let msg = format!("expected `(` or `{{`, found `{}`", tok); + let msg = format!("expected `(` or `{{`, found `{tok}`"); sess.span_diagnostic.span_err(delim_span.entire(), msg); } } @@ -228,7 +228,7 @@ fn parse_tree( } // `tree` is followed by another `$`. This is an escaped `$`. - Some(tokenstream::TokenTree::Token(Token { kind: token::Dollar, span }, _)) => { + Some(&tokenstream::TokenTree::Token(Token { kind: token::Dollar, span }, _)) => { if parsing_patterns { span_dollar_dollar_or_metavar_in_the_lhs_err( sess, @@ -256,11 +256,11 @@ fn parse_tree( } // `tree` is an arbitrary token. Keep it. - tokenstream::TokenTree::Token(token, _) => TokenTree::Token(token), + tokenstream::TokenTree::Token(token, _) => TokenTree::Token(token.clone()), // `tree` is the beginning of a delimited set of tokens (e.g., `(` or `{`). We need to // descend into the delimited set and further parse it. - tokenstream::TokenTree::Delimited(span, delim, tts) => TokenTree::Delimited( + &tokenstream::TokenTree::Delimited(span, delim, ref tts) => TokenTree::Delimited( span, Delimited { delim, @@ -286,16 +286,16 @@ fn kleene_op(token: &Token) -> Option<KleeneOp> { /// - Ok(Ok((op, span))) if the next token tree is a KleeneOp /// - Ok(Err(tok, span)) if the next token tree is a token but not a KleeneOp /// - Err(span) if the next token tree is not a token -fn parse_kleene_op( - input: &mut impl Iterator<Item = tokenstream::TokenTree>, +fn parse_kleene_op<'a>( + input: &mut impl Iterator<Item = &'a tokenstream::TokenTree>, span: Span, ) -> Result<Result<(KleeneOp, Span), Token>, Span> { match input.next() { Some(tokenstream::TokenTree::Token(token, _)) => match kleene_op(&token) { Some(op) => Ok(Ok((op, token.span))), - None => Ok(Err(token)), + None => Ok(Err(token.clone())), }, - tree => Err(tree.as_ref().map_or(span, tokenstream::TokenTree::span)), + tree => Err(tree.map_or(span, tokenstream::TokenTree::span)), } } @@ -311,8 +311,8 @@ fn parse_kleene_op( /// session `sess`. If the next one (or possibly two) tokens in `input` correspond to a Kleene /// operator and separator, then a tuple with `(separator, KleeneOp)` is returned. Otherwise, an /// error with the appropriate span is emitted to `sess` and a dummy value is returned. -fn parse_sep_and_kleene_op( - input: &mut impl Iterator<Item = tokenstream::TokenTree>, +fn parse_sep_and_kleene_op<'a>( + input: &mut impl Iterator<Item = &'a tokenstream::TokenTree>, span: Span, sess: &ParseSess, ) -> (Option<Token>, KleeneToken) { diff --git a/compiler/rustc_expand/src/mbe/transcribe.rs b/compiler/rustc_expand/src/mbe/transcribe.rs index d523d3eac..15e7ab3fe 100644 --- a/compiler/rustc_expand/src/mbe/transcribe.rs +++ b/compiler/rustc_expand/src/mbe/transcribe.rs @@ -182,9 +182,7 @@ pub(super) fn transcribe<'a>( LockstepIterSize::Constraint(len, _) => { // We do this to avoid an extra clone above. We know that this is a // sequence already. - let mbe::TokenTree::Sequence(sp, seq) = seq else { - unreachable!() - }; + let mbe::TokenTree::Sequence(sp, seq) = seq else { unreachable!() }; // Is the repetition empty? if len == 0 { @@ -222,16 +220,15 @@ pub(super) fn transcribe<'a>( MatchedTokenTree(tt) => { // `tt`s are emitted into the output stream directly as "raw tokens", // without wrapping them into groups. - let token = tt.clone(); - result.push(token); + result.push(tt.clone()); } MatchedNonterminal(nt) => { // Other variables are emitted into the output stream as groups with // `Delimiter::Invisible` to maintain parsing priorities. // `Interpolated` is currently used for such groups in rustc parser. marker.visit_span(&mut sp); - let token = TokenTree::token_alone(token::Interpolated(nt.clone()), sp); - result.push(token); + result + .push(TokenTree::token_alone(token::Interpolated(nt.clone()), sp)); } MatchedSeq(..) => { // We were unable to descend far enough. This is an error. @@ -399,7 +396,9 @@ fn lockstep_iter_size( } TokenTree::MetaVarExpr(_, expr) => { let default_rslt = LockstepIterSize::Unconstrained; - let Some(ident) = expr.ident() else { return default_rslt; }; + let Some(ident) = expr.ident() else { + return default_rslt; + }; let name = MacroRulesNormalizedIdent::new(ident); match lookup_cur_matched(name, interpolations, repeats) { Some(MatchedSeq(ads)) => { diff --git a/compiler/rustc_expand/src/parse/tests.rs b/compiler/rustc_expand/src/parse/tests.rs index 8b37728b6..bdc20882a 100644 --- a/compiler/rustc_expand/src/parse/tests.rs +++ b/compiler/rustc_expand/src/parse/tests.rs @@ -1,4 +1,6 @@ -use crate::tests::{matches_codepattern, string_to_stream, with_error_checking_parse}; +use crate::tests::{ + matches_codepattern, string_to_stream, with_error_checking_parse, with_expected_parse_error, +}; use rustc_ast::ptr::P; use rustc_ast::token::{self, Delimiter, Token}; @@ -51,11 +53,15 @@ fn string_to_item(source_str: String) -> Option<P<ast::Item>> { with_error_checking_parse(source_str, &sess(), |p| p.parse_item(ForceCollect::No)) } -#[should_panic] #[test] fn bad_path_expr_1() { + // This should trigger error: expected identifier, found keyword `return` create_default_session_globals_then(|| { - string_to_expr("::abc::def::return".to_string()); + with_expected_parse_error( + "::abc::def::return", + "expected identifier, found keyword `return`", + |p| p.parse_expr(), + ); }) } @@ -63,9 +69,8 @@ fn bad_path_expr_1() { #[test] fn string_to_tts_macro() { create_default_session_globals_then(|| { - let tts: Vec<_> = - string_to_stream("macro_rules! zip (($a)=>($a))".to_string()).into_trees().collect(); - let tts: &[TokenTree] = &tts[..]; + let stream = string_to_stream("macro_rules! zip (($a)=>($a))".to_string()); + let tts = &stream.trees().collect::<Vec<_>>()[..]; match tts { [ @@ -294,9 +299,7 @@ fn ttdelim_span() { .unwrap(); let ast::ExprKind::MacCall(mac) = &expr.kind else { panic!("not a macro") }; - let tts: Vec<_> = mac.args.tokens.clone().into_trees().collect(); - - let span = tts.iter().rev().next().unwrap().span(); + let span = mac.args.tokens.trees().last().unwrap().span(); match sess.source_map().span_to_snippet(span) { Ok(s) => assert_eq!(&s[..], "{ body }"), diff --git a/compiler/rustc_expand/src/placeholders.rs b/compiler/rustc_expand/src/placeholders.rs index e9af688ee..82cac2292 100644 --- a/compiler/rustc_expand/src/placeholders.rs +++ b/compiler/rustc_expand/src/placeholders.rs @@ -2,6 +2,7 @@ use crate::expand::{AstFragment, AstFragmentKind}; use rustc_ast as ast; use rustc_ast::mut_visit::*; use rustc_ast::ptr::P; +use rustc_ast::token::Delimiter; use rustc_data_structures::fx::FxHashMap; use rustc_span::source_map::DUMMY_SP; use rustc_span::symbol::Ident; @@ -18,7 +19,7 @@ pub fn placeholder( path: ast::Path { span: DUMMY_SP, segments: ThinVec::new(), tokens: None }, args: P(ast::DelimArgs { dspan: ast::tokenstream::DelimSpan::dummy(), - delim: ast::MacDelimiter::Parenthesis, + delim: Delimiter::Parenthesis, tokens: ast::tokenstream::TokenStream::new(Vec::new()), }), }) diff --git a/compiler/rustc_expand/src/proc_macro.rs b/compiler/rustc_expand/src/proc_macro.rs index 41b24407f..c617cd76e 100644 --- a/compiler/rustc_expand/src/proc_macro.rs +++ b/compiler/rustc_expand/src/proc_macro.rs @@ -95,7 +95,7 @@ impl base::AttrProcMacro for AttrProcMacro { |e| { let mut err = ecx.struct_span_err(span, "custom attribute panicked"); if let Some(s) = e.as_str() { - err.help(format!("message: {}", s)); + err.help(format!("message: {s}")); } err.emit() }, @@ -148,7 +148,7 @@ impl MultiItemModifier for DeriveProcMacro { Err(e) => { let mut err = ecx.struct_span_err(span, "proc-macro derive panicked"); if let Some(s) = e.as_str() { - err.help(format!("message: {}", s)); + err.help(format!("message: {s}")); } err.emit(); return ExpandResult::Ready(vec![]); diff --git a/compiler/rustc_expand/src/proc_macro_server.rs b/compiler/rustc_expand/src/proc_macro_server.rs index ecd231511..2dc9b51a3 100644 --- a/compiler/rustc_expand/src/proc_macro_server.rs +++ b/compiler/rustc_expand/src/proc_macro_server.rs @@ -94,10 +94,10 @@ impl FromInternal<(TokenStream, &mut Rustc<'_, '_>)> for Vec<TokenTree<TokenStre // Estimate the capacity as `stream.len()` rounded up to the next power // of two to limit the number of required reallocations. let mut trees = Vec::with_capacity(stream.len().next_power_of_two()); - let mut cursor = stream.into_trees(); + let mut cursor = stream.trees(); while let Some(tree) = cursor.next() { - let (Token { kind, span }, joint) = match tree { + let (Token { kind, span }, joint) = match tree.clone() { tokenstream::TokenTree::Delimited(span, delim, tts) => { let delimiter = pm::Delimiter::from_internal(delim); trees.push(TokenTree::Group(Group { @@ -622,7 +622,7 @@ impl server::SourceFile for Rustc<'_, '_> { impl server::Span for Rustc<'_, '_> { fn debug(&mut self, span: Self::Span) -> String { if self.ecx.ecfg.span_debug { - format!("{:?}", span) + format!("{span:?}") } else { format!("{:?} bytes({}..{})", span.ctxt(), span.lo().0, span.hi().0) } diff --git a/compiler/rustc_expand/src/tests.rs b/compiler/rustc_expand/src/tests.rs index 8a5e09475..8e3219c13 100644 --- a/compiler/rustc_expand/src/tests.rs +++ b/compiler/rustc_expand/src/tests.rs @@ -8,7 +8,8 @@ use rustc_span::{BytePos, Span}; use rustc_data_structures::sync::Lrc; use rustc_errors::emitter::EmitterWriter; -use rustc_errors::{Handler, MultiSpan, PResult, TerminalUrl}; +use rustc_errors::{Handler, MultiSpan, PResult}; +use termcolor::WriteColor; use std::io; use std::io::prelude::*; @@ -22,6 +23,23 @@ fn string_to_parser(ps: &ParseSess, source_str: String) -> Parser<'_> { new_parser_from_source_str(ps, PathBuf::from("bogofile").into(), source_str) } +fn create_test_handler() -> (Handler, Lrc<SourceMap>, Arc<Mutex<Vec<u8>>>) { + let output = Arc::new(Mutex::new(Vec::new())); + let source_map = Lrc::new(SourceMap::new(FilePathMapping::empty())); + let fallback_bundle = rustc_errors::fallback_fluent_bundle( + vec![crate::DEFAULT_LOCALE_RESOURCE, rustc_parse::DEFAULT_LOCALE_RESOURCE], + false, + ); + let emitter = EmitterWriter::new(Box::new(Shared { data: output.clone() }), fallback_bundle) + .sm(Some(source_map.clone())) + .diagnostic_width(Some(140)); + let handler = Handler::with_emitter(Box::new(emitter)); + (handler, source_map, output) +} + +/// Returns the result of parsing the given string via the given callback. +/// +/// If there are any errors, this will panic. pub(crate) fn with_error_checking_parse<'a, T, F>(s: String, ps: &'a ParseSess, f: F) -> T where F: FnOnce(&mut Parser<'a>) -> PResult<'a, T>, @@ -32,6 +50,26 @@ where x } +/// Verifies that parsing the given string using the given callback will +/// generate an error that contains the given text. +pub(crate) fn with_expected_parse_error<T, F>(source_str: &str, expected_output: &str, f: F) +where + F: for<'a> FnOnce(&mut Parser<'a>) -> PResult<'a, T>, +{ + let (handler, source_map, output) = create_test_handler(); + let ps = ParseSess::with_span_handler(handler, source_map); + let mut p = string_to_parser(&ps, source_str.to_string()); + let result = f(&mut p); + assert!(result.is_ok()); + + let bytes = output.lock().unwrap(); + let actual_output = str::from_utf8(&bytes).unwrap(); + println!("expected output:\n------\n{}------", expected_output); + println!("actual output:\n------\n{}------", actual_output); + + assert!(actual_output.contains(expected_output)) +} + /// Maps a string to tts, using a made-up filename. pub(crate) fn string_to_stream(source_str: String) -> TokenStream { let ps = ParseSess::new( @@ -118,6 +156,20 @@ pub(crate) struct Shared<T: Write> { pub data: Arc<Mutex<T>>, } +impl<T: Write> WriteColor for Shared<T> { + fn supports_color(&self) -> bool { + false + } + + fn set_color(&mut self, _spec: &termcolor::ColorSpec) -> io::Result<()> { + Ok(()) + } + + fn reset(&mut self) -> io::Result<()> { + Ok(()) + } +} + impl<T: Write> Write for Shared<T> { fn write(&mut self, buf: &[u8]) -> io::Result<usize> { self.data.lock().unwrap().write(buf) @@ -130,13 +182,7 @@ impl<T: Write> Write for Shared<T> { fn test_harness(file_text: &str, span_labels: Vec<SpanLabel>, expected_output: &str) { create_default_session_if_not_set_then(|_| { - let output = Arc::new(Mutex::new(Vec::new())); - - let fallback_bundle = rustc_errors::fallback_fluent_bundle( - vec![crate::DEFAULT_LOCALE_RESOURCE, rustc_parse::DEFAULT_LOCALE_RESOURCE], - false, - ); - let source_map = Lrc::new(SourceMap::new(FilePathMapping::empty())); + let (handler, source_map, output) = create_test_handler(); source_map.new_source_file(Path::new("test.rs").to_owned().into(), file_text.to_owned()); let primary_span = make_span(&file_text, &span_labels[0].start, &span_labels[0].end); @@ -148,20 +194,6 @@ fn test_harness(file_text: &str, span_labels: Vec<SpanLabel>, expected_output: & println!("text: {:?}", source_map.span_to_snippet(span)); } - let emitter = EmitterWriter::new( - Box::new(Shared { data: output.clone() }), - Some(source_map.clone()), - None, - fallback_bundle, - false, - false, - false, - None, - false, - false, - TerminalUrl::No, - ); - let handler = Handler::with_emitter(true, None, Box::new(emitter)); #[allow(rustc::untranslatable_diagnostic)] handler.span_err(msp, "foo"); |