summaryrefslogtreecommitdiffstats
path: root/compiler/rustc_ast
diff options
context:
space:
mode:
Diffstat (limited to 'compiler/rustc_ast')
-rw-r--r--compiler/rustc_ast/Cargo.toml3
-rw-r--r--compiler/rustc_ast/src/ast.rs106
-rw-r--r--compiler/rustc_ast/src/attr/mod.rs622
-rw-r--r--compiler/rustc_ast/src/format.rs272
-rw-r--r--compiler/rustc_ast/src/lib.rs3
-rw-r--r--compiler/rustc_ast/src/mut_visit.rs58
-rw-r--r--compiler/rustc_ast/src/tokenstream.rs42
-rw-r--r--compiler/rustc_ast/src/util/comments.rs33
-rw-r--r--compiler/rustc_ast/src/util/parser.rs6
-rw-r--r--compiler/rustc_ast/src/util/unicode.rs2
-rw-r--r--compiler/rustc_ast/src/visit.rs13
11 files changed, 759 insertions, 401 deletions
diff --git a/compiler/rustc_ast/Cargo.toml b/compiler/rustc_ast/Cargo.toml
index 9253b7e68..f0632ac92 100644
--- a/compiler/rustc_ast/Cargo.toml
+++ b/compiler/rustc_ast/Cargo.toml
@@ -7,6 +7,7 @@ edition = "2021"
[dependencies]
bitflags = "1.2.1"
+memchr = "2.5.0"
rustc_data_structures = { path = "../rustc_data_structures" }
rustc_index = { path = "../rustc_index" }
rustc_lexer = { path = "../rustc_lexer" }
@@ -14,5 +15,5 @@ rustc_macros = { path = "../rustc_macros" }
rustc_serialize = { path = "../rustc_serialize" }
rustc_span = { path = "../rustc_span" }
smallvec = { version = "1.8.1", features = ["union", "may_dangle"] }
-thin-vec = "0.2.9"
+thin-vec = "0.2.12"
tracing = "0.1"
diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs
index 9317579f7..03c375c46 100644
--- a/compiler/rustc_ast/src/ast.rs
+++ b/compiler/rustc_ast/src/ast.rs
@@ -18,6 +18,7 @@
//! - [`Attribute`]: Metadata associated with item.
//! - [`UnOp`], [`BinOp`], and [`BinOpKind`]: Unary and binary operators.
+pub use crate::format::*;
pub use crate::util::parser::ExprPrecedence;
pub use GenericArgs::*;
pub use UnsafeSource::*;
@@ -208,7 +209,7 @@ pub struct AngleBracketedArgs {
/// The overall span.
pub span: Span,
/// The comma separated parts in the `<...>`.
- pub args: Vec<AngleBracketedArg>,
+ pub args: ThinVec<AngleBracketedArg>,
}
/// Either an argument for a parameter e.g., `'a`, `Vec<u8>`, `0`,
@@ -252,7 +253,7 @@ pub struct ParenthesizedArgs {
pub span: Span,
/// `(A, B)`
- pub inputs: Vec<P<Ty>>,
+ pub inputs: ThinVec<P<Ty>>,
/// ```text
/// Foo(A, B) -> C
@@ -383,7 +384,7 @@ impl GenericParam {
/// a function, enum, trait, etc.
#[derive(Clone, Encodable, Decodable, Debug)]
pub struct Generics {
- pub params: Vec<GenericParam>,
+ pub params: ThinVec<GenericParam>,
pub where_clause: WhereClause,
pub span: Span,
}
@@ -391,7 +392,7 @@ pub struct Generics {
impl Default for Generics {
/// Creates an instance of `Generics`.
fn default() -> Generics {
- Generics { params: Vec::new(), where_clause: Default::default(), span: DUMMY_SP }
+ Generics { params: ThinVec::new(), where_clause: Default::default(), span: DUMMY_SP }
}
}
@@ -402,13 +403,13 @@ pub struct WhereClause {
/// if we parsed no predicates (e.g. `struct Foo where {}`).
/// This allows us to pretty-print accurately.
pub has_where_token: bool,
- pub predicates: Vec<WherePredicate>,
+ pub predicates: ThinVec<WherePredicate>,
pub span: Span,
}
impl Default for WhereClause {
fn default() -> WhereClause {
- WhereClause { has_where_token: false, predicates: Vec::new(), span: DUMMY_SP }
+ WhereClause { has_where_token: false, predicates: ThinVec::new(), span: DUMMY_SP }
}
}
@@ -440,7 +441,7 @@ impl WherePredicate {
pub struct WhereBoundPredicate {
pub span: Span,
/// Any generics from a `for` binding.
- pub bound_generic_params: Vec<GenericParam>,
+ pub bound_generic_params: ThinVec<GenericParam>,
/// The type being bounded.
pub bounded_ty: P<Ty>,
/// Trait and lifetime bounds (`Clone + Send + 'static`).
@@ -470,7 +471,7 @@ pub struct WhereEqPredicate {
#[derive(Clone, Encodable, Decodable, Debug)]
pub struct Crate {
pub attrs: AttrVec,
- pub items: Vec<P<Item>>,
+ pub items: ThinVec<P<Item>>,
pub spans: ModSpans,
/// Must be equal to `CRATE_NODE_ID` after the crate root is expanded, but may hold
/// expansion placeholders or an unassigned value (`DUMMY_NODE_ID`) before that.
@@ -502,7 +503,7 @@ pub enum MetaItemKind {
/// List meta item.
///
/// E.g., `#[derive(..)]`, where the field represents the `..`.
- List(Vec<NestedMetaItem>),
+ List(ThinVec<NestedMetaItem>),
/// Name value meta item.
///
@@ -530,7 +531,7 @@ pub enum NestedMetaItem {
#[derive(Clone, Encodable, Decodable, Debug)]
pub struct Block {
/// The statements in the block.
- pub stmts: Vec<Stmt>,
+ pub stmts: ThinVec<Stmt>,
pub id: NodeId,
/// Distinguishes between `unsafe { ... }` and `{ ... }`.
pub rules: BlockCheckMode,
@@ -580,7 +581,7 @@ impl Pat {
// A tuple pattern `(P0, .., Pn)` can be reparsed as `(T0, .., Tn)`
// assuming `T0` to `Tn` are all syntactically valid as types.
PatKind::Tuple(pats) => {
- let mut tys = Vec::with_capacity(pats.len());
+ let mut tys = ThinVec::with_capacity(pats.len());
// FIXME(#48994) - could just be collected into an Option<Vec>
for pat in pats {
tys.push(pat.to_ty()?);
@@ -721,14 +722,14 @@ pub enum PatKind {
/// A struct or struct variant pattern (e.g., `Variant {x, y, ..}`).
/// The `bool` is `true` in the presence of a `..`.
- Struct(Option<P<QSelf>>, Path, Vec<PatField>, /* recovered */ bool),
+ Struct(Option<P<QSelf>>, Path, ThinVec<PatField>, /* recovered */ bool),
/// A tuple struct/variant pattern (`Variant(x, y, .., z)`).
- TupleStruct(Option<P<QSelf>>, Path, Vec<P<Pat>>),
+ TupleStruct(Option<P<QSelf>>, Path, ThinVec<P<Pat>>),
/// An or-pattern `A | B | C`.
/// Invariant: `pats.len() >= 2`.
- Or(Vec<P<Pat>>),
+ Or(ThinVec<P<Pat>>),
/// A possibly qualified path pattern.
/// Unqualified path patterns `A::B::C` can legally refer to variants, structs, constants
@@ -737,7 +738,7 @@ pub enum PatKind {
Path(Option<P<QSelf>>, Path),
/// A tuple pattern (`(a, b)`).
- Tuple(Vec<P<Pat>>),
+ Tuple(ThinVec<P<Pat>>),
/// A `box` pattern.
Box(P<Pat>),
@@ -752,7 +753,7 @@ pub enum PatKind {
Range(Option<P<Expr>>, Option<P<Expr>>, Spanned<RangeEnd>),
/// A slice pattern `[a, b, c]`.
- Slice(Vec<P<Pat>>),
+ Slice(ThinVec<P<Pat>>),
/// A rest pattern `..`.
///
@@ -1168,7 +1169,7 @@ impl Expr {
pub fn to_bound(&self) -> Option<GenericBound> {
match &self.kind {
ExprKind::Path(None, path) => Some(GenericBound::Trait(
- PolyTraitRef::new(Vec::new(), path.clone(), self.span),
+ PolyTraitRef::new(ThinVec::new(), path.clone(), self.span),
TraitBoundModifier::None,
)),
_ => None,
@@ -1203,7 +1204,7 @@ impl Expr {
ExprKind::Array(exprs) if exprs.len() == 1 => exprs[0].to_ty().map(TyKind::Slice)?,
ExprKind::Tup(exprs) => {
- let tys = exprs.iter().map(|expr| expr.to_ty()).collect::<Option<Vec<_>>>()?;
+ let tys = exprs.iter().map(|expr| expr.to_ty()).collect::<Option<ThinVec<_>>>()?;
TyKind::Tup(tys)
}
@@ -1269,6 +1270,7 @@ impl Expr {
ExprKind::Try(..) => ExprPrecedence::Try,
ExprKind::Yield(..) => ExprPrecedence::Yield,
ExprKind::Yeet(..) => ExprPrecedence::Yeet,
+ ExprKind::FormatArgs(..) => ExprPrecedence::FormatArgs,
ExprKind::Err => ExprPrecedence::Err,
}
}
@@ -1335,7 +1337,7 @@ pub struct MethodCall {
/// The receiver, e.g. `x`.
pub receiver: P<Expr>,
/// The arguments, e.g. `a, b, c`.
- pub args: Vec<P<Expr>>,
+ pub args: ThinVec<P<Expr>>,
/// The span of the function, without the dot and receiver e.g. `foo::<Bar,
/// Baz>(a, b, c)`.
pub span: Span,
@@ -1355,7 +1357,7 @@ pub enum StructRest {
pub struct StructExpr {
pub qself: Option<P<QSelf>>,
pub path: Path,
- pub fields: Vec<ExprField>,
+ pub fields: ThinVec<ExprField>,
pub rest: StructRest,
}
@@ -1364,7 +1366,7 @@ pub enum ExprKind {
/// A `box x` expression.
Box(P<Expr>),
/// An array (`[a, b, c, d]`)
- Array(Vec<P<Expr>>),
+ Array(ThinVec<P<Expr>>),
/// Allow anonymous constants from an inline `const` block
ConstBlock(AnonConst),
/// A function call
@@ -1373,11 +1375,11 @@ pub enum ExprKind {
/// and the second field is the list of arguments.
/// This also represents calling the constructor of
/// tuple-like ADTs such as tuple structs and enum variants.
- Call(P<Expr>, Vec<P<Expr>>),
+ Call(P<Expr>, ThinVec<P<Expr>>),
/// A method call (e.g. `x.foo::<Bar, Baz>(a, b, c)`).
MethodCall(Box<MethodCall>),
/// A tuple (e.g., `(a, b, c, d)`).
- Tup(Vec<P<Expr>>),
+ Tup(ThinVec<P<Expr>>),
/// A binary operation (e.g., `a + b`, `a * b`).
Binary(BinOp, P<Expr>, P<Expr>),
/// A unary operation (e.g., `!x`, `*x`).
@@ -1412,7 +1414,7 @@ pub enum ExprKind {
/// `'label: loop { block }`
Loop(P<Block>, Option<Label>, Span),
/// A `match` block.
- Match(P<Expr>, Vec<Arm>),
+ Match(P<Expr>, ThinVec<Arm>),
/// A closure (e.g., `move |a, b, c| a + b + c`).
Closure(Box<Closure>),
/// A block (`'label: { ... }`).
@@ -1499,6 +1501,9 @@ pub enum ExprKind {
/// with a `ByteStr` literal.
IncludedBytes(Lrc<[u8]>),
+ /// A `format_args!()` expression.
+ FormatArgs(P<FormatArgs>),
+
/// Placeholder for an expression that wasn't syntactically well formed in some way.
Err,
}
@@ -1569,7 +1574,7 @@ pub enum ClosureBinder {
/// for<'a, 'b> |_: &'a (), _: &'b ()| { ... }
/// ^^^^^^ -- this
/// ```
- generic_params: P<[GenericParam]>,
+ generic_params: ThinVec<GenericParam>,
},
}
@@ -1821,6 +1826,13 @@ pub enum LitKind {
}
impl LitKind {
+ pub fn str(&self) -> Option<Symbol> {
+ match *self {
+ LitKind::Str(s, _) => Some(s),
+ _ => None,
+ }
+ }
+
/// Returns `true` if this literal is a string.
pub fn is_str(&self) -> bool {
matches!(self, LitKind::Str(..))
@@ -2044,7 +2056,7 @@ impl Ty {
pub struct BareFnTy {
pub unsafety: Unsafe,
pub ext: Extern,
- pub generic_params: Vec<GenericParam>,
+ pub generic_params: ThinVec<GenericParam>,
pub decl: P<FnDecl>,
/// Span of the `fn(...) -> ...` part.
pub decl_span: Span,
@@ -2066,7 +2078,7 @@ pub enum TyKind {
/// The never type (`!`).
Never,
/// A tuple (`(A, B, C, D,...)`).
- Tup(Vec<P<Ty>>),
+ Tup(ThinVec<P<Ty>>),
/// A path (`module::module::...::Type`), optionally
/// "qualified", e.g., `<Vec<T> as SomeTrait>::SomeType`.
///
@@ -2351,7 +2363,7 @@ impl Param {
/// which contains metadata about function safety, asyncness, constness and ABI.
#[derive(Clone, Encodable, Decodable, Debug)]
pub struct FnDecl {
- pub inputs: Vec<Param>,
+ pub inputs: ThinVec<Param>,
pub output: FnRetTy,
}
@@ -2463,7 +2475,7 @@ pub enum ModKind {
/// or with definition outlined to a separate file `mod foo;` and already loaded from it.
/// The inner span is from the first token past `{` to the last token until `}`,
/// or from the first to the last token in the loaded file.
- Loaded(Vec<P<Item>>, Inline, ModSpans),
+ Loaded(ThinVec<P<Item>>, Inline, ModSpans),
/// Module with definition outlined to a separate file `mod foo;` but not yet loaded from it.
Unloaded,
}
@@ -2485,12 +2497,12 @@ pub struct ForeignMod {
/// semantically by Rust.
pub unsafety: Unsafe,
pub abi: Option<StrLit>,
- pub items: Vec<P<ForeignItem>>,
+ pub items: ThinVec<P<ForeignItem>>,
}
#[derive(Clone, Encodable, Decodable, Debug)]
pub struct EnumDef {
- pub variants: Vec<Variant>,
+ pub variants: ThinVec<Variant>,
}
/// Enum variant.
#[derive(Clone, Encodable, Decodable, Debug)]
@@ -2520,7 +2532,7 @@ pub enum UseTreeKind {
/// `use prefix` or `use prefix as rename`
Simple(Option<Ident>),
/// `use prefix::{...}`
- Nested(Vec<(UseTree, NodeId)>),
+ Nested(ThinVec<(UseTree, NodeId)>),
/// `use prefix::*`
Glob,
}
@@ -2624,7 +2636,7 @@ pub struct TraitRef {
#[derive(Clone, Encodable, Decodable, Debug)]
pub struct PolyTraitRef {
/// The `'a` in `for<'a> Foo<&'a T>`.
- pub bound_generic_params: Vec<GenericParam>,
+ pub bound_generic_params: ThinVec<GenericParam>,
/// The `Foo<&'a T>` in `<'a> Foo<&'a T>`.
pub trait_ref: TraitRef,
@@ -2633,7 +2645,7 @@ pub struct PolyTraitRef {
}
impl PolyTraitRef {
- pub fn new(generic_params: Vec<GenericParam>, path: Path, span: Span) -> Self {
+ pub fn new(generic_params: ThinVec<GenericParam>, path: Path, span: Span) -> Self {
PolyTraitRef {
bound_generic_params: generic_params,
trait_ref: TraitRef { path, ref_id: DUMMY_NODE_ID },
@@ -2683,11 +2695,11 @@ pub enum VariantData {
/// Struct variant.
///
/// E.g., `Bar { .. }` as in `enum Foo { Bar { .. } }`.
- Struct(Vec<FieldDef>, bool),
+ Struct(ThinVec<FieldDef>, bool),
/// Tuple variant.
///
/// E.g., `Bar(..)` as in `enum Foo { Bar(..) }`.
- Tuple(Vec<FieldDef>, NodeId),
+ Tuple(ThinVec<FieldDef>, NodeId),
/// Unit variant.
///
/// E.g., `Bar = ..` as in `enum Foo { Bar = .. }`.
@@ -2814,7 +2826,7 @@ pub struct Trait {
pub is_auto: IsAuto,
pub generics: Generics,
pub bounds: GenericBounds,
- pub items: Vec<P<AssocItem>>,
+ pub items: ThinVec<P<AssocItem>>,
}
/// The location of a where clause on a `TyAlias` (`Span`) and whether there was
@@ -2862,7 +2874,7 @@ pub struct Impl {
/// The trait being implemented, if any.
pub of_trait: Option<TraitRef>,
pub self_ty: P<Ty>,
- pub items: Vec<P<AssocItem>>,
+ pub items: ThinVec<P<AssocItem>>,
}
#[derive(Clone, Encodable, Decodable, Debug)]
@@ -3100,26 +3112,26 @@ mod size_asserts {
static_assert_size!(AssocItem, 104);
static_assert_size!(AssocItemKind, 32);
static_assert_size!(Attribute, 32);
- static_assert_size!(Block, 48);
+ static_assert_size!(Block, 32);
static_assert_size!(Expr, 72);
static_assert_size!(ExprKind, 40);
- static_assert_size!(Fn, 184);
+ static_assert_size!(Fn, 152);
static_assert_size!(ForeignItem, 96);
static_assert_size!(ForeignItemKind, 24);
static_assert_size!(GenericArg, 24);
- static_assert_size!(GenericBound, 72);
- static_assert_size!(Generics, 72);
- static_assert_size!(Impl, 184);
- static_assert_size!(Item, 184);
- static_assert_size!(ItemKind, 112);
+ static_assert_size!(GenericBound, 56);
+ static_assert_size!(Generics, 40);
+ static_assert_size!(Impl, 136);
+ static_assert_size!(Item, 136);
+ static_assert_size!(ItemKind, 64);
static_assert_size!(LitKind, 24);
static_assert_size!(Local, 72);
static_assert_size!(MetaItemLit, 40);
static_assert_size!(Param, 40);
- static_assert_size!(Pat, 88);
+ static_assert_size!(Pat, 72);
static_assert_size!(Path, 24);
static_assert_size!(PathSegment, 24);
- static_assert_size!(PatKind, 64);
+ static_assert_size!(PatKind, 48);
static_assert_size!(Stmt, 32);
static_assert_size!(StmtKind, 16);
static_assert_size!(Ty, 64);
diff --git a/compiler/rustc_ast/src/attr/mod.rs b/compiler/rustc_ast/src/attr/mod.rs
index c6b6207b3..2e83b3e62 100644
--- a/compiler/rustc_ast/src/attr/mod.rs
+++ b/compiler/rustc_ast/src/attr/mod.rs
@@ -20,7 +20,7 @@ use std::iter;
use std::ops::BitXor;
#[cfg(debug_assertions)]
use std::sync::atomic::{AtomicU32, Ordering};
-use thin_vec::thin_vec;
+use thin_vec::{thin_vec, ThinVec};
pub struct MarkedAttrs(GrowableBitSet<AttrId>);
@@ -40,84 +40,65 @@ impl MarkedAttrs {
}
}
-impl NestedMetaItem {
- /// Returns the `MetaItem` if `self` is a `NestedMetaItem::MetaItem`.
- pub fn meta_item(&self) -> Option<&MetaItem> {
- match self {
- NestedMetaItem::MetaItem(item) => Some(item),
- _ => None,
- }
- }
+pub struct AttrIdGenerator(WorkerLocal<Cell<u32>>);
- /// Returns the `MetaItemLit` if `self` is a `NestedMetaItem::Literal`s.
- pub fn lit(&self) -> Option<&MetaItemLit> {
- match self {
- NestedMetaItem::Lit(lit) => Some(lit),
- _ => None,
- }
- }
+#[cfg(debug_assertions)]
+static MAX_ATTR_ID: AtomicU32 = AtomicU32::new(u32::MAX);
- /// Returns `true` if this list item is a MetaItem with a name of `name`.
- pub fn has_name(&self, name: Symbol) -> bool {
- self.meta_item().map_or(false, |meta_item| meta_item.has_name(name))
- }
+impl AttrIdGenerator {
+ pub fn new() -> Self {
+ // We use `(index as u32).reverse_bits()` to initialize the
+ // starting value of AttrId in each worker thread.
+ // The `index` is the index of the worker thread.
+ // This ensures that the AttrId generated in each thread is unique.
+ AttrIdGenerator(WorkerLocal::new(|index| {
+ let index: u32 = index.try_into().unwrap();
- /// For a single-segment meta item, returns its name; otherwise, returns `None`.
- pub fn ident(&self) -> Option<Ident> {
- self.meta_item().and_then(|meta_item| meta_item.ident())
- }
- pub fn name_or_empty(&self) -> Symbol {
- self.ident().unwrap_or_else(Ident::empty).name
- }
+ #[cfg(debug_assertions)]
+ {
+ let max_id = ((index + 1).next_power_of_two() - 1).bitxor(u32::MAX).reverse_bits();
+ MAX_ATTR_ID.fetch_min(max_id, Ordering::Release);
+ }
- /// Gets the string value if `self` is a `MetaItem` and the `MetaItem` is a
- /// `MetaItemKind::NameValue` variant containing a string, otherwise `None`.
- pub fn value_str(&self) -> Option<Symbol> {
- self.meta_item().and_then(|meta_item| meta_item.value_str())
+ Cell::new(index.reverse_bits())
+ }))
}
- /// Returns a name and single literal value tuple of the `MetaItem`.
- pub fn name_value_literal(&self) -> Option<(Symbol, &MetaItemLit)> {
- self.meta_item().and_then(|meta_item| {
- meta_item.meta_item_list().and_then(|meta_item_list| {
- if meta_item_list.len() == 1
- && let Some(ident) = meta_item.ident()
- && let Some(lit) = meta_item_list[0].lit()
- {
- return Some((ident.name, lit));
- }
- None
- })
- })
- }
+ pub fn mk_attr_id(&self) -> AttrId {
+ let id = self.0.get();
- /// Gets a list of inner meta items from a list `MetaItem` type.
- pub fn meta_item_list(&self) -> Option<&[NestedMetaItem]> {
- self.meta_item().and_then(|meta_item| meta_item.meta_item_list())
- }
+ // Ensure the assigned attr_id does not overlap the bits
+ // representing the number of threads.
+ #[cfg(debug_assertions)]
+ assert!(id <= MAX_ATTR_ID.load(Ordering::Acquire));
- /// Returns `true` if the variant is `MetaItem`.
- pub fn is_meta_item(&self) -> bool {
- self.meta_item().is_some()
+ self.0.set(id + 1);
+ AttrId::from_u32(id)
}
+}
- /// Returns `true` if `self` is a `MetaItem` and the meta item is a word.
- pub fn is_word(&self) -> bool {
- self.meta_item().map_or(false, |meta_item| meta_item.is_word())
+impl Attribute {
+ pub fn get_normal_item(&self) -> &AttrItem {
+ match &self.kind {
+ AttrKind::Normal(normal) => &normal.item,
+ AttrKind::DocComment(..) => panic!("unexpected doc comment"),
+ }
}
- /// See [`MetaItem::name_value_literal_span`].
- pub fn name_value_literal_span(&self) -> Option<Span> {
- self.meta_item()?.name_value_literal_span()
+ pub fn unwrap_normal_item(self) -> AttrItem {
+ match self.kind {
+ AttrKind::Normal(normal) => normal.into_inner().item,
+ AttrKind::DocComment(..) => panic!("unexpected doc comment"),
+ }
}
-}
-impl Attribute {
- #[inline]
- pub fn has_name(&self, name: Symbol) -> bool {
- match &self.kind {
- AttrKind::Normal(normal) => normal.item.path == name,
- AttrKind::DocComment(..) => false,
+ /// Returns `true` if it is a sugared doc comment (`///` or `//!` for example).
+ /// So `#[doc = "doc"]` (which is a doc comment) and `#[doc(...)]` (which is not
+ /// a doc comment) will return `false`.
+ pub fn is_doc_comment(&self) -> bool {
+ match self.kind {
+ AttrKind::Normal(..) => false,
+ AttrKind::DocComment(..) => true,
}
}
@@ -138,20 +119,11 @@ impl Attribute {
self.ident().unwrap_or_else(Ident::empty).name
}
- pub fn value_str(&self) -> Option<Symbol> {
- match &self.kind {
- AttrKind::Normal(normal) => normal.item.meta_kind().and_then(|kind| kind.value_str()),
- AttrKind::DocComment(..) => None,
- }
- }
-
- pub fn meta_item_list(&self) -> Option<Vec<NestedMetaItem>> {
+ #[inline]
+ pub fn has_name(&self, name: Symbol) -> bool {
match &self.kind {
- AttrKind::Normal(normal) => match normal.item.meta_kind() {
- Some(MetaItemKind::List(list)) => Some(list),
- _ => None,
- },
- AttrKind::DocComment(..) => None,
+ AttrKind::Normal(normal) => normal.item.path == name,
+ AttrKind::DocComment(..) => false,
}
}
@@ -162,82 +134,18 @@ impl Attribute {
false
}
}
-}
-
-impl MetaItem {
- /// For a single-segment meta item, returns its name; otherwise, returns `None`.
- pub fn ident(&self) -> Option<Ident> {
- if self.path.segments.len() == 1 { Some(self.path.segments[0].ident) } else { None }
- }
- pub fn name_or_empty(&self) -> Symbol {
- self.ident().unwrap_or_else(Ident::empty).name
- }
- /// ```text
- /// Example:
- /// #[attribute(name = "value")]
- /// ^^^^^^^^^^^^^^
- /// ```
- pub fn name_value_literal(&self) -> Option<&MetaItemLit> {
+ pub fn meta_item_list(&self) -> Option<ThinVec<NestedMetaItem>> {
match &self.kind {
- MetaItemKind::NameValue(v) => Some(v),
- _ => None,
+ AttrKind::Normal(normal) => normal.item.meta_item_list(),
+ AttrKind::DocComment(..) => None,
}
}
pub fn value_str(&self) -> Option<Symbol> {
- self.kind.value_str()
- }
-
- pub fn meta_item_list(&self) -> Option<&[NestedMetaItem]> {
match &self.kind {
- MetaItemKind::List(l) => Some(&**l),
- _ => None,
- }
- }
-
- pub fn is_word(&self) -> bool {
- matches!(self.kind, MetaItemKind::Word)
- }
-
- pub fn has_name(&self, name: Symbol) -> bool {
- self.path == name
- }
-
- /// This is used in case you want the value span instead of the whole attribute. Example:
- ///
- /// ```text
- /// #[doc(alias = "foo")]
- /// ```
- ///
- /// In here, it'll return a span for `"foo"`.
- pub fn name_value_literal_span(&self) -> Option<Span> {
- Some(self.name_value_literal()?.span)
- }
-}
-
-impl AttrItem {
- pub fn span(&self) -> Span {
- self.args.span().map_or(self.path.span, |args_span| self.path.span.to(args_span))
- }
-
- pub fn meta(&self, span: Span) -> Option<MetaItem> {
- Some(MetaItem { path: self.path.clone(), kind: self.meta_kind()?, span })
- }
-
- pub fn meta_kind(&self) -> Option<MetaItemKind> {
- MetaItemKind::from_attr_args(&self.args)
- }
-}
-
-impl Attribute {
- /// Returns `true` if it is a sugared doc comment (`///` or `//!` for example).
- /// So `#[doc = "doc"]` (which is a doc comment) and `#[doc(...)]` (which is not
- /// a doc comment) will return `false`.
- pub fn is_doc_comment(&self) -> bool {
- match self.kind {
- AttrKind::Normal(..) => false,
- AttrKind::DocComment(..) => true,
+ AttrKind::Normal(normal) => normal.item.value_str(),
+ AttrKind::DocComment(..) => None,
}
}
@@ -247,13 +155,11 @@ impl Attribute {
/// * `#[doc = "doc"]` returns `Some(("doc", CommentKind::Line))`.
/// * `#[doc(...)]` returns `None`.
pub fn doc_str_and_comment_kind(&self) -> Option<(Symbol, CommentKind)> {
- match self.kind {
- AttrKind::DocComment(kind, data) => Some((data, kind)),
- AttrKind::Normal(ref normal) if normal.item.path == sym::doc => normal
- .item
- .meta_kind()
- .and_then(|kind| kind.value_str())
- .map(|data| (data, CommentKind::Line)),
+ match &self.kind {
+ AttrKind::DocComment(kind, data) => Some((*data, *kind)),
+ AttrKind::Normal(normal) if normal.item.path == sym::doc => {
+ normal.item.value_str().map(|s| (s, CommentKind::Line))
+ }
_ => None,
}
}
@@ -265,9 +171,7 @@ impl Attribute {
pub fn doc_str(&self) -> Option<Symbol> {
match &self.kind {
AttrKind::DocComment(.., data) => Some(*data),
- AttrKind::Normal(normal) if normal.item.path == sym::doc => {
- normal.item.meta_kind().and_then(|kind| kind.value_str())
- }
+ AttrKind::Normal(normal) if normal.item.path == sym::doc => normal.item.value_str(),
_ => None,
}
}
@@ -276,20 +180,6 @@ impl Attribute {
self.doc_str().map_or(false, |s| comments::may_have_doc_links(s.as_str()))
}
- pub fn get_normal_item(&self) -> &AttrItem {
- match &self.kind {
- AttrKind::Normal(normal) => &normal.item,
- AttrKind::DocComment(..) => panic!("unexpected doc comment"),
- }
- }
-
- pub fn unwrap_normal_item(self) -> AttrItem {
- match self.kind {
- AttrKind::Normal(normal) => normal.into_inner().item,
- AttrKind::DocComment(..) => panic!("unexpected doc comment"),
- }
- }
-
/// Extracts the MetaItem from inside this Attribute.
pub fn meta(&self) -> Option<MetaItem> {
match &self.kind {
@@ -321,130 +211,102 @@ impl Attribute {
}
}
-pub struct AttrIdGenerator(WorkerLocal<Cell<u32>>);
-
-#[cfg(debug_assertions)]
-static MAX_ATTR_ID: AtomicU32 = AtomicU32::new(u32::MAX);
-
-impl AttrIdGenerator {
- pub fn new() -> Self {
- // We use `(index as u32).reverse_bits()` to initialize the
- // starting value of AttrId in each worker thread.
- // The `index` is the index of the worker thread.
- // This ensures that the AttrId generated in each thread is unique.
- AttrIdGenerator(WorkerLocal::new(|index| {
- let index: u32 = index.try_into().unwrap();
+impl AttrItem {
+ pub fn span(&self) -> Span {
+ self.args.span().map_or(self.path.span, |args_span| self.path.span.to(args_span))
+ }
- #[cfg(debug_assertions)]
- {
- let max_id = ((index + 1).next_power_of_two() - 1).bitxor(u32::MAX).reverse_bits();
- MAX_ATTR_ID.fetch_min(max_id, Ordering::Release);
+ fn meta_item_list(&self) -> Option<ThinVec<NestedMetaItem>> {
+ match &self.args {
+ AttrArgs::Delimited(args) if args.delim == MacDelimiter::Parenthesis => {
+ MetaItemKind::list_from_tokens(args.tokens.clone())
}
-
- Cell::new(index.reverse_bits())
- }))
+ AttrArgs::Delimited(_) | AttrArgs::Eq(..) | AttrArgs::Empty => None,
+ }
}
- pub fn mk_attr_id(&self) -> AttrId {
- let id = self.0.get();
+ fn value_str(&self) -> Option<Symbol> {
+ match &self.args {
+ AttrArgs::Eq(_, args) => args.value_str(),
+ AttrArgs::Delimited(_) | AttrArgs::Empty => None,
+ }
+ }
- // Ensure the assigned attr_id does not overlap the bits
- // representing the number of threads.
- #[cfg(debug_assertions)]
- assert!(id <= MAX_ATTR_ID.load(Ordering::Acquire));
+ pub fn meta(&self, span: Span) -> Option<MetaItem> {
+ Some(MetaItem { path: self.path.clone(), kind: self.meta_kind()?, span })
+ }
- self.0.set(id + 1);
- AttrId::from_u32(id)
+ pub fn meta_kind(&self) -> Option<MetaItemKind> {
+ MetaItemKind::from_attr_args(&self.args)
}
}
-pub fn mk_attr(
- g: &AttrIdGenerator,
- style: AttrStyle,
- path: Path,
- args: AttrArgs,
- span: Span,
-) -> Attribute {
- mk_attr_from_item(g, AttrItem { path, args, tokens: None }, None, style, span)
+impl AttrArgsEq {
+ fn value_str(&self) -> Option<Symbol> {
+ match self {
+ AttrArgsEq::Ast(expr) => match expr.kind {
+ ExprKind::Lit(token_lit) => {
+ LitKind::from_token_lit(token_lit).ok().and_then(|lit| lit.str())
+ }
+ _ => None,
+ },
+ AttrArgsEq::Hir(lit) => lit.kind.str(),
+ }
+ }
}
-pub fn mk_attr_from_item(
- g: &AttrIdGenerator,
- item: AttrItem,
- tokens: Option<LazyAttrTokenStream>,
- style: AttrStyle,
- span: Span,
-) -> Attribute {
- Attribute {
- kind: AttrKind::Normal(P(NormalAttr { item, tokens })),
- id: g.mk_attr_id(),
- style,
- span,
+impl MetaItem {
+ /// For a single-segment meta item, returns its name; otherwise, returns `None`.
+ pub fn ident(&self) -> Option<Ident> {
+ if self.path.segments.len() == 1 { Some(self.path.segments[0].ident) } else { None }
}
-}
-pub fn mk_attr_word(g: &AttrIdGenerator, style: AttrStyle, name: Symbol, span: Span) -> Attribute {
- let path = Path::from_ident(Ident::new(name, span));
- let args = AttrArgs::Empty;
- mk_attr(g, style, path, args, span)
-}
+ pub fn name_or_empty(&self) -> Symbol {
+ self.ident().unwrap_or_else(Ident::empty).name
+ }
-pub fn mk_attr_name_value_str(
- g: &AttrIdGenerator,
- style: AttrStyle,
- name: Symbol,
- val: Symbol,
- span: Span,
-) -> Attribute {
- let lit = token::Lit::new(token::Str, escape_string_symbol(val), None);
- let expr = P(Expr {
- id: DUMMY_NODE_ID,
- kind: ExprKind::Lit(lit),
- span,
- attrs: AttrVec::new(),
- tokens: None,
- });
- let path = Path::from_ident(Ident::new(name, span));
- let args = AttrArgs::Eq(span, AttrArgsEq::Ast(expr));
- mk_attr(g, style, path, args, span)
-}
+ pub fn has_name(&self, name: Symbol) -> bool {
+ self.path == name
+ }
-pub fn mk_attr_nested_word(
- g: &AttrIdGenerator,
- style: AttrStyle,
- outer: Symbol,
- inner: Symbol,
- span: Span,
-) -> Attribute {
- let inner_tokens = TokenStream::new(vec![TokenTree::Token(
- Token::from_ast_ident(Ident::new(inner, span)),
- Spacing::Alone,
- )]);
- let outer_ident = Ident::new(outer, span);
- let path = Path::from_ident(outer_ident);
- let attr_args = AttrArgs::Delimited(DelimArgs {
- dspan: DelimSpan::from_single(span),
- delim: MacDelimiter::Parenthesis,
- tokens: inner_tokens,
- });
- mk_attr(g, style, path, attr_args, span)
-}
+ pub fn is_word(&self) -> bool {
+ matches!(self.kind, MetaItemKind::Word)
+ }
-pub fn mk_doc_comment(
- g: &AttrIdGenerator,
- comment_kind: CommentKind,
- style: AttrStyle,
- data: Symbol,
- span: Span,
-) -> Attribute {
- Attribute { kind: AttrKind::DocComment(comment_kind, data), id: g.mk_attr_id(), style, span }
-}
+ pub fn meta_item_list(&self) -> Option<&[NestedMetaItem]> {
+ match &self.kind {
+ MetaItemKind::List(l) => Some(&**l),
+ _ => None,
+ }
+ }
-pub fn list_contains_name(items: &[NestedMetaItem], name: Symbol) -> bool {
- items.iter().any(|item| item.has_name(name))
-}
+ /// ```text
+ /// Example:
+ /// #[attribute(name = "value")]
+ /// ^^^^^^^^^^^^^^
+ /// ```
+ pub fn name_value_literal(&self) -> Option<&MetaItemLit> {
+ match &self.kind {
+ MetaItemKind::NameValue(v) => Some(v),
+ _ => None,
+ }
+ }
+
+ /// This is used in case you want the value span instead of the whole attribute. Example:
+ ///
+ /// ```text
+ /// #[doc(alias = "foo")]
+ /// ```
+ ///
+ /// In here, it'll return a span for `"foo"`.
+ pub fn name_value_literal_span(&self) -> Option<Span> {
+ Some(self.name_value_literal()?.span)
+ }
+
+ pub fn value_str(&self) -> Option<Symbol> {
+ self.kind.value_str()
+ }
-impl MetaItem {
fn from_tokens<I>(tokens: &mut iter::Peekable<I>) -> Option<MetaItem>
where
I: Iterator<Item = TokenTree>,
@@ -508,17 +370,14 @@ impl MetaItem {
impl MetaItemKind {
pub fn value_str(&self) -> Option<Symbol> {
match self {
- MetaItemKind::NameValue(v) => match v.kind {
- LitKind::Str(s, _) => Some(s),
- _ => None,
- },
+ MetaItemKind::NameValue(v) => v.kind.str(),
_ => None,
}
}
- fn list_from_tokens(tokens: TokenStream) -> Option<MetaItemKind> {
+ fn list_from_tokens(tokens: TokenStream) -> Option<ThinVec<NestedMetaItem>> {
let mut tokens = tokens.into_trees().peekable();
- let mut result = Vec::new();
+ let mut result = ThinVec::new();
while tokens.peek().is_some() {
let item = NestedMetaItem::from_tokens(&mut tokens)?;
result.push(item);
@@ -527,7 +386,7 @@ impl MetaItemKind {
_ => return None,
}
}
- Some(MetaItemKind::List(result))
+ Some(result)
}
fn name_value_from_tokens(
@@ -544,6 +403,24 @@ impl MetaItemKind {
}
}
+ fn from_tokens(
+ tokens: &mut iter::Peekable<impl Iterator<Item = TokenTree>>,
+ ) -> Option<MetaItemKind> {
+ match tokens.peek() {
+ Some(TokenTree::Delimited(_, Delimiter::Parenthesis, inner_tokens)) => {
+ let inner_tokens = inner_tokens.clone();
+ tokens.next();
+ MetaItemKind::list_from_tokens(inner_tokens).map(MetaItemKind::List)
+ }
+ Some(TokenTree::Delimited(..)) => None,
+ Some(TokenTree::Token(Token { kind: token::Eq, .. }, _)) => {
+ tokens.next();
+ MetaItemKind::name_value_from_tokens(tokens)
+ }
+ _ => Some(MetaItemKind::Word),
+ }
+ }
+
fn from_attr_args(args: &AttrArgs) -> Option<MetaItemKind> {
match args {
AttrArgs::Empty => Some(MetaItemKind::Word),
@@ -551,7 +428,7 @@ impl MetaItemKind {
dspan: _,
delim: MacDelimiter::Parenthesis,
tokens,
- }) => MetaItemKind::list_from_tokens(tokens.clone()),
+ }) => MetaItemKind::list_from_tokens(tokens.clone()).map(MetaItemKind::List),
AttrArgs::Delimited(..) => None,
AttrArgs::Eq(_, AttrArgsEq::Ast(expr)) => match expr.kind {
ExprKind::Lit(token_lit) => {
@@ -565,24 +442,6 @@ impl MetaItemKind {
AttrArgs::Eq(_, AttrArgsEq::Hir(lit)) => Some(MetaItemKind::NameValue(lit.clone())),
}
}
-
- fn from_tokens(
- tokens: &mut iter::Peekable<impl Iterator<Item = TokenTree>>,
- ) -> Option<MetaItemKind> {
- match tokens.peek() {
- Some(TokenTree::Delimited(_, Delimiter::Parenthesis, inner_tokens)) => {
- let inner_tokens = inner_tokens.clone();
- tokens.next();
- MetaItemKind::list_from_tokens(inner_tokens)
- }
- Some(TokenTree::Delimited(..)) => None,
- Some(TokenTree::Token(Token { kind: token::Eq, .. }, _)) => {
- tokens.next();
- MetaItemKind::name_value_from_tokens(tokens)
- }
- _ => Some(MetaItemKind::Word),
- }
- }
}
impl NestedMetaItem {
@@ -593,6 +452,77 @@ impl NestedMetaItem {
}
}
+ /// For a single-segment meta item, returns its name; otherwise, returns `None`.
+ pub fn ident(&self) -> Option<Ident> {
+ self.meta_item().and_then(|meta_item| meta_item.ident())
+ }
+
+ pub fn name_or_empty(&self) -> Symbol {
+ self.ident().unwrap_or_else(Ident::empty).name
+ }
+
+ /// Returns `true` if this list item is a MetaItem with a name of `name`.
+ pub fn has_name(&self, name: Symbol) -> bool {
+ self.meta_item().map_or(false, |meta_item| meta_item.has_name(name))
+ }
+
+ /// Returns `true` if `self` is a `MetaItem` and the meta item is a word.
+ pub fn is_word(&self) -> bool {
+ self.meta_item().map_or(false, |meta_item| meta_item.is_word())
+ }
+
+ /// Gets a list of inner meta items from a list `MetaItem` type.
+ pub fn meta_item_list(&self) -> Option<&[NestedMetaItem]> {
+ self.meta_item().and_then(|meta_item| meta_item.meta_item_list())
+ }
+
+ /// Returns a name and single literal value tuple of the `MetaItem`.
+ pub fn name_value_literal(&self) -> Option<(Symbol, &MetaItemLit)> {
+ self.meta_item().and_then(|meta_item| {
+ meta_item.meta_item_list().and_then(|meta_item_list| {
+ if meta_item_list.len() == 1
+ && let Some(ident) = meta_item.ident()
+ && let Some(lit) = meta_item_list[0].lit()
+ {
+ return Some((ident.name, lit));
+ }
+ None
+ })
+ })
+ }
+
+ /// See [`MetaItem::name_value_literal_span`].
+ pub fn name_value_literal_span(&self) -> Option<Span> {
+ self.meta_item()?.name_value_literal_span()
+ }
+
+ /// Gets the string value if `self` is a `MetaItem` and the `MetaItem` is a
+ /// `MetaItemKind::NameValue` variant containing a string, otherwise `None`.
+ pub fn value_str(&self) -> Option<Symbol> {
+ self.meta_item().and_then(|meta_item| meta_item.value_str())
+ }
+
+ /// Returns the `MetaItemLit` if `self` is a `NestedMetaItem::Literal`s.
+ pub fn lit(&self) -> Option<&MetaItemLit> {
+ match self {
+ NestedMetaItem::Lit(lit) => Some(lit),
+ _ => None,
+ }
+ }
+
+ /// Returns the `MetaItem` if `self` is a `NestedMetaItem::MetaItem`.
+ pub fn meta_item(&self) -> Option<&MetaItem> {
+ match self {
+ NestedMetaItem::MetaItem(item) => Some(item),
+ _ => None,
+ }
+ }
+
+ /// Returns `true` if the variant is `MetaItem`.
+ pub fn is_meta_item(&self) -> bool {
+ self.meta_item().is_some()
+ }
+
fn from_tokens<I>(tokens: &mut iter::Peekable<I>) -> Option<NestedMetaItem>
where
I: Iterator<Item = TokenTree>,
@@ -614,3 +544,89 @@ impl NestedMetaItem {
MetaItem::from_tokens(tokens).map(NestedMetaItem::MetaItem)
}
}
+
+pub fn mk_doc_comment(
+ g: &AttrIdGenerator,
+ comment_kind: CommentKind,
+ style: AttrStyle,
+ data: Symbol,
+ span: Span,
+) -> Attribute {
+ Attribute { kind: AttrKind::DocComment(comment_kind, data), id: g.mk_attr_id(), style, span }
+}
+
+pub fn mk_attr(
+ g: &AttrIdGenerator,
+ style: AttrStyle,
+ path: Path,
+ args: AttrArgs,
+ span: Span,
+) -> Attribute {
+ mk_attr_from_item(g, AttrItem { path, args, tokens: None }, None, style, span)
+}
+
+pub fn mk_attr_from_item(
+ g: &AttrIdGenerator,
+ item: AttrItem,
+ tokens: Option<LazyAttrTokenStream>,
+ style: AttrStyle,
+ span: Span,
+) -> Attribute {
+ Attribute {
+ kind: AttrKind::Normal(P(NormalAttr { item, tokens })),
+ id: g.mk_attr_id(),
+ style,
+ span,
+ }
+}
+
+pub fn mk_attr_word(g: &AttrIdGenerator, style: AttrStyle, name: Symbol, span: Span) -> Attribute {
+ let path = Path::from_ident(Ident::new(name, span));
+ let args = AttrArgs::Empty;
+ mk_attr(g, style, path, args, span)
+}
+
+pub fn mk_attr_nested_word(
+ g: &AttrIdGenerator,
+ style: AttrStyle,
+ outer: Symbol,
+ inner: Symbol,
+ span: Span,
+) -> Attribute {
+ let inner_tokens = TokenStream::new(vec![TokenTree::Token(
+ Token::from_ast_ident(Ident::new(inner, span)),
+ Spacing::Alone,
+ )]);
+ let outer_ident = Ident::new(outer, span);
+ let path = Path::from_ident(outer_ident);
+ let attr_args = AttrArgs::Delimited(DelimArgs {
+ dspan: DelimSpan::from_single(span),
+ delim: MacDelimiter::Parenthesis,
+ tokens: inner_tokens,
+ });
+ mk_attr(g, style, path, attr_args, span)
+}
+
+pub fn mk_attr_name_value_str(
+ g: &AttrIdGenerator,
+ style: AttrStyle,
+ name: Symbol,
+ val: Symbol,
+ span: Span,
+) -> Attribute {
+ let lit = token::Lit::new(token::Str, escape_string_symbol(val), None);
+ let expr = P(Expr {
+ id: DUMMY_NODE_ID,
+ kind: ExprKind::Lit(lit),
+ span,
+ attrs: AttrVec::new(),
+ tokens: None,
+ });
+ let path = Path::from_ident(Ident::new(name, span));
+ let args = AttrArgs::Eq(span, AttrArgsEq::Ast(expr));
+ mk_attr(g, style, path, args, span)
+}
+
+pub fn list_contains_name(items: &[NestedMetaItem], name: Symbol) -> bool {
+ items.iter().any(|item| item.has_name(name))
+}
diff --git a/compiler/rustc_ast/src/format.rs b/compiler/rustc_ast/src/format.rs
new file mode 100644
index 000000000..d021bea5e
--- /dev/null
+++ b/compiler/rustc_ast/src/format.rs
@@ -0,0 +1,272 @@
+use crate::ptr::P;
+use crate::Expr;
+use rustc_data_structures::fx::FxHashMap;
+use rustc_span::symbol::{Ident, Symbol};
+use rustc_span::Span;
+
+// Definitions:
+//
+// format_args!("hello {abc:.xyz$}!!", abc="world");
+// └──────────────────────────────────────────────┘
+// FormatArgs
+//
+// format_args!("hello {abc:.xyz$}!!", abc="world");
+// └─────────┘
+// argument
+//
+// format_args!("hello {abc:.xyz$}!!", abc="world");
+// └───────────────────┘
+// template
+//
+// format_args!("hello {abc:.xyz$}!!", abc="world");
+// └────┘└─────────┘└┘
+// pieces
+//
+// format_args!("hello {abc:.xyz$}!!", abc="world");
+// └────┘ └┘
+// literal pieces
+//
+// format_args!("hello {abc:.xyz$}!!", abc="world");
+// └─────────┘
+// placeholder
+//
+// format_args!("hello {abc:.xyz$}!!", abc="world");
+// └─┘ └─┘
+// positions (could be names, numbers, empty, or `*`)
+
+/// (Parsed) format args.
+///
+/// Basically the "AST" for a complete `format_args!()`.
+///
+/// E.g., `format_args!("hello {name}");`.
+#[derive(Clone, Encodable, Decodable, Debug)]
+pub struct FormatArgs {
+ pub span: Span,
+ pub template: Vec<FormatArgsPiece>,
+ pub arguments: FormatArguments,
+}
+
+/// A piece of a format template string.
+///
+/// E.g. "hello" or "{name}".
+#[derive(Clone, Encodable, Decodable, Debug)]
+pub enum FormatArgsPiece {
+ Literal(Symbol),
+ Placeholder(FormatPlaceholder),
+}
+
+/// The arguments to format_args!().
+///
+/// E.g. `1, 2, name="ferris", n=3`,
+/// but also implicit captured arguments like `x` in `format_args!("{x}")`.
+#[derive(Clone, Encodable, Decodable, Debug)]
+pub struct FormatArguments {
+ arguments: Vec<FormatArgument>,
+ num_unnamed_args: usize,
+ num_explicit_args: usize,
+ names: FxHashMap<Symbol, usize>,
+}
+
+// FIXME: Rustdoc has trouble proving Send/Sync for this. See #106930.
+#[cfg(parallel_compiler)]
+unsafe impl Sync for FormatArguments {}
+#[cfg(parallel_compiler)]
+unsafe impl Send for FormatArguments {}
+
+impl FormatArguments {
+ pub fn new() -> Self {
+ Self {
+ arguments: Vec::new(),
+ names: FxHashMap::default(),
+ num_unnamed_args: 0,
+ num_explicit_args: 0,
+ }
+ }
+
+ pub fn add(&mut self, arg: FormatArgument) -> usize {
+ let index = self.arguments.len();
+ if let Some(name) = arg.kind.ident() {
+ self.names.insert(name.name, index);
+ } else if self.names.is_empty() {
+ // Only count the unnamed args before the first named arg.
+ // (Any later ones are errors.)
+ self.num_unnamed_args += 1;
+ }
+ if !matches!(arg.kind, FormatArgumentKind::Captured(..)) {
+ // This is an explicit argument.
+ // Make sure that all arguments so far are explcit.
+ assert_eq!(
+ self.num_explicit_args,
+ self.arguments.len(),
+ "captured arguments must be added last"
+ );
+ self.num_explicit_args += 1;
+ }
+ self.arguments.push(arg);
+ index
+ }
+
+ pub fn by_name(&self, name: Symbol) -> Option<(usize, &FormatArgument)> {
+ let i = *self.names.get(&name)?;
+ Some((i, &self.arguments[i]))
+ }
+
+ pub fn by_index(&self, i: usize) -> Option<&FormatArgument> {
+ (i < self.num_explicit_args).then(|| &self.arguments[i])
+ }
+
+ pub fn unnamed_args(&self) -> &[FormatArgument] {
+ &self.arguments[..self.num_unnamed_args]
+ }
+
+ pub fn named_args(&self) -> &[FormatArgument] {
+ &self.arguments[self.num_unnamed_args..self.num_explicit_args]
+ }
+
+ pub fn explicit_args(&self) -> &[FormatArgument] {
+ &self.arguments[..self.num_explicit_args]
+ }
+
+ pub fn all_args(&self) -> &[FormatArgument] {
+ &self.arguments[..]
+ }
+
+ pub fn all_args_mut(&mut self) -> &mut [FormatArgument] {
+ &mut self.arguments[..]
+ }
+}
+
+#[derive(Clone, Encodable, Decodable, Debug)]
+pub struct FormatArgument {
+ pub kind: FormatArgumentKind,
+ pub expr: P<Expr>,
+}
+
+#[derive(Clone, Encodable, Decodable, Debug)]
+pub enum FormatArgumentKind {
+ /// `format_args(…, arg)`
+ Normal,
+ /// `format_args(…, arg = 1)`
+ Named(Ident),
+ /// `format_args("… {arg} …")`
+ Captured(Ident),
+}
+
+impl FormatArgumentKind {
+ pub fn ident(&self) -> Option<Ident> {
+ match self {
+ &Self::Normal => None,
+ &Self::Named(id) => Some(id),
+ &Self::Captured(id) => Some(id),
+ }
+ }
+}
+
+#[derive(Clone, Encodable, Decodable, Debug, PartialEq, Eq)]
+pub struct FormatPlaceholder {
+ /// Index into [`FormatArgs::arguments`].
+ pub argument: FormatArgPosition,
+ /// The span inside the format string for the full `{…}` placeholder.
+ pub span: Option<Span>,
+ /// `{}`, `{:?}`, or `{:x}`, etc.
+ pub format_trait: FormatTrait,
+ /// `{}` or `{:.5}` or `{:-^20}`, etc.
+ pub format_options: FormatOptions,
+}
+
+#[derive(Clone, Encodable, Decodable, Debug, PartialEq, Eq)]
+pub struct FormatArgPosition {
+ /// Which argument this position refers to (Ok),
+ /// or would've referred to if it existed (Err).
+ pub index: Result<usize, usize>,
+ /// What kind of position this is. See [`FormatArgPositionKind`].
+ pub kind: FormatArgPositionKind,
+ /// The span of the name or number.
+ pub span: Option<Span>,
+}
+
+#[derive(Copy, Clone, Encodable, Decodable, Debug, PartialEq, Eq)]
+pub enum FormatArgPositionKind {
+ /// `{}` or `{:.*}`
+ Implicit,
+ /// `{1}` or `{:1$}` or `{:.1$}`
+ Number,
+ /// `{a}` or `{:a$}` or `{:.a$}`
+ Named,
+}
+
+#[derive(Copy, Clone, Encodable, Decodable, Debug, PartialEq, Eq, Hash)]
+pub enum FormatTrait {
+ /// `{}`
+ Display,
+ /// `{:?}`
+ Debug,
+ /// `{:e}`
+ LowerExp,
+ /// `{:E}`
+ UpperExp,
+ /// `{:o}`
+ Octal,
+ /// `{:p}`
+ Pointer,
+ /// `{:b}`
+ Binary,
+ /// `{:x}`
+ LowerHex,
+ /// `{:X}`
+ UpperHex,
+}
+
+#[derive(Clone, Encodable, Decodable, Default, Debug, PartialEq, Eq)]
+pub struct FormatOptions {
+ /// The width. E.g. `{:5}` or `{:width$}`.
+ pub width: Option<FormatCount>,
+ /// The precision. E.g. `{:.5}` or `{:.precision$}`.
+ pub precision: Option<FormatCount>,
+ /// The alignment. E.g. `{:>}` or `{:<}` or `{:^}`.
+ pub alignment: Option<FormatAlignment>,
+ /// The fill character. E.g. the `.` in `{:.>10}`.
+ pub fill: Option<char>,
+ /// The `+` or `-` flag.
+ pub sign: Option<FormatSign>,
+ /// The `#` flag.
+ pub alternate: bool,
+ /// The `0` flag. E.g. the `0` in `{:02x}`.
+ pub zero_pad: bool,
+ /// The `x` or `X` flag (for `Debug` only). E.g. the `x` in `{:x?}`.
+ pub debug_hex: Option<FormatDebugHex>,
+}
+
+#[derive(Copy, Clone, Encodable, Decodable, Debug, PartialEq, Eq)]
+pub enum FormatSign {
+ /// The `+` flag.
+ Plus,
+ /// The `-` flag.
+ Minus,
+}
+
+#[derive(Copy, Clone, Encodable, Decodable, Debug, PartialEq, Eq)]
+pub enum FormatDebugHex {
+ /// The `x` flag in `{:x?}`.
+ Lower,
+ /// The `X` flag in `{:X?}`.
+ Upper,
+}
+
+#[derive(Copy, Clone, Encodable, Decodable, Debug, PartialEq, Eq)]
+pub enum FormatAlignment {
+ /// `{:<}`
+ Left,
+ /// `{:>}`
+ Right,
+ /// `{:^}`
+ Center,
+}
+
+#[derive(Clone, Encodable, Decodable, Debug, PartialEq, Eq)]
+pub enum FormatCount {
+ /// `{:5}` or `{:.5}`
+ Literal(usize),
+ /// `{:.*}`, `{:.5$}`, or `{:a$}`, etc.
+ Argument(FormatArgPosition),
+}
diff --git a/compiler/rustc_ast/src/lib.rs b/compiler/rustc_ast/src/lib.rs
index 9c1dfeb1a..23c32fa96 100644
--- a/compiler/rustc_ast/src/lib.rs
+++ b/compiler/rustc_ast/src/lib.rs
@@ -16,7 +16,6 @@
#![feature(let_chains)]
#![feature(min_specialization)]
#![feature(negative_impls)]
-#![feature(slice_internals)]
#![feature(stmt_expr_attributes)]
#![recursion_limit = "256"]
#![deny(rustc::untranslatable_diagnostic)]
@@ -42,6 +41,7 @@ pub mod ast_traits;
pub mod attr;
pub mod entry;
pub mod expand;
+pub mod format;
pub mod mut_visit;
pub mod node_id;
pub mod ptr;
@@ -51,6 +51,7 @@ pub mod visit;
pub use self::ast::*;
pub use self::ast_traits::{AstDeref, AstNodeWrapper, HasAttrs, HasNodeId, HasSpan, HasTokens};
+pub use self::format::*;
use rustc_data_structures::stable_hasher::{HashStable, StableHasher};
diff --git a/compiler/rustc_ast/src/mut_visit.rs b/compiler/rustc_ast/src/mut_visit.rs
index 77f342d1e..7dcb03b4c 100644
--- a/compiler/rustc_ast/src/mut_visit.rs
+++ b/compiler/rustc_ast/src/mut_visit.rs
@@ -17,10 +17,10 @@ use rustc_data_structures::sync::Lrc;
use rustc_span::source_map::Spanned;
use rustc_span::symbol::Ident;
use rustc_span::Span;
-
use smallvec::{smallvec, Array, SmallVec};
use std::ops::DerefMut;
use std::{panic, ptr};
+use thin_vec::ThinVec;
pub trait ExpectOne<A: Array> {
fn expect_one(self, err: &'static str) -> A::Item;
@@ -297,6 +297,10 @@ pub trait MutVisitor: Sized {
fn visit_inline_asm_sym(&mut self, sym: &mut InlineAsmSym) {
noop_visit_inline_asm_sym(sym, self)
}
+
+ fn visit_format_args(&mut self, fmt: &mut FormatArgs) {
+ noop_visit_format_args(fmt, self)
+ }
}
/// Use a map-style function (`FnOnce(T) -> T`) to overwrite a `&mut T`. Useful
@@ -333,6 +337,17 @@ where
// No `noop_` prefix because there isn't a corresponding method in `MutVisitor`.
#[inline]
+pub fn visit_thin_vec<T, F>(elems: &mut ThinVec<T>, mut visit_elem: F)
+where
+ F: FnMut(&mut T),
+{
+ for elem in elems {
+ visit_elem(elem);
+ }
+}
+
+// No `noop_` prefix because there isn't a corresponding method in `MutVisitor`.
+#[inline]
pub fn visit_opt<T, F>(opt: &mut Option<T>, mut visit_elem: F)
where
F: FnMut(&mut T),
@@ -355,6 +370,11 @@ pub fn visit_exprs<T: MutVisitor>(exprs: &mut Vec<P<Expr>>, vis: &mut T) {
}
// No `noop_` prefix because there isn't a corresponding method in `MutVisitor`.
+pub fn visit_thin_exprs<T: MutVisitor>(exprs: &mut ThinVec<P<Expr>>, vis: &mut T) {
+ exprs.flat_map_in_place(|expr| vis.filter_map_expr(expr))
+}
+
+// No `noop_` prefix because there isn't a corresponding method in `MutVisitor`.
pub fn visit_bounds<T: MutVisitor>(bounds: &mut GenericBounds, vis: &mut T) {
visit_vec(bounds, |bound| vis.visit_param_bound(bound));
}
@@ -470,7 +490,7 @@ pub fn noop_visit_ty<T: MutVisitor>(ty: &mut P<Ty>, vis: &mut T) {
vis.visit_fn_decl(decl);
vis.visit_span(decl_span);
}
- TyKind::Tup(tys) => visit_vec(tys, |ty| vis.visit_ty(ty)),
+ TyKind::Tup(tys) => visit_thin_vec(tys, |ty| vis.visit_ty(ty)),
TyKind::Paren(ty) => vis.visit_ty(ty),
TyKind::Path(qself, path) => {
vis.visit_qself(qself);
@@ -557,7 +577,7 @@ pub fn noop_visit_angle_bracketed_parameter_data<T: MutVisitor>(
vis: &mut T,
) {
let AngleBracketedArgs { args, span } = data;
- visit_vec(args, |arg| match arg {
+ visit_thin_vec(args, |arg| match arg {
AngleBracketedArg::Arg(arg) => vis.visit_generic_arg(arg),
AngleBracketedArg::Constraint(constraint) => vis.visit_constraint(constraint),
});
@@ -569,7 +589,7 @@ pub fn noop_visit_parenthesized_parameter_data<T: MutVisitor>(
vis: &mut T,
) {
let ParenthesizedArgs { inputs, output, span, .. } = args;
- visit_vec(inputs, |input| vis.visit_ty(input));
+ visit_thin_vec(inputs, |input| vis.visit_ty(input));
noop_visit_fn_ret_ty(output, vis);
vis.visit_span(span);
}
@@ -632,7 +652,7 @@ pub fn noop_visit_meta_item<T: MutVisitor>(mi: &mut MetaItem, vis: &mut T) {
let MetaItem { path: _, kind, span } = mi;
match kind {
MetaItemKind::Word => {}
- MetaItemKind::List(mis) => visit_vec(mis, |mi| vis.visit_meta_list_item(mi)),
+ MetaItemKind::List(mis) => visit_thin_vec(mis, |mi| vis.visit_meta_list_item(mi)),
MetaItemKind::NameValue(_s) => {}
}
vis.visit_span(span);
@@ -835,9 +855,7 @@ pub fn noop_visit_closure_binder<T: MutVisitor>(binder: &mut ClosureBinder, vis:
match binder {
ClosureBinder::NotPresent => {}
ClosureBinder::For { span: _, generic_params } => {
- let mut vec = std::mem::take(generic_params).into_vec();
- vec.flat_map_in_place(|param| vis.flat_map_generic_param(param));
- *generic_params = P::from_vec(vec);
+ generic_params.flat_map_in_place(|param| vis.flat_map_generic_param(param));
}
}
}
@@ -915,7 +933,7 @@ pub fn noop_visit_generics<T: MutVisitor>(generics: &mut Generics, vis: &mut T)
pub fn noop_visit_where_clause<T: MutVisitor>(wc: &mut WhereClause, vis: &mut T) {
let WhereClause { has_where_token: _, predicates, span } = wc;
- visit_vec(predicates, |predicate| vis.visit_where_predicate(predicate));
+ visit_thin_vec(predicates, |predicate| vis.visit_where_predicate(predicate));
vis.visit_span(span);
}
@@ -1223,7 +1241,7 @@ pub fn noop_visit_pat<T: MutVisitor>(pat: &mut P<Pat>, vis: &mut T) {
PatKind::TupleStruct(qself, path, elems) => {
vis.visit_qself(qself);
vis.visit_path(path);
- visit_vec(elems, |elem| vis.visit_pat(elem));
+ visit_thin_vec(elems, |elem| vis.visit_pat(elem));
}
PatKind::Path(qself, path) => {
vis.visit_qself(qself);
@@ -1242,7 +1260,7 @@ pub fn noop_visit_pat<T: MutVisitor>(pat: &mut P<Pat>, vis: &mut T) {
vis.visit_span(span);
}
PatKind::Tuple(elems) | PatKind::Slice(elems) | PatKind::Or(elems) => {
- visit_vec(elems, |elem| vis.visit_pat(elem))
+ visit_thin_vec(elems, |elem| vis.visit_pat(elem))
}
PatKind::Paren(inner) => vis.visit_pat(inner),
PatKind::MacCall(mac) => vis.visit_mac_call(mac),
@@ -1284,13 +1302,22 @@ pub fn noop_visit_inline_asm_sym<T: MutVisitor>(
vis.visit_path(path);
}
+pub fn noop_visit_format_args<T: MutVisitor>(fmt: &mut FormatArgs, vis: &mut T) {
+ for arg in fmt.arguments.all_args_mut() {
+ if let FormatArgumentKind::Named(name) = &mut arg.kind {
+ vis.visit_ident(name);
+ }
+ vis.visit_expr(&mut arg.expr);
+ }
+}
+
pub fn noop_visit_expr<T: MutVisitor>(
Expr { kind, id, span, attrs, tokens }: &mut Expr,
vis: &mut T,
) {
match kind {
ExprKind::Box(expr) => vis.visit_expr(expr),
- ExprKind::Array(exprs) => visit_exprs(exprs, vis),
+ ExprKind::Array(exprs) => visit_thin_exprs(exprs, vis),
ExprKind::ConstBlock(anon_const) => {
vis.visit_anon_const(anon_const);
}
@@ -1298,10 +1325,10 @@ pub fn noop_visit_expr<T: MutVisitor>(
vis.visit_expr(expr);
vis.visit_anon_const(count);
}
- ExprKind::Tup(exprs) => visit_exprs(exprs, vis),
+ ExprKind::Tup(exprs) => visit_thin_exprs(exprs, vis),
ExprKind::Call(f, args) => {
vis.visit_expr(f);
- visit_exprs(args, vis);
+ visit_thin_exprs(args, vis);
}
ExprKind::MethodCall(box MethodCall {
seg: PathSegment { ident, id, args: seg_args },
@@ -1313,7 +1340,7 @@ pub fn noop_visit_expr<T: MutVisitor>(
vis.visit_id(id);
visit_opt(seg_args, |args| vis.visit_generic_args(args));
vis.visit_method_receiver_expr(receiver);
- visit_exprs(call_args, vis);
+ visit_thin_exprs(call_args, vis);
vis.visit_span(span);
}
ExprKind::Binary(_binop, lhs, rhs) => {
@@ -1425,6 +1452,7 @@ pub fn noop_visit_expr<T: MutVisitor>(
visit_opt(expr, |expr| vis.visit_expr(expr));
}
ExprKind::InlineAsm(asm) => vis.visit_inline_asm(asm),
+ ExprKind::FormatArgs(fmt) => vis.visit_format_args(fmt),
ExprKind::MacCall(mac) => vis.visit_mac_call(mac),
ExprKind::Struct(se) => {
let StructExpr { qself, path, fields, rest } = se.deref_mut();
diff --git a/compiler/rustc_ast/src/tokenstream.rs b/compiler/rustc_ast/src/tokenstream.rs
index fabd43a16..f0a6a5e07 100644
--- a/compiler/rustc_ast/src/tokenstream.rs
+++ b/compiler/rustc_ast/src/tokenstream.rs
@@ -41,7 +41,8 @@ use std::{fmt, iter};
/// Nothing special happens to misnamed or misplaced `SubstNt`s.
#[derive(Debug, Clone, PartialEq, Encodable, Decodable, HashStable_Generic)]
pub enum TokenTree {
- /// A single token.
+ /// A single token. Should never be `OpenDelim` or `CloseDelim`, because
+ /// delimiters are implicitly represented by `Delimited`.
Token(Token, Spacing),
/// A delimited sequence of token trees.
Delimited(DelimSpan, Delimiter, TokenStream),
@@ -388,12 +389,12 @@ impl TokenStream {
self.0.len()
}
- pub fn trees(&self) -> CursorRef<'_> {
- CursorRef::new(self)
+ pub fn trees(&self) -> RefTokenTreeCursor<'_> {
+ RefTokenTreeCursor::new(self)
}
- pub fn into_trees(self) -> Cursor {
- Cursor::new(self)
+ pub fn into_trees(self) -> TokenTreeCursor {
+ TokenTreeCursor::new(self)
}
/// Compares two `TokenStream`s, checking equality without regarding span information.
@@ -551,16 +552,17 @@ impl TokenStream {
}
}
-/// By-reference iterator over a [`TokenStream`].
+/// By-reference iterator over a [`TokenStream`], that produces `&TokenTree`
+/// items.
#[derive(Clone)]
-pub struct CursorRef<'t> {
+pub struct RefTokenTreeCursor<'t> {
stream: &'t TokenStream,
index: usize,
}
-impl<'t> CursorRef<'t> {
+impl<'t> RefTokenTreeCursor<'t> {
fn new(stream: &'t TokenStream) -> Self {
- CursorRef { stream, index: 0 }
+ RefTokenTreeCursor { stream, index: 0 }
}
pub fn look_ahead(&self, n: usize) -> Option<&TokenTree> {
@@ -568,7 +570,7 @@ impl<'t> CursorRef<'t> {
}
}
-impl<'t> Iterator for CursorRef<'t> {
+impl<'t> Iterator for RefTokenTreeCursor<'t> {
type Item = &'t TokenTree;
fn next(&mut self) -> Option<&'t TokenTree> {
@@ -579,15 +581,16 @@ impl<'t> Iterator for CursorRef<'t> {
}
}
-/// Owning by-value iterator over a [`TokenStream`].
+/// Owning by-value iterator over a [`TokenStream`], that produces `TokenTree`
+/// items.
// FIXME: Many uses of this can be replaced with by-reference iterator to avoid clones.
#[derive(Clone)]
-pub struct Cursor {
+pub struct TokenTreeCursor {
pub stream: TokenStream,
index: usize,
}
-impl Iterator for Cursor {
+impl Iterator for TokenTreeCursor {
type Item = TokenTree;
fn next(&mut self) -> Option<TokenTree> {
@@ -598,9 +601,9 @@ impl Iterator for Cursor {
}
}
-impl Cursor {
+impl TokenTreeCursor {
fn new(stream: TokenStream) -> Self {
- Cursor { stream, index: 0 }
+ TokenTreeCursor { stream, index: 0 }
}
#[inline]
@@ -614,6 +617,15 @@ impl Cursor {
pub fn look_ahead(&self, n: usize) -> Option<&TokenTree> {
self.stream.0.get(self.index + n)
}
+
+ // Replace the previously obtained token tree with `tts`, and rewind to
+ // just before them.
+ pub fn replace_prev_and_rewind(&mut self, tts: Vec<TokenTree>) {
+ assert!(self.index > 0);
+ self.index -= 1;
+ let stream = Lrc::make_mut(&mut self.stream.0);
+ stream.splice(self.index..self.index + 1, tts);
+ }
}
#[derive(Debug, Copy, Clone, PartialEq, Encodable, Decodable, HashStable_Generic)]
diff --git a/compiler/rustc_ast/src/util/comments.rs b/compiler/rustc_ast/src/util/comments.rs
index 275ed02c2..eece99a3e 100644
--- a/compiler/rustc_ast/src/util/comments.rs
+++ b/compiler/rustc_ast/src/util/comments.rs
@@ -58,23 +58,24 @@ pub fn beautify_doc_string(data: Symbol, kind: CommentKind) -> Symbol {
// In case we have doc comments like `/**` or `/*!`, we want to remove stars if they are
// present. However, we first need to strip the empty lines so they don't get in the middle
// when we try to compute the "horizontal trim".
- let lines = if kind == CommentKind::Block {
- // Whatever happens, we skip the first line.
- let mut i = lines
- .get(0)
- .map(|l| if l.trim_start().starts_with('*') { 0 } else { 1 })
- .unwrap_or(0);
- let mut j = lines.len();
-
- while i < j && lines[i].trim().is_empty() {
- i += 1;
- }
- while j > i && lines[j - 1].trim().is_empty() {
- j -= 1;
+ let lines = match kind {
+ CommentKind::Block => {
+ // Whatever happens, we skip the first line.
+ let mut i = lines
+ .get(0)
+ .map(|l| if l.trim_start().starts_with('*') { 0 } else { 1 })
+ .unwrap_or(0);
+ let mut j = lines.len();
+
+ while i < j && lines[i].trim().is_empty() {
+ i += 1;
+ }
+ while j > i && lines[j - 1].trim().is_empty() {
+ j -= 1;
+ }
+ &lines[i..j]
}
- &lines[i..j]
- } else {
- lines
+ CommentKind::Line => lines,
};
for line in lines {
diff --git a/compiler/rustc_ast/src/util/parser.rs b/compiler/rustc_ast/src/util/parser.rs
index 4f7099c7b..3a0af04f9 100644
--- a/compiler/rustc_ast/src/util/parser.rs
+++ b/compiler/rustc_ast/src/util/parser.rs
@@ -225,7 +225,7 @@ impl AssocOp {
AssignOp(_) | // `{ 42 } +=`
As | // `{ 42 } as usize`
// Equal | // `{ 42 } == { 42 }` Accepting these here would regress incorrect
- // NotEqual | // `{ 42 } != { 42 } struct literals parser recovery.
+ // NotEqual | // `{ 42 } != { 42 }` struct literals parser recovery.
Colon, // `{ 42 }: usize`
)
}
@@ -271,6 +271,7 @@ pub enum ExprPrecedence {
Try,
InlineAsm,
Mac,
+ FormatArgs,
Array,
Repeat,
@@ -335,7 +336,8 @@ impl ExprPrecedence {
| ExprPrecedence::Index
| ExprPrecedence::Try
| ExprPrecedence::InlineAsm
- | ExprPrecedence::Mac => PREC_POSTFIX,
+ | ExprPrecedence::Mac
+ | ExprPrecedence::FormatArgs => PREC_POSTFIX,
// Never need parens
ExprPrecedence::Array
diff --git a/compiler/rustc_ast/src/util/unicode.rs b/compiler/rustc_ast/src/util/unicode.rs
index 0eae791b2..6f57d66b2 100644
--- a/compiler/rustc_ast/src/util/unicode.rs
+++ b/compiler/rustc_ast/src/util/unicode.rs
@@ -17,7 +17,7 @@ pub fn contains_text_flow_control_chars(s: &str) -> bool {
// U+2069 - E2 81 A9
let mut bytes = s.as_bytes();
loop {
- match core::slice::memchr::memchr(0xE2, bytes) {
+ match memchr::memchr(0xE2, bytes) {
Some(idx) => {
// bytes are valid UTF-8 -> E2 must be followed by two bytes
let ch = &bytes[idx..idx + 3];
diff --git a/compiler/rustc_ast/src/visit.rs b/compiler/rustc_ast/src/visit.rs
index e8823eff8..bdb1879ec 100644
--- a/compiler/rustc_ast/src/visit.rs
+++ b/compiler/rustc_ast/src/visit.rs
@@ -242,6 +242,9 @@ pub trait Visitor<'ast>: Sized {
fn visit_inline_asm(&mut self, asm: &'ast InlineAsm) {
walk_inline_asm(self, asm)
}
+ fn visit_format_args(&mut self, fmt: &'ast FormatArgs) {
+ walk_format_args(self, fmt)
+ }
fn visit_inline_asm_sym(&mut self, sym: &'ast InlineAsmSym) {
walk_inline_asm_sym(self, sym)
}
@@ -756,6 +759,15 @@ pub fn walk_inline_asm_sym<'a, V: Visitor<'a>>(visitor: &mut V, sym: &'a InlineA
visitor.visit_path(&sym.path, sym.id);
}
+pub fn walk_format_args<'a, V: Visitor<'a>>(visitor: &mut V, fmt: &'a FormatArgs) {
+ for arg in fmt.arguments.all_args() {
+ if let FormatArgumentKind::Named(name) = arg.kind {
+ visitor.visit_ident(name);
+ }
+ visitor.visit_expr(&arg.expr);
+ }
+}
+
pub fn walk_expr<'a, V: Visitor<'a>>(visitor: &mut V, expression: &'a Expr) {
walk_list!(visitor, visit_attribute, expression.attrs.iter());
@@ -896,6 +908,7 @@ pub fn walk_expr<'a, V: Visitor<'a>>(visitor: &mut V, expression: &'a Expr) {
ExprKind::MacCall(mac) => visitor.visit_mac_call(mac),
ExprKind::Paren(subexpression) => visitor.visit_expr(subexpression),
ExprKind::InlineAsm(asm) => visitor.visit_inline_asm(asm),
+ ExprKind::FormatArgs(f) => visitor.visit_format_args(f),
ExprKind::Yield(optional_expression) => {
walk_list!(visitor, visit_expr, optional_expression);
}