use rustc_ast::tokenstream::{TokenStream, TokenTree}; use rustc_ast::util::classify; use rustc_ast::{AttrKind, token}; use rustc_errors::PResult; use rustc_span::Span; use crate::exp; use crate::parser::{AttrWrapper, ForceCollect, Parser, Restrictions, Trailing, UsePreAttrPos}; #[derive(Default)] pub struct CfgSelectBranchAttrSpans { pub attrs: Vec, pub doc_comments: Vec, } impl<'a> Parser<'a> { /// Parses a `TokenTree` consisting either of `{ /* ... */ }` optionally followed by a comma /// (and strip the braces and the optional comma) or an expression followed by a comma /// (and strip the comma). pub fn parse_delimited_token_tree(&mut self) -> PResult<'a, TokenStream> { if self.token == token::OpenBrace { // Strip the outer '{' and '}'. match self.parse_token_tree() { TokenTree::Token(..) => unreachable!("because the current token is a '{{'"), TokenTree::Delimited(.., tts) => { // Optionally end with a comma. let _ = self.eat(exp!(Comma)); return Ok(tts); } } } let expr = self.collect_tokens(None, AttrWrapper::empty(), ForceCollect::Yes, |p, _| { p.parse_expr_res(Restrictions::STMT_EXPR, AttrWrapper::empty()) .map(|(expr, _)| (expr, Trailing::No, UsePreAttrPos::No)) })?; if !classify::expr_is_complete(&expr) && self.token != token::CloseBrace && self.token != token::Eof { self.expect(exp!(Comma))?; } else { let _ = self.eat(exp!(Comma)); } Ok(TokenStream::from_ast(&expr)) } /// Parses outer attributes before a `cfg_select!` branch for recovery. pub fn parse_cfg_select_branch_outer_attrs( &mut self, ) -> PResult<'a, Option> { let attrs = self.parse_outer_attributes()?; if attrs.is_empty() { return Ok(None); } let mut spans = CfgSelectBranchAttrSpans::default(); for attr in attrs.take_for_recovery(self.psess) { match attr.kind { AttrKind::Normal(..) => spans.attrs.push(attr.span), // `parse_outer_attributes` already emitted E0753 for inner doc comments before // recovering them as outer doc-comment attributes. AttrKind::DocComment(comment_kind, _) if self.span_to_snippet(attr.span).ok().is_some_and( |snippet| match comment_kind { token::CommentKind::Line => snippet.starts_with("//!"), token::CommentKind::Block => snippet.starts_with("/*!"), }, ) => {} AttrKind::DocComment(..) => spans.doc_comments.push(attr.span), } } Ok(Some(spans)) } }