summaryrefslogtreecommitdiffstats
path: root/compiler/rustc_ast
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--compiler/rustc_ast/Cargo.toml18
-rw-r--r--compiler/rustc_ast/README.md8
-rw-r--r--compiler/rustc_ast/src/ast.rs3051
-rw-r--r--compiler/rustc_ast/src/ast_traits.rs442
-rw-r--r--compiler/rustc_ast/src/attr/mod.rs634
-rw-r--r--compiler/rustc_ast/src/entry.rs8
-rw-r--r--compiler/rustc_ast/src/expand/allocator.rs53
-rw-r--r--compiler/rustc_ast/src/expand/mod.rs3
-rw-r--r--compiler/rustc_ast/src/lib.rs63
-rw-r--r--compiler/rustc_ast/src/mut_visit.rs1601
-rw-r--r--compiler/rustc_ast/src/node_id.rs40
-rw-r--r--compiler/rustc_ast/src/ptr.rs212
-rw-r--r--compiler/rustc_ast/src/token.rs851
-rw-r--r--compiler/rustc_ast/src/tokenstream.rs681
-rw-r--r--compiler/rustc_ast/src/util/classify.rs52
-rw-r--r--compiler/rustc_ast/src/util/comments.rs255
-rw-r--r--compiler/rustc_ast/src/util/comments/tests.rs61
-rw-r--r--compiler/rustc_ast/src/util/literal.rs336
-rw-r--r--compiler/rustc_ast/src/util/parser.rs406
-rw-r--r--compiler/rustc_ast/src/util/unicode.rs35
-rw-r--r--compiler/rustc_ast/src/visit.rs959
-rw-r--r--compiler/rustc_ast_lowering/Cargo.toml23
-rw-r--r--compiler/rustc_ast_lowering/src/asm.rs485
-rw-r--r--compiler/rustc_ast_lowering/src/block.rs122
-rw-r--r--compiler/rustc_ast_lowering/src/expr.rs1914
-rw-r--r--compiler/rustc_ast_lowering/src/index.rs346
-rw-r--r--compiler/rustc_ast_lowering/src/item.rs1513
-rw-r--r--compiler/rustc_ast_lowering/src/lib.rs2501
-rw-r--r--compiler/rustc_ast_lowering/src/lifetime_collector.rs115
-rw-r--r--compiler/rustc_ast_lowering/src/pat.rs350
-rw-r--r--compiler/rustc_ast_lowering/src/path.rs406
-rw-r--r--compiler/rustc_ast_passes/Cargo.toml18
-rw-r--r--compiler/rustc_ast_passes/src/ast_validation.rs1909
-rw-r--r--compiler/rustc_ast_passes/src/feature_gate.rs901
-rw-r--r--compiler/rustc_ast_passes/src/lib.rs18
-rw-r--r--compiler/rustc_ast_passes/src/node_count.rs135
-rw-r--r--compiler/rustc_ast_passes/src/show_span.rs65
-rw-r--r--compiler/rustc_ast_pretty/Cargo.toml11
-rw-r--r--compiler/rustc_ast_pretty/src/helpers.rs48
-rw-r--r--compiler/rustc_ast_pretty/src/lib.rs8
-rw-r--r--compiler/rustc_ast_pretty/src/pp.rs451
-rw-r--r--compiler/rustc_ast_pretty/src/pp/convenience.rs94
-rw-r--r--compiler/rustc_ast_pretty/src/pp/ring.rs77
-rw-r--r--compiler/rustc_ast_pretty/src/pprust/mod.rs86
-rw-r--r--compiler/rustc_ast_pretty/src/pprust/state.rs1770
-rw-r--r--compiler/rustc_ast_pretty/src/pprust/state/delimited.rs41
-rw-r--r--compiler/rustc_ast_pretty/src/pprust/state/expr.rs621
-rw-r--r--compiler/rustc_ast_pretty/src/pprust/state/item.rs708
-rw-r--r--compiler/rustc_ast_pretty/src/pprust/tests.rs63
49 files changed, 24568 insertions, 0 deletions
diff --git a/compiler/rustc_ast/Cargo.toml b/compiler/rustc_ast/Cargo.toml
new file mode 100644
index 000000000..9822e9864
--- /dev/null
+++ b/compiler/rustc_ast/Cargo.toml
@@ -0,0 +1,18 @@
+[package]
+name = "rustc_ast"
+version = "0.0.0"
+edition = "2021"
+
+[lib]
+doctest = false
+
+[dependencies]
+rustc_serialize = { path = "../rustc_serialize" }
+tracing = "0.1"
+rustc_span = { path = "../rustc_span" }
+rustc_data_structures = { path = "../rustc_data_structures" }
+rustc_index = { path = "../rustc_index" }
+rustc_lexer = { path = "../rustc_lexer" }
+rustc_macros = { path = "../rustc_macros" }
+smallvec = { version = "1.8.1", features = ["union", "may_dangle"] }
+bitflags = "1.2.1"
diff --git a/compiler/rustc_ast/README.md b/compiler/rustc_ast/README.md
new file mode 100644
index 000000000..b2b90fed0
--- /dev/null
+++ b/compiler/rustc_ast/README.md
@@ -0,0 +1,8 @@
+The `rustc_ast` crate contains those things concerned purely with syntax
+– that is, the AST ("abstract syntax tree"), along with some definitions for tokens and token streams, data structures/traits for mutating ASTs, and shared definitions for other AST-related parts of the compiler (like the lexer and macro-expansion).
+
+For more information about how these things work in rustc, see the
+rustc dev guide:
+
+- [Parsing](https://rustc-dev-guide.rust-lang.org/the-parser.html)
+- [Macro Expansion](https://rustc-dev-guide.rust-lang.org/macro-expansion.html)
diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs
new file mode 100644
index 000000000..870a7c0be
--- /dev/null
+++ b/compiler/rustc_ast/src/ast.rs
@@ -0,0 +1,3051 @@
+//! The Rust abstract syntax tree module.
+//!
+//! This module contains common structures forming the language AST.
+//! Two main entities in the module are [`Item`] (which represents an AST element with
+//! additional metadata), and [`ItemKind`] (which represents a concrete type and contains
+//! information specific to the type of the item).
+//!
+//! Other module items worth mentioning:
+//! - [`Ty`] and [`TyKind`]: A parsed Rust type.
+//! - [`Expr`] and [`ExprKind`]: A parsed Rust expression.
+//! - [`Pat`] and [`PatKind`]: A parsed Rust pattern. Patterns are often dual to expressions.
+//! - [`Stmt`] and [`StmtKind`]: An executable action that does not return a value.
+//! - [`FnDecl`], [`FnHeader`] and [`Param`]: Metadata associated with a function declaration.
+//! - [`Generics`], [`GenericParam`], [`WhereClause`]: Metadata associated with generic parameters.
+//! - [`EnumDef`] and [`Variant`]: Enum declaration.
+//! - [`Lit`] and [`LitKind`]: Literal expressions.
+//! - [`MacroDef`], [`MacStmtStyle`], [`MacCall`], [`MacDelimiter`]: Macro definition and invocation.
+//! - [`Attribute`]: Metadata associated with item.
+//! - [`UnOp`], [`BinOp`], and [`BinOpKind`]: Unary and binary operators.
+
+pub use crate::util::parser::ExprPrecedence;
+pub use GenericArgs::*;
+pub use UnsafeSource::*;
+
+use crate::ptr::P;
+use crate::token::{self, CommentKind, Delimiter};
+use crate::tokenstream::{DelimSpan, LazyTokenStream, TokenStream};
+
+use rustc_data_structures::stable_hasher::{HashStable, StableHasher};
+use rustc_data_structures::stack::ensure_sufficient_stack;
+use rustc_data_structures::sync::Lrc;
+use rustc_data_structures::thin_vec::ThinVec;
+use rustc_macros::HashStable_Generic;
+use rustc_serialize::{Decodable, Decoder, Encodable, Encoder};
+use rustc_span::source_map::{respan, Spanned};
+use rustc_span::symbol::{kw, sym, Ident, Symbol};
+use rustc_span::{Span, DUMMY_SP};
+
+use std::cmp::Ordering;
+use std::convert::TryFrom;
+use std::fmt;
+use std::mem;
+
+/// A "Label" is an identifier of some point in sources,
+/// e.g. in the following code:
+///
+/// ```rust
+/// 'outer: loop {
+/// break 'outer;
+/// }
+/// ```
+///
+/// `'outer` is a label.
+#[derive(Clone, Encodable, Decodable, Copy, HashStable_Generic, Eq, PartialEq)]
+pub struct Label {
+ pub ident: Ident,
+}
+
+impl fmt::Debug for Label {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ write!(f, "label({:?})", self.ident)
+ }
+}
+
+/// A "Lifetime" is an annotation of the scope in which variable
+/// can be used, e.g. `'a` in `&'a i32`.
+#[derive(Clone, Encodable, Decodable, Copy, PartialEq, Eq)]
+pub struct Lifetime {
+ pub id: NodeId,
+ pub ident: Ident,
+}
+
+impl fmt::Debug for Lifetime {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ write!(f, "lifetime({}: {})", self.id, self)
+ }
+}
+
+impl fmt::Display for Lifetime {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ write!(f, "{}", self.ident.name)
+ }
+}
+
+/// A "Path" is essentially Rust's notion of a name.
+///
+/// It's represented as a sequence of identifiers,
+/// along with a bunch of supporting information.
+///
+/// E.g., `std::cmp::PartialEq`.
+#[derive(Clone, Encodable, Decodable, Debug)]
+pub struct Path {
+ pub span: Span,
+ /// The segments in the path: the things separated by `::`.
+ /// Global paths begin with `kw::PathRoot`.
+ pub segments: Vec<PathSegment>,
+ pub tokens: Option<LazyTokenStream>,
+}
+
+impl PartialEq<Symbol> for Path {
+ #[inline]
+ fn eq(&self, symbol: &Symbol) -> bool {
+ self.segments.len() == 1 && { self.segments[0].ident.name == *symbol }
+ }
+}
+
+impl<CTX: rustc_span::HashStableContext> HashStable<CTX> for Path {
+ fn hash_stable(&self, hcx: &mut CTX, hasher: &mut StableHasher) {
+ self.segments.len().hash_stable(hcx, hasher);
+ for segment in &self.segments {
+ segment.ident.hash_stable(hcx, hasher);
+ }
+ }
+}
+
+impl Path {
+ // Convert a span and an identifier to the corresponding
+ // one-segment path.
+ pub fn from_ident(ident: Ident) -> Path {
+ Path { segments: vec![PathSegment::from_ident(ident)], span: ident.span, tokens: None }
+ }
+
+ pub fn is_global(&self) -> bool {
+ !self.segments.is_empty() && self.segments[0].ident.name == kw::PathRoot
+ }
+}
+
+/// A segment of a path: an identifier, an optional lifetime, and a set of types.
+///
+/// E.g., `std`, `String` or `Box<T>`.
+#[derive(Clone, Encodable, Decodable, Debug)]
+pub struct PathSegment {
+ /// The identifier portion of this path segment.
+ pub ident: Ident,
+
+ pub id: NodeId,
+
+ /// Type/lifetime parameters attached to this path. They come in
+ /// two flavors: `Path<A,B,C>` and `Path(A,B) -> C`.
+ /// `None` means that no parameter list is supplied (`Path`),
+ /// `Some` means that parameter list is supplied (`Path<X, Y>`)
+ /// but it can be empty (`Path<>`).
+ /// `P` is used as a size optimization for the common case with no parameters.
+ pub args: Option<P<GenericArgs>>,
+}
+
+impl PathSegment {
+ pub fn from_ident(ident: Ident) -> Self {
+ PathSegment { ident, id: DUMMY_NODE_ID, args: None }
+ }
+
+ pub fn path_root(span: Span) -> Self {
+ PathSegment::from_ident(Ident::new(kw::PathRoot, span))
+ }
+
+ pub fn span(&self) -> Span {
+ match &self.args {
+ Some(args) => self.ident.span.to(args.span()),
+ None => self.ident.span,
+ }
+ }
+}
+
+/// The arguments of a path segment.
+///
+/// E.g., `<A, B>` as in `Foo<A, B>` or `(A, B)` as in `Foo(A, B)`.
+#[derive(Clone, Encodable, Decodable, Debug)]
+pub enum GenericArgs {
+ /// The `<'a, A, B, C>` in `foo::bar::baz::<'a, A, B, C>`.
+ AngleBracketed(AngleBracketedArgs),
+ /// The `(A, B)` and `C` in `Foo(A, B) -> C`.
+ Parenthesized(ParenthesizedArgs),
+}
+
+impl GenericArgs {
+ pub fn is_angle_bracketed(&self) -> bool {
+ matches!(self, AngleBracketed(..))
+ }
+
+ pub fn span(&self) -> Span {
+ match *self {
+ AngleBracketed(ref data) => data.span,
+ Parenthesized(ref data) => data.span,
+ }
+ }
+}
+
+/// Concrete argument in the sequence of generic args.
+#[derive(Clone, Encodable, Decodable, Debug)]
+pub enum GenericArg {
+ /// `'a` in `Foo<'a>`
+ Lifetime(Lifetime),
+ /// `Bar` in `Foo<Bar>`
+ Type(P<Ty>),
+ /// `1` in `Foo<1>`
+ Const(AnonConst),
+}
+
+impl GenericArg {
+ pub fn span(&self) -> Span {
+ match self {
+ GenericArg::Lifetime(lt) => lt.ident.span,
+ GenericArg::Type(ty) => ty.span,
+ GenericArg::Const(ct) => ct.value.span,
+ }
+ }
+}
+
+/// A path like `Foo<'a, T>`.
+#[derive(Clone, Encodable, Decodable, Debug, Default)]
+pub struct AngleBracketedArgs {
+ /// The overall span.
+ pub span: Span,
+ /// The comma separated parts in the `<...>`.
+ pub args: Vec<AngleBracketedArg>,
+}
+
+/// Either an argument for a parameter e.g., `'a`, `Vec<u8>`, `0`,
+/// or a constraint on an associated item, e.g., `Item = String` or `Item: Bound`.
+#[derive(Clone, Encodable, Decodable, Debug)]
+pub enum AngleBracketedArg {
+ /// Argument for a generic parameter.
+ Arg(GenericArg),
+ /// Constraint for an associated item.
+ Constraint(AssocConstraint),
+}
+
+impl AngleBracketedArg {
+ pub fn span(&self) -> Span {
+ match self {
+ AngleBracketedArg::Arg(arg) => arg.span(),
+ AngleBracketedArg::Constraint(constraint) => constraint.span,
+ }
+ }
+}
+
+impl Into<Option<P<GenericArgs>>> for AngleBracketedArgs {
+ fn into(self) -> Option<P<GenericArgs>> {
+ Some(P(GenericArgs::AngleBracketed(self)))
+ }
+}
+
+impl Into<Option<P<GenericArgs>>> for ParenthesizedArgs {
+ fn into(self) -> Option<P<GenericArgs>> {
+ Some(P(GenericArgs::Parenthesized(self)))
+ }
+}
+
+/// A path like `Foo(A, B) -> C`.
+#[derive(Clone, Encodable, Decodable, Debug)]
+pub struct ParenthesizedArgs {
+ /// ```text
+ /// Foo(A, B) -> C
+ /// ^^^^^^^^^^^^^^
+ /// ```
+ pub span: Span,
+
+ /// `(A, B)`
+ pub inputs: Vec<P<Ty>>,
+
+ /// ```text
+ /// Foo(A, B) -> C
+ /// ^^^^^^
+ /// ```
+ pub inputs_span: Span,
+
+ /// `C`
+ pub output: FnRetTy,
+}
+
+impl ParenthesizedArgs {
+ pub fn as_angle_bracketed_args(&self) -> AngleBracketedArgs {
+ let args = self
+ .inputs
+ .iter()
+ .cloned()
+ .map(|input| AngleBracketedArg::Arg(GenericArg::Type(input)))
+ .collect();
+ AngleBracketedArgs { span: self.inputs_span, args }
+ }
+}
+
+pub use crate::node_id::{NodeId, CRATE_NODE_ID, DUMMY_NODE_ID};
+
+/// A modifier on a bound, e.g., `?Trait` or `~const Trait`.
+///
+/// Negative bounds should also be handled here.
+#[derive(Copy, Clone, PartialEq, Eq, Encodable, Decodable, Debug)]
+pub enum TraitBoundModifier {
+ /// No modifiers
+ None,
+
+ /// `?Trait`
+ Maybe,
+
+ /// `~const Trait`
+ MaybeConst,
+
+ /// `~const ?Trait`
+ //
+ // This parses but will be rejected during AST validation.
+ MaybeConstMaybe,
+}
+
+/// The AST represents all type param bounds as types.
+/// `typeck::collect::compute_bounds` matches these against
+/// the "special" built-in traits (see `middle::lang_items`) and
+/// detects `Copy`, `Send` and `Sync`.
+#[derive(Clone, Encodable, Decodable, Debug)]
+pub enum GenericBound {
+ Trait(PolyTraitRef, TraitBoundModifier),
+ Outlives(Lifetime),
+}
+
+impl GenericBound {
+ pub fn span(&self) -> Span {
+ match self {
+ GenericBound::Trait(ref t, ..) => t.span,
+ GenericBound::Outlives(ref l) => l.ident.span,
+ }
+ }
+}
+
+pub type GenericBounds = Vec<GenericBound>;
+
+/// Specifies the enforced ordering for generic parameters. In the future,
+/// if we wanted to relax this order, we could override `PartialEq` and
+/// `PartialOrd`, to allow the kinds to be unordered.
+#[derive(Hash, Clone, Copy)]
+pub enum ParamKindOrd {
+ Lifetime,
+ Type,
+ Const,
+ // `Infer` is not actually constructed directly from the AST, but is implicitly constructed
+ // during HIR lowering, and `ParamKindOrd` will implicitly order inferred variables last.
+ Infer,
+}
+
+impl Ord for ParamKindOrd {
+ fn cmp(&self, other: &Self) -> Ordering {
+ use ParamKindOrd::*;
+ let to_int = |v| match v {
+ Lifetime => 0,
+ Infer | Type | Const => 1,
+ };
+
+ to_int(*self).cmp(&to_int(*other))
+ }
+}
+impl PartialOrd for ParamKindOrd {
+ fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
+ Some(self.cmp(other))
+ }
+}
+impl PartialEq for ParamKindOrd {
+ fn eq(&self, other: &Self) -> bool {
+ self.cmp(other) == Ordering::Equal
+ }
+}
+impl Eq for ParamKindOrd {}
+
+impl fmt::Display for ParamKindOrd {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ match self {
+ ParamKindOrd::Lifetime => "lifetime".fmt(f),
+ ParamKindOrd::Type => "type".fmt(f),
+ ParamKindOrd::Const { .. } => "const".fmt(f),
+ ParamKindOrd::Infer => "infer".fmt(f),
+ }
+ }
+}
+
+#[derive(Clone, Encodable, Decodable, Debug)]
+pub enum GenericParamKind {
+ /// A lifetime definition (e.g., `'a: 'b + 'c + 'd`).
+ Lifetime,
+ Type {
+ default: Option<P<Ty>>,
+ },
+ Const {
+ ty: P<Ty>,
+ /// Span of the `const` keyword.
+ kw_span: Span,
+ /// Optional default value for the const generic param
+ default: Option<AnonConst>,
+ },
+}
+
+#[derive(Clone, Encodable, Decodable, Debug)]
+pub struct GenericParam {
+ pub id: NodeId,
+ pub ident: Ident,
+ pub attrs: AttrVec,
+ pub bounds: GenericBounds,
+ pub is_placeholder: bool,
+ pub kind: GenericParamKind,
+ pub colon_span: Option<Span>,
+}
+
+impl GenericParam {
+ pub fn span(&self) -> Span {
+ match &self.kind {
+ GenericParamKind::Lifetime | GenericParamKind::Type { default: None } => {
+ self.ident.span
+ }
+ GenericParamKind::Type { default: Some(ty) } => self.ident.span.to(ty.span),
+ GenericParamKind::Const { kw_span, default: Some(default), .. } => {
+ kw_span.to(default.value.span)
+ }
+ GenericParamKind::Const { kw_span, default: None, ty } => kw_span.to(ty.span),
+ }
+ }
+}
+
+/// Represents lifetime, type and const parameters attached to a declaration of
+/// a function, enum, trait, etc.
+#[derive(Clone, Encodable, Decodable, Debug)]
+pub struct Generics {
+ pub params: Vec<GenericParam>,
+ pub where_clause: WhereClause,
+ pub span: Span,
+}
+
+impl Default for Generics {
+ /// Creates an instance of `Generics`.
+ fn default() -> Generics {
+ Generics {
+ params: Vec::new(),
+ where_clause: WhereClause {
+ has_where_token: false,
+ predicates: Vec::new(),
+ span: DUMMY_SP,
+ },
+ span: DUMMY_SP,
+ }
+ }
+}
+
+/// A where-clause in a definition.
+#[derive(Clone, Encodable, Decodable, Debug)]
+pub struct WhereClause {
+ /// `true` if we ate a `where` token: this can happen
+ /// 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 span: Span,
+}
+
+/// A single predicate in a where-clause.
+#[derive(Clone, Encodable, Decodable, Debug)]
+pub enum WherePredicate {
+ /// A type binding (e.g., `for<'c> Foo: Send + Clone + 'c`).
+ BoundPredicate(WhereBoundPredicate),
+ /// A lifetime predicate (e.g., `'a: 'b + 'c`).
+ RegionPredicate(WhereRegionPredicate),
+ /// An equality predicate (unsupported).
+ EqPredicate(WhereEqPredicate),
+}
+
+impl WherePredicate {
+ pub fn span(&self) -> Span {
+ match self {
+ WherePredicate::BoundPredicate(p) => p.span,
+ WherePredicate::RegionPredicate(p) => p.span,
+ WherePredicate::EqPredicate(p) => p.span,
+ }
+ }
+}
+
+/// A type bound.
+///
+/// E.g., `for<'c> Foo: Send + Clone + 'c`.
+#[derive(Clone, Encodable, Decodable, Debug)]
+pub struct WhereBoundPredicate {
+ pub span: Span,
+ /// Any generics from a `for` binding.
+ pub bound_generic_params: Vec<GenericParam>,
+ /// The type being bounded.
+ pub bounded_ty: P<Ty>,
+ /// Trait and lifetime bounds (`Clone + Send + 'static`).
+ pub bounds: GenericBounds,
+}
+
+/// A lifetime predicate.
+///
+/// E.g., `'a: 'b + 'c`.
+#[derive(Clone, Encodable, Decodable, Debug)]
+pub struct WhereRegionPredicate {
+ pub span: Span,
+ pub lifetime: Lifetime,
+ pub bounds: GenericBounds,
+}
+
+/// An equality predicate (unsupported).
+///
+/// E.g., `T = int`.
+#[derive(Clone, Encodable, Decodable, Debug)]
+pub struct WhereEqPredicate {
+ pub id: NodeId,
+ pub span: Span,
+ pub lhs_ty: P<Ty>,
+ pub rhs_ty: P<Ty>,
+}
+
+#[derive(Clone, Encodable, Decodable, Debug)]
+pub struct Crate {
+ pub attrs: Vec<Attribute>,
+ pub items: Vec<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.
+ pub id: NodeId,
+ pub is_placeholder: bool,
+}
+
+/// Possible values inside of compile-time attribute lists.
+///
+/// E.g., the '..' in `#[name(..)]`.
+#[derive(Clone, Encodable, Decodable, Debug, HashStable_Generic)]
+pub enum NestedMetaItem {
+ /// A full MetaItem, for recursive meta items.
+ MetaItem(MetaItem),
+ /// A literal.
+ ///
+ /// E.g., `"foo"`, `64`, `true`.
+ Literal(Lit),
+}
+
+/// A spanned compile-time attribute item.
+///
+/// E.g., `#[test]`, `#[derive(..)]`, `#[rustfmt::skip]` or `#[feature = "foo"]`.
+#[derive(Clone, Encodable, Decodable, Debug, HashStable_Generic)]
+pub struct MetaItem {
+ pub path: Path,
+ pub kind: MetaItemKind,
+ pub span: Span,
+}
+
+/// A compile-time attribute item.
+///
+/// E.g., `#[test]`, `#[derive(..)]` or `#[feature = "foo"]`.
+#[derive(Clone, Encodable, Decodable, Debug, HashStable_Generic)]
+pub enum MetaItemKind {
+ /// Word meta item.
+ ///
+ /// E.g., `test` as in `#[test]`.
+ Word,
+ /// List meta item.
+ ///
+ /// E.g., `derive(..)` as in `#[derive(..)]`.
+ List(Vec<NestedMetaItem>),
+ /// Name value meta item.
+ ///
+ /// E.g., `feature = "foo"` as in `#[feature = "foo"]`.
+ NameValue(Lit),
+}
+
+/// A block (`{ .. }`).
+///
+/// E.g., `{ .. }` as in `fn foo() { .. }`.
+#[derive(Clone, Encodable, Decodable, Debug)]
+pub struct Block {
+ /// The statements in the block.
+ pub stmts: Vec<Stmt>,
+ pub id: NodeId,
+ /// Distinguishes between `unsafe { ... }` and `{ ... }`.
+ pub rules: BlockCheckMode,
+ pub span: Span,
+ pub tokens: Option<LazyTokenStream>,
+ /// The following *isn't* a parse error, but will cause multiple errors in following stages.
+ /// ```compile_fail
+ /// let x = {
+ /// foo: var
+ /// };
+ /// ```
+ /// #34255
+ pub could_be_bare_literal: bool,
+}
+
+/// A match pattern.
+///
+/// Patterns appear in match statements and some other contexts, such as `let` and `if let`.
+#[derive(Clone, Encodable, Decodable, Debug)]
+pub struct Pat {
+ pub id: NodeId,
+ pub kind: PatKind,
+ pub span: Span,
+ pub tokens: Option<LazyTokenStream>,
+}
+
+impl Pat {
+ /// Attempt reparsing the pattern as a type.
+ /// This is intended for use by diagnostics.
+ pub fn to_ty(&self) -> Option<P<Ty>> {
+ let kind = match &self.kind {
+ // In a type expression `_` is an inference variable.
+ PatKind::Wild => TyKind::Infer,
+ // An IDENT pattern with no binding mode would be valid as path to a type. E.g. `u32`.
+ PatKind::Ident(BindingMode::ByValue(Mutability::Not), ident, None) => {
+ TyKind::Path(None, Path::from_ident(*ident))
+ }
+ PatKind::Path(qself, path) => TyKind::Path(qself.clone(), path.clone()),
+ PatKind::MacCall(mac) => TyKind::MacCall(mac.clone()),
+ // `&mut? P` can be reinterpreted as `&mut? T` where `T` is `P` reparsed as a type.
+ PatKind::Ref(pat, mutbl) => {
+ pat.to_ty().map(|ty| TyKind::Rptr(None, MutTy { ty, mutbl: *mutbl }))?
+ }
+ // A slice/array pattern `[P]` can be reparsed as `[T]`, an unsized array,
+ // when `P` can be reparsed as a type `T`.
+ PatKind::Slice(pats) if pats.len() == 1 => pats[0].to_ty().map(TyKind::Slice)?,
+ // 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());
+ // FIXME(#48994) - could just be collected into an Option<Vec>
+ for pat in pats {
+ tys.push(pat.to_ty()?);
+ }
+ TyKind::Tup(tys)
+ }
+ _ => return None,
+ };
+
+ Some(P(Ty { kind, id: self.id, span: self.span, tokens: None }))
+ }
+
+ /// Walk top-down and call `it` in each place where a pattern occurs
+ /// starting with the root pattern `walk` is called on. If `it` returns
+ /// false then we will descend no further but siblings will be processed.
+ pub fn walk(&self, it: &mut impl FnMut(&Pat) -> bool) {
+ if !it(self) {
+ return;
+ }
+
+ match &self.kind {
+ // Walk into the pattern associated with `Ident` (if any).
+ PatKind::Ident(_, _, Some(p)) => p.walk(it),
+
+ // Walk into each field of struct.
+ PatKind::Struct(_, _, fields, _) => fields.iter().for_each(|field| field.pat.walk(it)),
+
+ // Sequence of patterns.
+ PatKind::TupleStruct(_, _, s)
+ | PatKind::Tuple(s)
+ | PatKind::Slice(s)
+ | PatKind::Or(s) => s.iter().for_each(|p| p.walk(it)),
+
+ // Trivial wrappers over inner patterns.
+ PatKind::Box(s) | PatKind::Ref(s, _) | PatKind::Paren(s) => s.walk(it),
+
+ // These patterns do not contain subpatterns, skip.
+ PatKind::Wild
+ | PatKind::Rest
+ | PatKind::Lit(_)
+ | PatKind::Range(..)
+ | PatKind::Ident(..)
+ | PatKind::Path(..)
+ | PatKind::MacCall(_) => {}
+ }
+ }
+
+ /// Is this a `..` pattern?
+ pub fn is_rest(&self) -> bool {
+ matches!(self.kind, PatKind::Rest)
+ }
+}
+
+/// A single field in a struct pattern.
+///
+/// Patterns like the fields of `Foo { x, ref y, ref mut z }`
+/// are treated the same as `x: x, y: ref y, z: ref mut z`,
+/// except when `is_shorthand` is true.
+#[derive(Clone, Encodable, Decodable, Debug)]
+pub struct PatField {
+ /// The identifier for the field.
+ pub ident: Ident,
+ /// The pattern the field is destructured to.
+ pub pat: P<Pat>,
+ pub is_shorthand: bool,
+ pub attrs: AttrVec,
+ pub id: NodeId,
+ pub span: Span,
+ pub is_placeholder: bool,
+}
+
+#[derive(Clone, PartialEq, Encodable, Decodable, Debug, Copy)]
+pub enum BindingMode {
+ ByRef(Mutability),
+ ByValue(Mutability),
+}
+
+#[derive(Clone, Encodable, Decodable, Debug)]
+pub enum RangeEnd {
+ /// `..=` or `...`
+ Included(RangeSyntax),
+ /// `..`
+ Excluded,
+}
+
+#[derive(Clone, Encodable, Decodable, Debug)]
+pub enum RangeSyntax {
+ /// `...`
+ DotDotDot,
+ /// `..=`
+ DotDotEq,
+}
+
+/// All the different flavors of pattern that Rust recognizes.
+#[derive(Clone, Encodable, Decodable, Debug)]
+pub enum PatKind {
+ /// Represents a wildcard pattern (`_`).
+ Wild,
+
+ /// A `PatKind::Ident` may either be a new bound variable (`ref mut binding @ OPT_SUBPATTERN`),
+ /// or a unit struct/variant pattern, or a const pattern (in the last two cases the third
+ /// field must be `None`). Disambiguation cannot be done with parser alone, so it happens
+ /// during name resolution.
+ Ident(BindingMode, Ident, Option<P<Pat>>),
+
+ /// A struct or struct variant pattern (e.g., `Variant {x, y, ..}`).
+ /// The `bool` is `true` in the presence of a `..`.
+ Struct(Option<QSelf>, Path, Vec<PatField>, /* recovered */ bool),
+
+ /// A tuple struct/variant pattern (`Variant(x, y, .., z)`).
+ TupleStruct(Option<QSelf>, Path, Vec<P<Pat>>),
+
+ /// An or-pattern `A | B | C`.
+ /// Invariant: `pats.len() >= 2`.
+ Or(Vec<P<Pat>>),
+
+ /// A possibly qualified path pattern.
+ /// Unqualified path patterns `A::B::C` can legally refer to variants, structs, constants
+ /// or associated constants. Qualified path patterns `<A>::B::C`/`<A as Trait>::B::C` can
+ /// only legally refer to associated constants.
+ Path(Option<QSelf>, Path),
+
+ /// A tuple pattern (`(a, b)`).
+ Tuple(Vec<P<Pat>>),
+
+ /// A `box` pattern.
+ Box(P<Pat>),
+
+ /// A reference pattern (e.g., `&mut (a, b)`).
+ Ref(P<Pat>, Mutability),
+
+ /// A literal.
+ Lit(P<Expr>),
+
+ /// A range pattern (e.g., `1...2`, `1..2`, `1..`, `..2`, `1..=2`, `..=2`).
+ Range(Option<P<Expr>>, Option<P<Expr>>, Spanned<RangeEnd>),
+
+ /// A slice pattern `[a, b, c]`.
+ Slice(Vec<P<Pat>>),
+
+ /// A rest pattern `..`.
+ ///
+ /// Syntactically it is valid anywhere.
+ ///
+ /// Semantically however, it only has meaning immediately inside:
+ /// - a slice pattern: `[a, .., b]`,
+ /// - a binding pattern immediately inside a slice pattern: `[a, r @ ..]`,
+ /// - a tuple pattern: `(a, .., b)`,
+ /// - a tuple struct/variant pattern: `$path(a, .., b)`.
+ ///
+ /// In all of these cases, an additional restriction applies,
+ /// only one rest pattern may occur in the pattern sequences.
+ Rest,
+
+ /// Parentheses in patterns used for grouping (i.e., `(PAT)`).
+ Paren(P<Pat>),
+
+ /// A macro pattern; pre-expansion.
+ MacCall(MacCall),
+}
+
+#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Copy)]
+#[derive(HashStable_Generic, Encodable, Decodable)]
+pub enum Mutability {
+ Mut,
+ Not,
+}
+
+impl Mutability {
+ pub fn invert(self) -> Self {
+ match self {
+ Mutability::Mut => Mutability::Not,
+ Mutability::Not => Mutability::Mut,
+ }
+ }
+
+ pub fn prefix_str(&self) -> &'static str {
+ match self {
+ Mutability::Mut => "mut ",
+ Mutability::Not => "",
+ }
+ }
+}
+
+/// The kind of borrow in an `AddrOf` expression,
+/// e.g., `&place` or `&raw const place`.
+#[derive(Clone, Copy, PartialEq, Eq, Debug)]
+#[derive(Encodable, Decodable, HashStable_Generic)]
+pub enum BorrowKind {
+ /// A normal borrow, `&$expr` or `&mut $expr`.
+ /// The resulting type is either `&'a T` or `&'a mut T`
+ /// where `T = typeof($expr)` and `'a` is some lifetime.
+ Ref,
+ /// A raw borrow, `&raw const $expr` or `&raw mut $expr`.
+ /// The resulting type is either `*const T` or `*mut T`
+ /// where `T = typeof($expr)`.
+ Raw,
+}
+
+#[derive(Clone, PartialEq, Encodable, Decodable, Debug, Copy)]
+pub enum BinOpKind {
+ /// The `+` operator (addition)
+ Add,
+ /// The `-` operator (subtraction)
+ Sub,
+ /// The `*` operator (multiplication)
+ Mul,
+ /// The `/` operator (division)
+ Div,
+ /// The `%` operator (modulus)
+ Rem,
+ /// The `&&` operator (logical and)
+ And,
+ /// The `||` operator (logical or)
+ Or,
+ /// The `^` operator (bitwise xor)
+ BitXor,
+ /// The `&` operator (bitwise and)
+ BitAnd,
+ /// The `|` operator (bitwise or)
+ BitOr,
+ /// The `<<` operator (shift left)
+ Shl,
+ /// The `>>` operator (shift right)
+ Shr,
+ /// The `==` operator (equality)
+ Eq,
+ /// The `<` operator (less than)
+ Lt,
+ /// The `<=` operator (less than or equal to)
+ Le,
+ /// The `!=` operator (not equal to)
+ Ne,
+ /// The `>=` operator (greater than or equal to)
+ Ge,
+ /// The `>` operator (greater than)
+ Gt,
+}
+
+impl BinOpKind {
+ pub fn to_string(&self) -> &'static str {
+ use BinOpKind::*;
+ match *self {
+ Add => "+",
+ Sub => "-",
+ Mul => "*",
+ Div => "/",
+ Rem => "%",
+ And => "&&",
+ Or => "||",
+ BitXor => "^",
+ BitAnd => "&",
+ BitOr => "|",
+ Shl => "<<",
+ Shr => ">>",
+ Eq => "==",
+ Lt => "<",
+ Le => "<=",
+ Ne => "!=",
+ Ge => ">=",
+ Gt => ">",
+ }
+ }
+ pub fn lazy(&self) -> bool {
+ matches!(self, BinOpKind::And | BinOpKind::Or)
+ }
+
+ pub fn is_comparison(&self) -> bool {
+ use BinOpKind::*;
+ // Note for developers: please keep this as is;
+ // we want compilation to fail if another variant is added.
+ match *self {
+ Eq | Lt | Le | Ne | Gt | Ge => true,
+ And | Or | Add | Sub | Mul | Div | Rem | BitXor | BitAnd | BitOr | Shl | Shr => false,
+ }
+ }
+}
+
+pub type BinOp = Spanned<BinOpKind>;
+
+/// Unary operator.
+///
+/// Note that `&data` is not an operator, it's an `AddrOf` expression.
+#[derive(Clone, Encodable, Decodable, Debug, Copy)]
+pub enum UnOp {
+ /// The `*` operator for dereferencing
+ Deref,
+ /// The `!` operator for logical inversion
+ Not,
+ /// The `-` operator for negation
+ Neg,
+}
+
+impl UnOp {
+ pub fn to_string(op: UnOp) -> &'static str {
+ match op {
+ UnOp::Deref => "*",
+ UnOp::Not => "!",
+ UnOp::Neg => "-",
+ }
+ }
+}
+
+/// A statement
+#[derive(Clone, Encodable, Decodable, Debug)]
+pub struct Stmt {
+ pub id: NodeId,
+ pub kind: StmtKind,
+ pub span: Span,
+}
+
+impl Stmt {
+ pub fn has_trailing_semicolon(&self) -> bool {
+ match &self.kind {
+ StmtKind::Semi(_) => true,
+ StmtKind::MacCall(mac) => matches!(mac.style, MacStmtStyle::Semicolon),
+ _ => false,
+ }
+ }
+
+ /// Converts a parsed `Stmt` to a `Stmt` with
+ /// a trailing semicolon.
+ ///
+ /// This only modifies the parsed AST struct, not the attached
+ /// `LazyTokenStream`. The parser is responsible for calling
+ /// `CreateTokenStream::add_trailing_semi` when there is actually
+ /// a semicolon in the tokenstream.
+ pub fn add_trailing_semicolon(mut self) -> Self {
+ self.kind = match self.kind {
+ StmtKind::Expr(expr) => StmtKind::Semi(expr),
+ StmtKind::MacCall(mac) => {
+ StmtKind::MacCall(mac.map(|MacCallStmt { mac, style: _, attrs, tokens }| {
+ MacCallStmt { mac, style: MacStmtStyle::Semicolon, attrs, tokens }
+ }))
+ }
+ kind => kind,
+ };
+
+ self
+ }
+
+ pub fn is_item(&self) -> bool {
+ matches!(self.kind, StmtKind::Item(_))
+ }
+
+ pub fn is_expr(&self) -> bool {
+ matches!(self.kind, StmtKind::Expr(_))
+ }
+}
+
+#[derive(Clone, Encodable, Decodable, Debug)]
+pub enum StmtKind {
+ /// A local (let) binding.
+ Local(P<Local>),
+ /// An item definition.
+ Item(P<Item>),
+ /// Expr without trailing semi-colon.
+ Expr(P<Expr>),
+ /// Expr with a trailing semi-colon.
+ Semi(P<Expr>),
+ /// Just a trailing semi-colon.
+ Empty,
+ /// Macro.
+ MacCall(P<MacCallStmt>),
+}
+
+#[derive(Clone, Encodable, Decodable, Debug)]
+pub struct MacCallStmt {
+ pub mac: MacCall,
+ pub style: MacStmtStyle,
+ pub attrs: AttrVec,
+ pub tokens: Option<LazyTokenStream>,
+}
+
+#[derive(Clone, Copy, PartialEq, Encodable, Decodable, Debug)]
+pub enum MacStmtStyle {
+ /// The macro statement had a trailing semicolon (e.g., `foo! { ... };`
+ /// `foo!(...);`, `foo![...];`).
+ Semicolon,
+ /// The macro statement had braces (e.g., `foo! { ... }`).
+ Braces,
+ /// The macro statement had parentheses or brackets and no semicolon (e.g.,
+ /// `foo!(...)`). All of these will end up being converted into macro
+ /// expressions.
+ NoBraces,
+}
+
+/// Local represents a `let` statement, e.g., `let <pat>:<ty> = <expr>;`.
+#[derive(Clone, Encodable, Decodable, Debug)]
+pub struct Local {
+ pub id: NodeId,
+ pub pat: P<Pat>,
+ pub ty: Option<P<Ty>>,
+ pub kind: LocalKind,
+ pub span: Span,
+ pub attrs: AttrVec,
+ pub tokens: Option<LazyTokenStream>,
+}
+
+#[derive(Clone, Encodable, Decodable, Debug)]
+pub enum LocalKind {
+ /// Local declaration.
+ /// Example: `let x;`
+ Decl,
+ /// Local declaration with an initializer.
+ /// Example: `let x = y;`
+ Init(P<Expr>),
+ /// Local declaration with an initializer and an `else` clause.
+ /// Example: `let Some(x) = y else { return };`
+ InitElse(P<Expr>, P<Block>),
+}
+
+impl LocalKind {
+ pub fn init(&self) -> Option<&Expr> {
+ match self {
+ Self::Decl => None,
+ Self::Init(i) | Self::InitElse(i, _) => Some(i),
+ }
+ }
+
+ pub fn init_else_opt(&self) -> Option<(&Expr, Option<&Block>)> {
+ match self {
+ Self::Decl => None,
+ Self::Init(init) => Some((init, None)),
+ Self::InitElse(init, els) => Some((init, Some(els))),
+ }
+ }
+}
+
+/// An arm of a 'match'.
+///
+/// E.g., `0..=10 => { println!("match!") }` as in
+///
+/// ```
+/// match 123 {
+/// 0..=10 => { println!("match!") },
+/// _ => { println!("no match!") },
+/// }
+/// ```
+#[derive(Clone, Encodable, Decodable, Debug)]
+pub struct Arm {
+ pub attrs: AttrVec,
+ /// Match arm pattern, e.g. `10` in `match foo { 10 => {}, _ => {} }`
+ pub pat: P<Pat>,
+ /// Match arm guard, e.g. `n > 10` in `match foo { n if n > 10 => {}, _ => {} }`
+ pub guard: Option<P<Expr>>,
+ /// Match arm body.
+ pub body: P<Expr>,
+ pub span: Span,
+ pub id: NodeId,
+ pub is_placeholder: bool,
+}
+
+/// A single field in a struct expression, e.g. `x: value` and `y` in `Foo { x: value, y }`.
+#[derive(Clone, Encodable, Decodable, Debug)]
+pub struct ExprField {
+ pub attrs: AttrVec,
+ pub id: NodeId,
+ pub span: Span,
+ pub ident: Ident,
+ pub expr: P<Expr>,
+ pub is_shorthand: bool,
+ pub is_placeholder: bool,
+}
+
+#[derive(Clone, PartialEq, Encodable, Decodable, Debug, Copy)]
+pub enum BlockCheckMode {
+ Default,
+ Unsafe(UnsafeSource),
+}
+
+#[derive(Clone, PartialEq, Encodable, Decodable, Debug, Copy)]
+pub enum UnsafeSource {
+ CompilerGenerated,
+ UserProvided,
+}
+
+/// A constant (expression) that's not an item or associated item,
+/// but needs its own `DefId` for type-checking, const-eval, etc.
+/// These are usually found nested inside types (e.g., array lengths)
+/// or expressions (e.g., repeat counts), and also used to define
+/// explicit discriminant values for enum variants.
+#[derive(Clone, Encodable, Decodable, Debug)]
+pub struct AnonConst {
+ pub id: NodeId,
+ pub value: P<Expr>,
+}
+
+/// An expression.
+#[derive(Clone, Encodable, Decodable, Debug)]
+pub struct Expr {
+ pub id: NodeId,
+ pub kind: ExprKind,
+ pub span: Span,
+ pub attrs: AttrVec,
+ pub tokens: Option<LazyTokenStream>,
+}
+
+impl Expr {
+ /// Returns `true` if this expression would be valid somewhere that expects a value;
+ /// for example, an `if` condition.
+ pub fn returns(&self) -> bool {
+ if let ExprKind::Block(ref block, _) = self.kind {
+ match block.stmts.last().map(|last_stmt| &last_stmt.kind) {
+ // Implicit return
+ Some(StmtKind::Expr(_)) => true,
+ // Last statement is an explicit return?
+ Some(StmtKind::Semi(expr)) => matches!(expr.kind, ExprKind::Ret(_)),
+ // This is a block that doesn't end in either an implicit or explicit return.
+ _ => false,
+ }
+ } else {
+ // This is not a block, it is a value.
+ true
+ }
+ }
+
+ /// Is this expr either `N`, or `{ N }`.
+ ///
+ /// If this is not the case, name resolution does not resolve `N` when using
+ /// `min_const_generics` as more complex expressions are not supported.
+ pub fn is_potential_trivial_const_param(&self) -> bool {
+ let this = if let ExprKind::Block(ref block, None) = self.kind {
+ if block.stmts.len() == 1 {
+ if let StmtKind::Expr(ref expr) = block.stmts[0].kind { expr } else { self }
+ } else {
+ self
+ }
+ } else {
+ self
+ };
+
+ if let ExprKind::Path(None, ref path) = this.kind {
+ if path.segments.len() == 1 && path.segments[0].args.is_none() {
+ return true;
+ }
+ }
+
+ false
+ }
+
+ 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),
+ TraitBoundModifier::None,
+ )),
+ _ => None,
+ }
+ }
+
+ pub fn peel_parens(&self) -> &Expr {
+ let mut expr = self;
+ while let ExprKind::Paren(inner) = &expr.kind {
+ expr = &inner;
+ }
+ expr
+ }
+
+ /// Attempts to reparse as `Ty` (for diagnostic purposes).
+ pub fn to_ty(&self) -> Option<P<Ty>> {
+ let kind = match &self.kind {
+ // Trivial conversions.
+ ExprKind::Path(qself, path) => TyKind::Path(qself.clone(), path.clone()),
+ ExprKind::MacCall(mac) => TyKind::MacCall(mac.clone()),
+
+ ExprKind::Paren(expr) => expr.to_ty().map(TyKind::Paren)?,
+
+ ExprKind::AddrOf(BorrowKind::Ref, mutbl, expr) => {
+ expr.to_ty().map(|ty| TyKind::Rptr(None, MutTy { ty, mutbl: *mutbl }))?
+ }
+
+ ExprKind::Repeat(expr, expr_len) => {
+ expr.to_ty().map(|ty| TyKind::Array(ty, expr_len.clone()))?
+ }
+
+ 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<_>>>()?;
+ TyKind::Tup(tys)
+ }
+
+ // If binary operator is `Add` and both `lhs` and `rhs` are trait bounds,
+ // then type of result is trait object.
+ // Otherwise we don't assume the result type.
+ ExprKind::Binary(binop, lhs, rhs) if binop.node == BinOpKind::Add => {
+ if let (Some(lhs), Some(rhs)) = (lhs.to_bound(), rhs.to_bound()) {
+ TyKind::TraitObject(vec![lhs, rhs], TraitObjectSyntax::None)
+ } else {
+ return None;
+ }
+ }
+
+ ExprKind::Underscore => TyKind::Infer,
+
+ // This expression doesn't look like a type syntactically.
+ _ => return None,
+ };
+
+ Some(P(Ty { kind, id: self.id, span: self.span, tokens: None }))
+ }
+
+ pub fn precedence(&self) -> ExprPrecedence {
+ match self.kind {
+ ExprKind::Box(_) => ExprPrecedence::Box,
+ ExprKind::Array(_) => ExprPrecedence::Array,
+ ExprKind::ConstBlock(_) => ExprPrecedence::ConstBlock,
+ ExprKind::Call(..) => ExprPrecedence::Call,
+ ExprKind::MethodCall(..) => ExprPrecedence::MethodCall,
+ ExprKind::Tup(_) => ExprPrecedence::Tup,
+ ExprKind::Binary(op, ..) => ExprPrecedence::Binary(op.node),
+ ExprKind::Unary(..) => ExprPrecedence::Unary,
+ ExprKind::Lit(_) => ExprPrecedence::Lit,
+ ExprKind::Type(..) | ExprKind::Cast(..) => ExprPrecedence::Cast,
+ ExprKind::Let(..) => ExprPrecedence::Let,
+ ExprKind::If(..) => ExprPrecedence::If,
+ ExprKind::While(..) => ExprPrecedence::While,
+ ExprKind::ForLoop(..) => ExprPrecedence::ForLoop,
+ ExprKind::Loop(..) => ExprPrecedence::Loop,
+ ExprKind::Match(..) => ExprPrecedence::Match,
+ ExprKind::Closure(..) => ExprPrecedence::Closure,
+ ExprKind::Block(..) => ExprPrecedence::Block,
+ ExprKind::TryBlock(..) => ExprPrecedence::TryBlock,
+ ExprKind::Async(..) => ExprPrecedence::Async,
+ ExprKind::Await(..) => ExprPrecedence::Await,
+ ExprKind::Assign(..) => ExprPrecedence::Assign,
+ ExprKind::AssignOp(..) => ExprPrecedence::AssignOp,
+ ExprKind::Field(..) => ExprPrecedence::Field,
+ ExprKind::Index(..) => ExprPrecedence::Index,
+ ExprKind::Range(..) => ExprPrecedence::Range,
+ ExprKind::Underscore => ExprPrecedence::Path,
+ ExprKind::Path(..) => ExprPrecedence::Path,
+ ExprKind::AddrOf(..) => ExprPrecedence::AddrOf,
+ ExprKind::Break(..) => ExprPrecedence::Break,
+ ExprKind::Continue(..) => ExprPrecedence::Continue,
+ ExprKind::Ret(..) => ExprPrecedence::Ret,
+ ExprKind::InlineAsm(..) => ExprPrecedence::InlineAsm,
+ ExprKind::MacCall(..) => ExprPrecedence::Mac,
+ ExprKind::Struct(..) => ExprPrecedence::Struct,
+ ExprKind::Repeat(..) => ExprPrecedence::Repeat,
+ ExprKind::Paren(..) => ExprPrecedence::Paren,
+ ExprKind::Try(..) => ExprPrecedence::Try,
+ ExprKind::Yield(..) => ExprPrecedence::Yield,
+ ExprKind::Yeet(..) => ExprPrecedence::Yeet,
+ ExprKind::Err => ExprPrecedence::Err,
+ }
+ }
+
+ pub fn take(&mut self) -> Self {
+ mem::replace(
+ self,
+ Expr {
+ id: DUMMY_NODE_ID,
+ kind: ExprKind::Err,
+ span: DUMMY_SP,
+ attrs: ThinVec::new(),
+ tokens: None,
+ },
+ )
+ }
+
+ // To a first-order approximation, is this a pattern
+ pub fn is_approximately_pattern(&self) -> bool {
+ match &self.peel_parens().kind {
+ ExprKind::Box(_)
+ | ExprKind::Array(_)
+ | ExprKind::Call(_, _)
+ | ExprKind::Tup(_)
+ | ExprKind::Lit(_)
+ | ExprKind::Range(_, _, _)
+ | ExprKind::Underscore
+ | ExprKind::Path(_, _)
+ | ExprKind::Struct(_) => true,
+ _ => false,
+ }
+ }
+}
+
+/// Limit types of a range (inclusive or exclusive)
+#[derive(Copy, Clone, PartialEq, Encodable, Decodable, Debug)]
+pub enum RangeLimits {
+ /// Inclusive at the beginning, exclusive at the end
+ HalfOpen,
+ /// Inclusive at the beginning and end
+ Closed,
+}
+
+#[derive(Clone, Encodable, Decodable, Debug)]
+pub enum StructRest {
+ /// `..x`.
+ Base(P<Expr>),
+ /// `..`.
+ Rest(Span),
+ /// No trailing `..` or expression.
+ None,
+}
+
+#[derive(Clone, Encodable, Decodable, Debug)]
+pub struct StructExpr {
+ pub qself: Option<QSelf>,
+ pub path: Path,
+ pub fields: Vec<ExprField>,
+ pub rest: StructRest,
+}
+
+#[derive(Clone, Encodable, Decodable, Debug)]
+pub enum ExprKind {
+ /// A `box x` expression.
+ Box(P<Expr>),
+ /// An array (`[a, b, c, d]`)
+ Array(Vec<P<Expr>>),
+ /// Allow anonymous constants from an inline `const` block
+ ConstBlock(AnonConst),
+ /// A function call
+ ///
+ /// The first field resolves to the function itself,
+ /// 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>>),
+ /// A method call (`x.foo::<'static, Bar, Baz>(a, b, c, d)`)
+ ///
+ /// The `PathSegment` represents the method name and its generic arguments
+ /// (within the angle brackets).
+ /// The first element of the vector of an `Expr` is the expression that evaluates
+ /// to the object on which the method is being called on (the receiver),
+ /// and the remaining elements are the rest of the arguments.
+ /// Thus, `x.foo::<Bar, Baz>(a, b, c, d)` is represented as
+ /// `ExprKind::MethodCall(PathSegment { foo, [Bar, Baz] }, [x, a, b, c, d])`.
+ /// This `Span` is the span of the function, without the dot and receiver
+ /// (e.g. `foo(a, b)` in `x.foo(a, b)`
+ MethodCall(PathSegment, Vec<P<Expr>>, Span),
+ /// A tuple (e.g., `(a, b, c, d)`).
+ Tup(Vec<P<Expr>>),
+ /// A binary operation (e.g., `a + b`, `a * b`).
+ Binary(BinOp, P<Expr>, P<Expr>),
+ /// A unary operation (e.g., `!x`, `*x`).
+ Unary(UnOp, P<Expr>),
+ /// A literal (e.g., `1`, `"foo"`).
+ Lit(Lit),
+ /// A cast (e.g., `foo as f64`).
+ Cast(P<Expr>, P<Ty>),
+ /// A type ascription (e.g., `42: usize`).
+ Type(P<Expr>, P<Ty>),
+ /// A `let pat = expr` expression that is only semantically allowed in the condition
+ /// of `if` / `while` expressions. (e.g., `if let 0 = x { .. }`).
+ ///
+ /// `Span` represents the whole `let pat = expr` statement.
+ Let(P<Pat>, P<Expr>, Span),
+ /// An `if` block, with an optional `else` block.
+ ///
+ /// `if expr { block } else { expr }`
+ If(P<Expr>, P<Block>, Option<P<Expr>>),
+ /// A while loop, with an optional label.
+ ///
+ /// `'label: while expr { block }`
+ While(P<Expr>, P<Block>, Option<Label>),
+ /// A `for` loop, with an optional label.
+ ///
+ /// `'label: for pat in expr { block }`
+ ///
+ /// This is desugared to a combination of `loop` and `match` expressions.
+ ForLoop(P<Pat>, P<Expr>, P<Block>, Option<Label>),
+ /// Conditionless loop (can be exited with `break`, `continue`, or `return`).
+ ///
+ /// `'label: loop { block }`
+ Loop(P<Block>, Option<Label>),
+ /// A `match` block.
+ Match(P<Expr>, Vec<Arm>),
+ /// A closure (e.g., `move |a, b, c| a + b + c`).
+ ///
+ /// The final span is the span of the argument block `|...|`.
+ Closure(ClosureBinder, CaptureBy, Async, Movability, P<FnDecl>, P<Expr>, Span),
+ /// A block (`'label: { ... }`).
+ Block(P<Block>, Option<Label>),
+ /// An async block (`async move { ... }`).
+ ///
+ /// The `NodeId` is the `NodeId` for the closure that results from
+ /// desugaring an async block, just like the NodeId field in the
+ /// `Async::Yes` variant. This is necessary in order to create a def for the
+ /// closure which can be used as a parent of any child defs. Defs
+ /// created during lowering cannot be made the parent of any other
+ /// preexisting defs.
+ Async(CaptureBy, NodeId, P<Block>),
+ /// An await expression (`my_future.await`).
+ Await(P<Expr>),
+
+ /// A try block (`try { ... }`).
+ TryBlock(P<Block>),
+
+ /// An assignment (`a = foo()`).
+ /// The `Span` argument is the span of the `=` token.
+ Assign(P<Expr>, P<Expr>, Span),
+ /// An assignment with an operator.
+ ///
+ /// E.g., `a += 1`.
+ AssignOp(BinOp, P<Expr>, P<Expr>),
+ /// Access of a named (e.g., `obj.foo`) or unnamed (e.g., `obj.0`) struct field.
+ Field(P<Expr>, Ident),
+ /// An indexing operation (e.g., `foo[2]`).
+ Index(P<Expr>, P<Expr>),
+ /// A range (e.g., `1..2`, `1..`, `..2`, `1..=2`, `..=2`; and `..` in destructuring assignment).
+ Range(Option<P<Expr>>, Option<P<Expr>>, RangeLimits),
+ /// An underscore, used in destructuring assignment to ignore a value.
+ Underscore,
+
+ /// Variable reference, possibly containing `::` and/or type
+ /// parameters (e.g., `foo::bar::<baz>`).
+ ///
+ /// Optionally "qualified" (e.g., `<Vec<T> as SomeTrait>::SomeType`).
+ Path(Option<QSelf>, Path),
+
+ /// A referencing operation (`&a`, `&mut a`, `&raw const a` or `&raw mut a`).
+ AddrOf(BorrowKind, Mutability, P<Expr>),
+ /// A `break`, with an optional label to break, and an optional expression.
+ Break(Option<Label>, Option<P<Expr>>),
+ /// A `continue`, with an optional label.
+ Continue(Option<Label>),
+ /// A `return`, with an optional value to be returned.
+ Ret(Option<P<Expr>>),
+
+ /// Output of the `asm!()` macro.
+ InlineAsm(P<InlineAsm>),
+
+ /// A macro invocation; pre-expansion.
+ MacCall(MacCall),
+
+ /// A struct literal expression.
+ ///
+ /// E.g., `Foo {x: 1, y: 2}`, or `Foo {x: 1, .. rest}`.
+ Struct(P<StructExpr>),
+
+ /// An array literal constructed from one repeated element.
+ ///
+ /// E.g., `[1; 5]`. The expression is the element to be
+ /// repeated; the constant is the number of times to repeat it.
+ Repeat(P<Expr>, AnonConst),
+
+ /// No-op: used solely so we can pretty-print faithfully.
+ Paren(P<Expr>),
+
+ /// A try expression (`expr?`).
+ Try(P<Expr>),
+
+ /// A `yield`, with an optional value to be yielded.
+ Yield(Option<P<Expr>>),
+
+ /// A `do yeet` (aka `throw`/`fail`/`bail`/`raise`/whatever),
+ /// with an optional value to be returned.
+ Yeet(Option<P<Expr>>),
+
+ /// Placeholder for an expression that wasn't syntactically well formed in some way.
+ Err,
+}
+
+/// The explicit `Self` type in a "qualified path". The actual
+/// path, including the trait and the associated item, is stored
+/// separately. `position` represents the index of the associated
+/// item qualified with this `Self` type.
+///
+/// ```ignore (only-for-syntax-highlight)
+/// <Vec<T> as a::b::Trait>::AssociatedItem
+/// ^~~~~ ~~~~~~~~~~~~~~^
+/// ty position = 3
+///
+/// <Vec<T>>::AssociatedItem
+/// ^~~~~ ^
+/// ty position = 0
+/// ```
+#[derive(Clone, Encodable, Decodable, Debug)]
+pub struct QSelf {
+ pub ty: P<Ty>,
+
+ /// The span of `a::b::Trait` in a path like `<Vec<T> as
+ /// a::b::Trait>::AssociatedItem`; in the case where `position ==
+ /// 0`, this is an empty span.
+ pub path_span: Span,
+ pub position: usize,
+}
+
+/// A capture clause used in closures and `async` blocks.
+#[derive(Clone, Copy, PartialEq, Encodable, Decodable, Debug, HashStable_Generic)]
+pub enum CaptureBy {
+ /// `move |x| y + x`.
+ Value,
+ /// `move` keyword was not specified.
+ Ref,
+}
+
+/// The movability of a generator / closure literal:
+/// whether a generator contains self-references, causing it to be `!Unpin`.
+#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Encodable, Decodable, Debug, Copy)]
+#[derive(HashStable_Generic)]
+pub enum Movability {
+ /// May contain self-references, `!Unpin`.
+ Static,
+ /// Must not contain self-references, `Unpin`.
+ Movable,
+}
+
+/// Closure lifetime binder, `for<'a, 'b>` in `for<'a, 'b> |_: &'a (), _: &'b ()|`.
+#[derive(Clone, Encodable, Decodable, Debug)]
+pub enum ClosureBinder {
+ /// The binder is not present, all closure lifetimes are inferred.
+ NotPresent,
+ /// The binder is present.
+ For {
+ /// Span of the whole `for<>` clause
+ ///
+ /// ```text
+ /// for<'a, 'b> |_: &'a (), _: &'b ()| { ... }
+ /// ^^^^^^^^^^^ -- this
+ /// ```
+ span: Span,
+
+ /// Lifetimes in the `for<>` closure
+ ///
+ /// ```text
+ /// for<'a, 'b> |_: &'a (), _: &'b ()| { ... }
+ /// ^^^^^^ -- this
+ /// ```
+ generic_params: P<[GenericParam]>,
+ },
+}
+
+/// Represents a macro invocation. The `path` indicates which macro
+/// is being invoked, and the `args` are arguments passed to it.
+#[derive(Clone, Encodable, Decodable, Debug)]
+pub struct MacCall {
+ pub path: Path,
+ pub args: P<MacArgs>,
+ pub prior_type_ascription: Option<(Span, bool)>,
+}
+
+impl MacCall {
+ pub fn span(&self) -> Span {
+ self.path.span.to(self.args.span().unwrap_or(self.path.span))
+ }
+}
+
+/// Arguments passed to an attribute or a function-like macro.
+#[derive(Clone, Encodable, Decodable, Debug)]
+pub enum MacArgs {
+ /// No arguments - `#[attr]`.
+ Empty,
+ /// Delimited arguments - `#[attr()/[]/{}]` or `mac!()/[]/{}`.
+ Delimited(DelimSpan, MacDelimiter, TokenStream),
+ /// Arguments of a key-value attribute - `#[attr = "value"]`.
+ Eq(
+ /// Span of the `=` token.
+ Span,
+ /// The "value".
+ MacArgsEq,
+ ),
+}
+
+// The RHS of a `MacArgs::Eq` starts out as an expression. Once macro expansion
+// is completed, all cases end up either as a literal, which is the form used
+// after lowering to HIR, or as an error.
+#[derive(Clone, Encodable, Decodable, Debug)]
+pub enum MacArgsEq {
+ Ast(P<Expr>),
+ Hir(Lit),
+}
+
+impl MacArgs {
+ pub fn delim(&self) -> Option<Delimiter> {
+ match self {
+ MacArgs::Delimited(_, delim, _) => Some(delim.to_token()),
+ MacArgs::Empty | MacArgs::Eq(..) => None,
+ }
+ }
+
+ pub fn span(&self) -> Option<Span> {
+ match self {
+ MacArgs::Empty => None,
+ MacArgs::Delimited(dspan, ..) => Some(dspan.entire()),
+ MacArgs::Eq(eq_span, MacArgsEq::Ast(expr)) => Some(eq_span.to(expr.span)),
+ MacArgs::Eq(_, MacArgsEq::Hir(lit)) => {
+ unreachable!("in literal form when getting span: {:?}", lit);
+ }
+ }
+ }
+
+ /// Tokens inside the delimiters or after `=`.
+ /// Proc macros see these tokens, for example.
+ pub fn inner_tokens(&self) -> TokenStream {
+ match self {
+ MacArgs::Empty => TokenStream::default(),
+ MacArgs::Delimited(.., tokens) => tokens.clone(),
+ MacArgs::Eq(_, MacArgsEq::Ast(expr)) => TokenStream::from_ast(expr),
+ MacArgs::Eq(_, MacArgsEq::Hir(lit)) => {
+ unreachable!("in literal form when getting inner tokens: {:?}", lit)
+ }
+ }
+ }
+
+ /// Whether a macro with these arguments needs a semicolon
+ /// when used as a standalone item or statement.
+ pub fn need_semicolon(&self) -> bool {
+ !matches!(self, MacArgs::Delimited(_, MacDelimiter::Brace, _))
+ }
+}
+
+impl<CTX> HashStable<CTX> for MacArgs
+where
+ CTX: crate::HashStableContext,
+{
+ fn hash_stable(&self, ctx: &mut CTX, hasher: &mut StableHasher) {
+ mem::discriminant(self).hash_stable(ctx, hasher);
+ match self {
+ MacArgs::Empty => {}
+ MacArgs::Delimited(dspan, delim, tokens) => {
+ dspan.hash_stable(ctx, hasher);
+ delim.hash_stable(ctx, hasher);
+ tokens.hash_stable(ctx, hasher);
+ }
+ MacArgs::Eq(_eq_span, MacArgsEq::Ast(expr)) => {
+ unreachable!("hash_stable {:?}", expr);
+ }
+ MacArgs::Eq(eq_span, MacArgsEq::Hir(lit)) => {
+ eq_span.hash_stable(ctx, hasher);
+ lit.hash_stable(ctx, hasher);
+ }
+ }
+ }
+}
+
+#[derive(Copy, Clone, PartialEq, Eq, Encodable, Decodable, Debug, HashStable_Generic)]
+pub enum MacDelimiter {
+ Parenthesis,
+ Bracket,
+ Brace,
+}
+
+impl MacDelimiter {
+ pub fn to_token(self) -> Delimiter {
+ match self {
+ MacDelimiter::Parenthesis => Delimiter::Parenthesis,
+ MacDelimiter::Bracket => Delimiter::Bracket,
+ MacDelimiter::Brace => Delimiter::Brace,
+ }
+ }
+
+ pub fn from_token(delim: Delimiter) -> Option<MacDelimiter> {
+ match delim {
+ Delimiter::Parenthesis => Some(MacDelimiter::Parenthesis),
+ Delimiter::Bracket => Some(MacDelimiter::Bracket),
+ Delimiter::Brace => Some(MacDelimiter::Brace),
+ Delimiter::Invisible => None,
+ }
+ }
+}
+
+/// Represents a macro definition.
+#[derive(Clone, Encodable, Decodable, Debug, HashStable_Generic)]
+pub struct MacroDef {
+ pub body: P<MacArgs>,
+ /// `true` if macro was defined with `macro_rules`.
+ pub macro_rules: bool,
+}
+
+#[derive(Clone, Encodable, Decodable, Debug, Copy, Hash, Eq, PartialEq)]
+#[derive(HashStable_Generic)]
+pub enum StrStyle {
+ /// A regular string, like `"foo"`.
+ Cooked,
+ /// A raw string, like `r##"foo"##`.
+ ///
+ /// The value is the number of `#` symbols used.
+ Raw(u8),
+}
+
+/// An AST literal.
+#[derive(Clone, Encodable, Decodable, Debug, HashStable_Generic)]
+pub struct Lit {
+ /// The original literal token as written in source code.
+ pub token: token::Lit,
+ /// The "semantic" representation of the literal lowered from the original tokens.
+ /// Strings are unescaped, hexadecimal forms are eliminated, etc.
+ /// FIXME: Remove this and only create the semantic representation during lowering to HIR.
+ pub kind: LitKind,
+ pub span: Span,
+}
+
+/// Same as `Lit`, but restricted to string literals.
+#[derive(Clone, Copy, Encodable, Decodable, Debug)]
+pub struct StrLit {
+ /// The original literal token as written in source code.
+ pub style: StrStyle,
+ pub symbol: Symbol,
+ pub suffix: Option<Symbol>,
+ pub span: Span,
+ /// The unescaped "semantic" representation of the literal lowered from the original token.
+ /// FIXME: Remove this and only create the semantic representation during lowering to HIR.
+ pub symbol_unescaped: Symbol,
+}
+
+impl StrLit {
+ pub fn as_lit(&self) -> Lit {
+ let token_kind = match self.style {
+ StrStyle::Cooked => token::Str,
+ StrStyle::Raw(n) => token::StrRaw(n),
+ };
+ Lit {
+ token: token::Lit::new(token_kind, self.symbol, self.suffix),
+ span: self.span,
+ kind: LitKind::Str(self.symbol_unescaped, self.style),
+ }
+ }
+}
+
+/// Type of the integer literal based on provided suffix.
+#[derive(Clone, Copy, Encodable, Decodable, Debug, Hash, Eq, PartialEq)]
+#[derive(HashStable_Generic)]
+pub enum LitIntType {
+ /// e.g. `42_i32`.
+ Signed(IntTy),
+ /// e.g. `42_u32`.
+ Unsigned(UintTy),
+ /// e.g. `42`.
+ Unsuffixed,
+}
+
+/// Type of the float literal based on provided suffix.
+#[derive(Clone, Copy, Encodable, Decodable, Debug, Hash, Eq, PartialEq)]
+#[derive(HashStable_Generic)]
+pub enum LitFloatType {
+ /// A float literal with a suffix (`1f32` or `1E10f32`).
+ Suffixed(FloatTy),
+ /// A float literal without a suffix (`1.0 or 1.0E10`).
+ Unsuffixed,
+}
+
+/// Literal kind.
+///
+/// E.g., `"foo"`, `42`, `12.34`, or `bool`.
+#[derive(Clone, Encodable, Decodable, Debug, Hash, Eq, PartialEq, HashStable_Generic)]
+pub enum LitKind {
+ /// A string literal (`"foo"`).
+ Str(Symbol, StrStyle),
+ /// A byte string (`b"foo"`).
+ ByteStr(Lrc<[u8]>),
+ /// A byte char (`b'f'`).
+ Byte(u8),
+ /// A character literal (`'a'`).
+ Char(char),
+ /// An integer literal (`1`).
+ Int(u128, LitIntType),
+ /// A float literal (`1f64` or `1E10f64`).
+ Float(Symbol, LitFloatType),
+ /// A boolean literal.
+ Bool(bool),
+ /// Placeholder for a literal that wasn't well-formed in some way.
+ Err(Symbol),
+}
+
+impl LitKind {
+ /// Returns `true` if this literal is a string.
+ pub fn is_str(&self) -> bool {
+ matches!(self, LitKind::Str(..))
+ }
+
+ /// Returns `true` if this literal is byte literal string.
+ pub fn is_bytestr(&self) -> bool {
+ matches!(self, LitKind::ByteStr(_))
+ }
+
+ /// Returns `true` if this is a numeric literal.
+ pub fn is_numeric(&self) -> bool {
+ matches!(self, LitKind::Int(..) | LitKind::Float(..))
+ }
+
+ /// Returns `true` if this literal has no suffix.
+ /// Note: this will return true for literals with prefixes such as raw strings and byte strings.
+ pub fn is_unsuffixed(&self) -> bool {
+ !self.is_suffixed()
+ }
+
+ /// Returns `true` if this literal has a suffix.
+ pub fn is_suffixed(&self) -> bool {
+ match *self {
+ // suffixed variants
+ LitKind::Int(_, LitIntType::Signed(..) | LitIntType::Unsigned(..))
+ | LitKind::Float(_, LitFloatType::Suffixed(..)) => true,
+ // unsuffixed variants
+ LitKind::Str(..)
+ | LitKind::ByteStr(..)
+ | LitKind::Byte(..)
+ | LitKind::Char(..)
+ | LitKind::Int(_, LitIntType::Unsuffixed)
+ | LitKind::Float(_, LitFloatType::Unsuffixed)
+ | LitKind::Bool(..)
+ | LitKind::Err(..) => false,
+ }
+ }
+}
+
+// N.B., If you change this, you'll probably want to change the corresponding
+// type structure in `middle/ty.rs` as well.
+#[derive(Clone, Encodable, Decodable, Debug)]
+pub struct MutTy {
+ pub ty: P<Ty>,
+ pub mutbl: Mutability,
+}
+
+/// Represents a function's signature in a trait declaration,
+/// trait implementation, or free function.
+#[derive(Clone, Encodable, Decodable, Debug)]
+pub struct FnSig {
+ pub header: FnHeader,
+ pub decl: P<FnDecl>,
+ pub span: Span,
+}
+
+#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
+#[derive(Encodable, Decodable, HashStable_Generic)]
+pub enum FloatTy {
+ F32,
+ F64,
+}
+
+impl FloatTy {
+ pub fn name_str(self) -> &'static str {
+ match self {
+ FloatTy::F32 => "f32",
+ FloatTy::F64 => "f64",
+ }
+ }
+
+ pub fn name(self) -> Symbol {
+ match self {
+ FloatTy::F32 => sym::f32,
+ FloatTy::F64 => sym::f64,
+ }
+ }
+}
+
+#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
+#[derive(Encodable, Decodable, HashStable_Generic)]
+pub enum IntTy {
+ Isize,
+ I8,
+ I16,
+ I32,
+ I64,
+ I128,
+}
+
+impl IntTy {
+ pub fn name_str(&self) -> &'static str {
+ match *self {
+ IntTy::Isize => "isize",
+ IntTy::I8 => "i8",
+ IntTy::I16 => "i16",
+ IntTy::I32 => "i32",
+ IntTy::I64 => "i64",
+ IntTy::I128 => "i128",
+ }
+ }
+
+ pub fn name(&self) -> Symbol {
+ match *self {
+ IntTy::Isize => sym::isize,
+ IntTy::I8 => sym::i8,
+ IntTy::I16 => sym::i16,
+ IntTy::I32 => sym::i32,
+ IntTy::I64 => sym::i64,
+ IntTy::I128 => sym::i128,
+ }
+ }
+}
+
+#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Copy, Debug)]
+#[derive(Encodable, Decodable, HashStable_Generic)]
+pub enum UintTy {
+ Usize,
+ U8,
+ U16,
+ U32,
+ U64,
+ U128,
+}
+
+impl UintTy {
+ pub fn name_str(&self) -> &'static str {
+ match *self {
+ UintTy::Usize => "usize",
+ UintTy::U8 => "u8",
+ UintTy::U16 => "u16",
+ UintTy::U32 => "u32",
+ UintTy::U64 => "u64",
+ UintTy::U128 => "u128",
+ }
+ }
+
+ pub fn name(&self) -> Symbol {
+ match *self {
+ UintTy::Usize => sym::usize,
+ UintTy::U8 => sym::u8,
+ UintTy::U16 => sym::u16,
+ UintTy::U32 => sym::u32,
+ UintTy::U64 => sym::u64,
+ UintTy::U128 => sym::u128,
+ }
+ }
+}
+
+/// A constraint on an associated type (e.g., `A = Bar` in `Foo<A = Bar>` or
+/// `A: TraitA + TraitB` in `Foo<A: TraitA + TraitB>`).
+#[derive(Clone, Encodable, Decodable, Debug)]
+pub struct AssocConstraint {
+ pub id: NodeId,
+ pub ident: Ident,
+ pub gen_args: Option<GenericArgs>,
+ pub kind: AssocConstraintKind,
+ pub span: Span,
+}
+
+/// The kinds of an `AssocConstraint`.
+#[derive(Clone, Encodable, Decodable, Debug)]
+pub enum Term {
+ Ty(P<Ty>),
+ Const(AnonConst),
+}
+
+impl From<P<Ty>> for Term {
+ fn from(v: P<Ty>) -> Self {
+ Term::Ty(v)
+ }
+}
+
+impl From<AnonConst> for Term {
+ fn from(v: AnonConst) -> Self {
+ Term::Const(v)
+ }
+}
+
+/// The kinds of an `AssocConstraint`.
+#[derive(Clone, Encodable, Decodable, Debug)]
+pub enum AssocConstraintKind {
+ /// E.g., `A = Bar`, `A = 3` in `Foo<A = Bar>` where A is an associated type.
+ Equality { term: Term },
+ /// E.g. `A: TraitA + TraitB` in `Foo<A: TraitA + TraitB>`.
+ Bound { bounds: GenericBounds },
+}
+
+#[derive(Encodable, Decodable, Debug)]
+pub struct Ty {
+ pub id: NodeId,
+ pub kind: TyKind,
+ pub span: Span,
+ pub tokens: Option<LazyTokenStream>,
+}
+
+impl Clone for Ty {
+ fn clone(&self) -> Self {
+ ensure_sufficient_stack(|| Self {
+ id: self.id,
+ kind: self.kind.clone(),
+ span: self.span,
+ tokens: self.tokens.clone(),
+ })
+ }
+}
+
+impl Ty {
+ pub fn peel_refs(&self) -> &Self {
+ let mut final_ty = self;
+ while let TyKind::Rptr(_, MutTy { ty, .. }) = &final_ty.kind {
+ final_ty = &ty;
+ }
+ final_ty
+ }
+}
+
+#[derive(Clone, Encodable, Decodable, Debug)]
+pub struct BareFnTy {
+ pub unsafety: Unsafe,
+ pub ext: Extern,
+ pub generic_params: Vec<GenericParam>,
+ pub decl: P<FnDecl>,
+ /// Span of the `fn(...) -> ...` part.
+ pub decl_span: Span,
+}
+
+/// The various kinds of type recognized by the compiler.
+#[derive(Clone, Encodable, Decodable, Debug)]
+pub enum TyKind {
+ /// A variable-length slice (`[T]`).
+ Slice(P<Ty>),
+ /// A fixed length array (`[T; n]`).
+ Array(P<Ty>, AnonConst),
+ /// A raw pointer (`*const T` or `*mut T`).
+ Ptr(MutTy),
+ /// A reference (`&'a T` or `&'a mut T`).
+ Rptr(Option<Lifetime>, MutTy),
+ /// A bare function (e.g., `fn(usize) -> bool`).
+ BareFn(P<BareFnTy>),
+ /// The never type (`!`).
+ Never,
+ /// A tuple (`(A, B, C, D,...)`).
+ Tup(Vec<P<Ty>>),
+ /// A path (`module::module::...::Type`), optionally
+ /// "qualified", e.g., `<Vec<T> as SomeTrait>::SomeType`.
+ ///
+ /// Type parameters are stored in the `Path` itself.
+ Path(Option<QSelf>, Path),
+ /// A trait object type `Bound1 + Bound2 + Bound3`
+ /// where `Bound` is a trait or a lifetime.
+ TraitObject(GenericBounds, TraitObjectSyntax),
+ /// An `impl Bound1 + Bound2 + Bound3` type
+ /// where `Bound` is a trait or a lifetime.
+ ///
+ /// The `NodeId` exists to prevent lowering from having to
+ /// generate `NodeId`s on the fly, which would complicate
+ /// the generation of opaque `type Foo = impl Trait` items significantly.
+ ImplTrait(NodeId, GenericBounds),
+ /// No-op; kept solely so that we can pretty-print faithfully.
+ Paren(P<Ty>),
+ /// Unused for now.
+ Typeof(AnonConst),
+ /// This means the type should be inferred instead of it having been
+ /// specified. This can appear anywhere in a type.
+ Infer,
+ /// Inferred type of a `self` or `&self` argument in a method.
+ ImplicitSelf,
+ /// A macro in the type position.
+ MacCall(MacCall),
+ /// Placeholder for a kind that has failed to be defined.
+ Err,
+ /// Placeholder for a `va_list`.
+ CVarArgs,
+}
+
+impl TyKind {
+ pub fn is_implicit_self(&self) -> bool {
+ matches!(self, TyKind::ImplicitSelf)
+ }
+
+ pub fn is_unit(&self) -> bool {
+ matches!(self, TyKind::Tup(tys) if tys.is_empty())
+ }
+
+ pub fn is_simple_path(&self) -> Option<Symbol> {
+ if let TyKind::Path(None, Path { segments, .. }) = &self && segments.len() == 1 {
+ Some(segments[0].ident.name)
+ } else {
+ None
+ }
+ }
+}
+
+/// Syntax used to declare a trait object.
+#[derive(Clone, Copy, PartialEq, Encodable, Decodable, Debug, HashStable_Generic)]
+pub enum TraitObjectSyntax {
+ Dyn,
+ None,
+}
+
+/// Inline assembly operand explicit register or register class.
+///
+/// E.g., `"eax"` as in `asm!("mov eax, 2", out("eax") result)`.
+#[derive(Clone, Copy, Encodable, Decodable, Debug)]
+pub enum InlineAsmRegOrRegClass {
+ Reg(Symbol),
+ RegClass(Symbol),
+}
+
+bitflags::bitflags! {
+ #[derive(Encodable, Decodable, HashStable_Generic)]
+ pub struct InlineAsmOptions: u16 {
+ const PURE = 1 << 0;
+ const NOMEM = 1 << 1;
+ const READONLY = 1 << 2;
+ const PRESERVES_FLAGS = 1 << 3;
+ const NORETURN = 1 << 4;
+ const NOSTACK = 1 << 5;
+ const ATT_SYNTAX = 1 << 6;
+ const RAW = 1 << 7;
+ const MAY_UNWIND = 1 << 8;
+ }
+}
+
+#[derive(Clone, PartialEq, Encodable, Decodable, Debug, Hash, HashStable_Generic)]
+pub enum InlineAsmTemplatePiece {
+ String(String),
+ Placeholder { operand_idx: usize, modifier: Option<char>, span: Span },
+}
+
+impl fmt::Display for InlineAsmTemplatePiece {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ match self {
+ Self::String(s) => {
+ for c in s.chars() {
+ match c {
+ '{' => f.write_str("{{")?,
+ '}' => f.write_str("}}")?,
+ _ => c.fmt(f)?,
+ }
+ }
+ Ok(())
+ }
+ Self::Placeholder { operand_idx, modifier: Some(modifier), .. } => {
+ write!(f, "{{{}:{}}}", operand_idx, modifier)
+ }
+ Self::Placeholder { operand_idx, modifier: None, .. } => {
+ write!(f, "{{{}}}", operand_idx)
+ }
+ }
+ }
+}
+
+impl InlineAsmTemplatePiece {
+ /// Rebuilds the asm template string from its pieces.
+ pub fn to_string(s: &[Self]) -> String {
+ use fmt::Write;
+ let mut out = String::new();
+ for p in s.iter() {
+ let _ = write!(out, "{}", p);
+ }
+ out
+ }
+}
+
+/// Inline assembly symbol operands get their own AST node that is somewhat
+/// similar to `AnonConst`.
+///
+/// The main difference is that we specifically don't assign it `DefId` in
+/// `DefCollector`. Instead this is deferred until AST lowering where we
+/// lower it to an `AnonConst` (for functions) or a `Path` (for statics)
+/// depending on what the path resolves to.
+#[derive(Clone, Encodable, Decodable, Debug)]
+pub struct InlineAsmSym {
+ pub id: NodeId,
+ pub qself: Option<QSelf>,
+ pub path: Path,
+}
+
+/// Inline assembly operand.
+///
+/// E.g., `out("eax") result` as in `asm!("mov eax, 2", out("eax") result)`.
+#[derive(Clone, Encodable, Decodable, Debug)]
+pub enum InlineAsmOperand {
+ In {
+ reg: InlineAsmRegOrRegClass,
+ expr: P<Expr>,
+ },
+ Out {
+ reg: InlineAsmRegOrRegClass,
+ late: bool,
+ expr: Option<P<Expr>>,
+ },
+ InOut {
+ reg: InlineAsmRegOrRegClass,
+ late: bool,
+ expr: P<Expr>,
+ },
+ SplitInOut {
+ reg: InlineAsmRegOrRegClass,
+ late: bool,
+ in_expr: P<Expr>,
+ out_expr: Option<P<Expr>>,
+ },
+ Const {
+ anon_const: AnonConst,
+ },
+ Sym {
+ sym: InlineAsmSym,
+ },
+}
+
+/// Inline assembly.
+///
+/// E.g., `asm!("NOP");`.
+#[derive(Clone, Encodable, Decodable, Debug)]
+pub struct InlineAsm {
+ pub template: Vec<InlineAsmTemplatePiece>,
+ pub template_strs: Box<[(Symbol, Option<Symbol>, Span)]>,
+ pub operands: Vec<(InlineAsmOperand, Span)>,
+ pub clobber_abis: Vec<(Symbol, Span)>,
+ pub options: InlineAsmOptions,
+ pub line_spans: Vec<Span>,
+}
+
+/// A parameter in a function header.
+///
+/// E.g., `bar: usize` as in `fn foo(bar: usize)`.
+#[derive(Clone, Encodable, Decodable, Debug)]
+pub struct Param {
+ pub attrs: AttrVec,
+ pub ty: P<Ty>,
+ pub pat: P<Pat>,
+ pub id: NodeId,
+ pub span: Span,
+ pub is_placeholder: bool,
+}
+
+/// Alternative representation for `Arg`s describing `self` parameter of methods.
+///
+/// E.g., `&mut self` as in `fn foo(&mut self)`.
+#[derive(Clone, Encodable, Decodable, Debug)]
+pub enum SelfKind {
+ /// `self`, `mut self`
+ Value(Mutability),
+ /// `&'lt self`, `&'lt mut self`
+ Region(Option<Lifetime>, Mutability),
+ /// `self: TYPE`, `mut self: TYPE`
+ Explicit(P<Ty>, Mutability),
+}
+
+pub type ExplicitSelf = Spanned<SelfKind>;
+
+impl Param {
+ /// Attempts to cast parameter to `ExplicitSelf`.
+ pub fn to_self(&self) -> Option<ExplicitSelf> {
+ if let PatKind::Ident(BindingMode::ByValue(mutbl), ident, _) = self.pat.kind {
+ if ident.name == kw::SelfLower {
+ return match self.ty.kind {
+ TyKind::ImplicitSelf => Some(respan(self.pat.span, SelfKind::Value(mutbl))),
+ TyKind::Rptr(lt, MutTy { ref ty, mutbl }) if ty.kind.is_implicit_self() => {
+ Some(respan(self.pat.span, SelfKind::Region(lt, mutbl)))
+ }
+ _ => Some(respan(
+ self.pat.span.to(self.ty.span),
+ SelfKind::Explicit(self.ty.clone(), mutbl),
+ )),
+ };
+ }
+ }
+ None
+ }
+
+ /// Returns `true` if parameter is `self`.
+ pub fn is_self(&self) -> bool {
+ if let PatKind::Ident(_, ident, _) = self.pat.kind {
+ ident.name == kw::SelfLower
+ } else {
+ false
+ }
+ }
+
+ /// Builds a `Param` object from `ExplicitSelf`.
+ pub fn from_self(attrs: AttrVec, eself: ExplicitSelf, eself_ident: Ident) -> Param {
+ let span = eself.span.to(eself_ident.span);
+ let infer_ty = P(Ty { id: DUMMY_NODE_ID, kind: TyKind::ImplicitSelf, span, tokens: None });
+ let param = |mutbl, ty| Param {
+ attrs,
+ pat: P(Pat {
+ id: DUMMY_NODE_ID,
+ kind: PatKind::Ident(BindingMode::ByValue(mutbl), eself_ident, None),
+ span,
+ tokens: None,
+ }),
+ span,
+ ty,
+ id: DUMMY_NODE_ID,
+ is_placeholder: false,
+ };
+ match eself.node {
+ SelfKind::Explicit(ty, mutbl) => param(mutbl, ty),
+ SelfKind::Value(mutbl) => param(mutbl, infer_ty),
+ SelfKind::Region(lt, mutbl) => param(
+ Mutability::Not,
+ P(Ty {
+ id: DUMMY_NODE_ID,
+ kind: TyKind::Rptr(lt, MutTy { ty: infer_ty, mutbl }),
+ span,
+ tokens: None,
+ }),
+ ),
+ }
+ }
+}
+
+/// A signature (not the body) of a function declaration.
+///
+/// E.g., `fn foo(bar: baz)`.
+///
+/// Please note that it's different from `FnHeader` structure
+/// which contains metadata about function safety, asyncness, constness and ABI.
+#[derive(Clone, Encodable, Decodable, Debug)]
+pub struct FnDecl {
+ pub inputs: Vec<Param>,
+ pub output: FnRetTy,
+}
+
+impl FnDecl {
+ pub fn has_self(&self) -> bool {
+ self.inputs.get(0).map_or(false, Param::is_self)
+ }
+ pub fn c_variadic(&self) -> bool {
+ self.inputs.last().map_or(false, |arg| matches!(arg.ty.kind, TyKind::CVarArgs))
+ }
+}
+
+/// Is the trait definition an auto trait?
+#[derive(Copy, Clone, PartialEq, Encodable, Decodable, Debug, HashStable_Generic)]
+pub enum IsAuto {
+ Yes,
+ No,
+}
+
+#[derive(Copy, Clone, PartialEq, Eq, Hash, Encodable, Decodable, Debug)]
+#[derive(HashStable_Generic)]
+pub enum Unsafe {
+ Yes(Span),
+ No,
+}
+
+#[derive(Copy, Clone, Encodable, Decodable, Debug)]
+pub enum Async {
+ Yes { span: Span, closure_id: NodeId, return_impl_trait_id: NodeId },
+ No,
+}
+
+impl Async {
+ pub fn is_async(self) -> bool {
+ matches!(self, Async::Yes { .. })
+ }
+
+ /// In this case this is an `async` return, the `NodeId` for the generated `impl Trait` item.
+ pub fn opt_return_id(self) -> Option<NodeId> {
+ match self {
+ Async::Yes { return_impl_trait_id, .. } => Some(return_impl_trait_id),
+ Async::No => None,
+ }
+ }
+}
+
+#[derive(Copy, Clone, PartialEq, Eq, Hash, Encodable, Decodable, Debug)]
+#[derive(HashStable_Generic)]
+pub enum Const {
+ Yes(Span),
+ No,
+}
+
+/// Item defaultness.
+/// For details see the [RFC #2532](https://github.com/rust-lang/rfcs/pull/2532).
+#[derive(Copy, Clone, PartialEq, Encodable, Decodable, Debug, HashStable_Generic)]
+pub enum Defaultness {
+ Default(Span),
+ Final,
+}
+
+#[derive(Copy, Clone, PartialEq, Encodable, Decodable, HashStable_Generic)]
+pub enum ImplPolarity {
+ /// `impl Trait for Type`
+ Positive,
+ /// `impl !Trait for Type`
+ Negative(Span),
+}
+
+impl fmt::Debug for ImplPolarity {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ match *self {
+ ImplPolarity::Positive => "positive".fmt(f),
+ ImplPolarity::Negative(_) => "negative".fmt(f),
+ }
+ }
+}
+
+#[derive(Clone, Encodable, Decodable, Debug)]
+pub enum FnRetTy {
+ /// Returns type is not specified.
+ ///
+ /// Functions default to `()` and closures default to inference.
+ /// Span points to where return type would be inserted.
+ Default(Span),
+ /// Everything else.
+ Ty(P<Ty>),
+}
+
+impl FnRetTy {
+ pub fn span(&self) -> Span {
+ match *self {
+ FnRetTy::Default(span) => span,
+ FnRetTy::Ty(ref ty) => ty.span,
+ }
+ }
+}
+
+#[derive(Clone, Copy, PartialEq, Encodable, Decodable, Debug)]
+pub enum Inline {
+ Yes,
+ No,
+}
+
+/// Module item kind.
+#[derive(Clone, Encodable, Decodable, Debug)]
+pub enum ModKind {
+ /// Module with inlined definition `mod foo { ... }`,
+ /// 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),
+ /// Module with definition outlined to a separate file `mod foo;` but not yet loaded from it.
+ Unloaded,
+}
+
+#[derive(Copy, Clone, Encodable, Decodable, Debug)]
+pub struct ModSpans {
+ /// `inner_span` covers the body of the module; for a file module, its the whole file.
+ /// For an inline module, its the span inside the `{ ... }`, not including the curly braces.
+ pub inner_span: Span,
+ pub inject_use_span: Span,
+}
+
+impl Default for ModSpans {
+ fn default() -> ModSpans {
+ ModSpans { inner_span: Default::default(), inject_use_span: Default::default() }
+ }
+}
+
+/// Foreign module declaration.
+///
+/// E.g., `extern { .. }` or `extern "C" { .. }`.
+#[derive(Clone, Encodable, Decodable, Debug)]
+pub struct ForeignMod {
+ /// `unsafe` keyword accepted syntactically for macro DSLs, but not
+ /// semantically by Rust.
+ pub unsafety: Unsafe,
+ pub abi: Option<StrLit>,
+ pub items: Vec<P<ForeignItem>>,
+}
+
+#[derive(Clone, Encodable, Decodable, Debug)]
+pub struct EnumDef {
+ pub variants: Vec<Variant>,
+}
+/// Enum variant.
+#[derive(Clone, Encodable, Decodable, Debug)]
+pub struct Variant {
+ /// Attributes of the variant.
+ pub attrs: AttrVec,
+ /// Id of the variant (not the constructor, see `VariantData::ctor_id()`).
+ pub id: NodeId,
+ /// Span
+ pub span: Span,
+ /// The visibility of the variant. Syntactically accepted but not semantically.
+ pub vis: Visibility,
+ /// Name of the variant.
+ pub ident: Ident,
+
+ /// Fields and constructor id of the variant.
+ pub data: VariantData,
+ /// Explicit discriminant, e.g., `Foo = 1`.
+ pub disr_expr: Option<AnonConst>,
+ /// Is a macro placeholder
+ pub is_placeholder: bool,
+}
+
+/// Part of `use` item to the right of its prefix.
+#[derive(Clone, Encodable, Decodable, Debug)]
+pub enum UseTreeKind {
+ /// `use prefix` or `use prefix as rename`
+ ///
+ /// The extra `NodeId`s are for HIR lowering, when additional statements are created for each
+ /// namespace.
+ Simple(Option<Ident>, NodeId, NodeId),
+ /// `use prefix::{...}`
+ Nested(Vec<(UseTree, NodeId)>),
+ /// `use prefix::*`
+ Glob,
+}
+
+/// A tree of paths sharing common prefixes.
+/// Used in `use` items both at top-level and inside of braces in import groups.
+#[derive(Clone, Encodable, Decodable, Debug)]
+pub struct UseTree {
+ pub prefix: Path,
+ pub kind: UseTreeKind,
+ pub span: Span,
+}
+
+impl UseTree {
+ pub fn ident(&self) -> Ident {
+ match self.kind {
+ UseTreeKind::Simple(Some(rename), ..) => rename,
+ UseTreeKind::Simple(None, ..) => {
+ self.prefix.segments.last().expect("empty prefix in a simple import").ident
+ }
+ _ => panic!("`UseTree::ident` can only be used on a simple import"),
+ }
+ }
+}
+
+/// Distinguishes between `Attribute`s that decorate items and Attributes that
+/// are contained as statements within items. These two cases need to be
+/// distinguished for pretty-printing.
+#[derive(Clone, PartialEq, Encodable, Decodable, Debug, Copy, HashStable_Generic)]
+pub enum AttrStyle {
+ Outer,
+ Inner,
+}
+
+rustc_index::newtype_index! {
+ pub struct AttrId {
+ ENCODABLE = custom
+ DEBUG_FORMAT = "AttrId({})"
+ }
+}
+
+impl<S: Encoder> Encodable<S> for AttrId {
+ fn encode(&self, _s: &mut S) {}
+}
+
+impl<D: Decoder> Decodable<D> for AttrId {
+ fn decode(_: &mut D) -> AttrId {
+ crate::attr::mk_attr_id()
+ }
+}
+
+#[derive(Clone, Encodable, Decodable, Debug, HashStable_Generic)]
+pub struct AttrItem {
+ pub path: Path,
+ pub args: MacArgs,
+ pub tokens: Option<LazyTokenStream>,
+}
+
+/// A list of attributes.
+pub type AttrVec = ThinVec<Attribute>;
+
+/// Metadata associated with an item.
+#[derive(Clone, Encodable, Decodable, Debug)]
+pub struct Attribute {
+ pub kind: AttrKind,
+ pub id: AttrId,
+ /// Denotes if the attribute decorates the following construct (outer)
+ /// or the construct this attribute is contained within (inner).
+ pub style: AttrStyle,
+ pub span: Span,
+}
+
+#[derive(Clone, Encodable, Decodable, Debug)]
+pub enum AttrKind {
+ /// A normal attribute.
+ Normal(AttrItem, Option<LazyTokenStream>),
+
+ /// A doc comment (e.g. `/// ...`, `//! ...`, `/** ... */`, `/*! ... */`).
+ /// Doc attributes (e.g. `#[doc="..."]`) are represented with the `Normal`
+ /// variant (which is much less compact and thus more expensive).
+ DocComment(CommentKind, Symbol),
+}
+
+/// `TraitRef`s appear in impls.
+///
+/// Resolution maps each `TraitRef`'s `ref_id` to its defining trait; that's all
+/// that the `ref_id` is for. The `impl_id` maps to the "self type" of this impl.
+/// If this impl is an `ItemKind::Impl`, the `impl_id` is redundant (it could be the
+/// same as the impl's `NodeId`).
+#[derive(Clone, Encodable, Decodable, Debug)]
+pub struct TraitRef {
+ pub path: Path,
+ pub ref_id: NodeId,
+}
+
+#[derive(Clone, Encodable, Decodable, Debug)]
+pub struct PolyTraitRef {
+ /// The `'a` in `for<'a> Foo<&'a T>`.
+ pub bound_generic_params: Vec<GenericParam>,
+
+ /// The `Foo<&'a T>` in `<'a> Foo<&'a T>`.
+ pub trait_ref: TraitRef,
+
+ pub span: Span,
+}
+
+impl PolyTraitRef {
+ pub fn new(generic_params: Vec<GenericParam>, path: Path, span: Span) -> Self {
+ PolyTraitRef {
+ bound_generic_params: generic_params,
+ trait_ref: TraitRef { path, ref_id: DUMMY_NODE_ID },
+ span,
+ }
+ }
+}
+
+#[derive(Clone, Encodable, Decodable, Debug)]
+pub struct Visibility {
+ pub kind: VisibilityKind,
+ pub span: Span,
+ pub tokens: Option<LazyTokenStream>,
+}
+
+#[derive(Clone, Encodable, Decodable, Debug)]
+pub enum VisibilityKind {
+ Public,
+ Restricted { path: P<Path>, id: NodeId },
+ Inherited,
+}
+
+impl VisibilityKind {
+ pub fn is_pub(&self) -> bool {
+ matches!(self, VisibilityKind::Public)
+ }
+}
+
+/// Field definition in a struct, variant or union.
+///
+/// E.g., `bar: usize` as in `struct Foo { bar: usize }`.
+#[derive(Clone, Encodable, Decodable, Debug)]
+pub struct FieldDef {
+ pub attrs: AttrVec,
+ pub id: NodeId,
+ pub span: Span,
+ pub vis: Visibility,
+ pub ident: Option<Ident>,
+
+ pub ty: P<Ty>,
+ pub is_placeholder: bool,
+}
+
+/// Fields and constructor ids of enum variants and structs.
+#[derive(Clone, Encodable, Decodable, Debug)]
+pub enum VariantData {
+ /// Struct variant.
+ ///
+ /// E.g., `Bar { .. }` as in `enum Foo { Bar { .. } }`.
+ Struct(Vec<FieldDef>, bool),
+ /// Tuple variant.
+ ///
+ /// E.g., `Bar(..)` as in `enum Foo { Bar(..) }`.
+ Tuple(Vec<FieldDef>, NodeId),
+ /// Unit variant.
+ ///
+ /// E.g., `Bar = ..` as in `enum Foo { Bar = .. }`.
+ Unit(NodeId),
+}
+
+impl VariantData {
+ /// Return the fields of this variant.
+ pub fn fields(&self) -> &[FieldDef] {
+ match *self {
+ VariantData::Struct(ref fields, ..) | VariantData::Tuple(ref fields, _) => fields,
+ _ => &[],
+ }
+ }
+
+ /// Return the `NodeId` of this variant's constructor, if it has one.
+ pub fn ctor_id(&self) -> Option<NodeId> {
+ match *self {
+ VariantData::Struct(..) => None,
+ VariantData::Tuple(_, id) | VariantData::Unit(id) => Some(id),
+ }
+ }
+}
+
+/// An item definition.
+#[derive(Clone, Encodable, Decodable, Debug)]
+pub struct Item<K = ItemKind> {
+ pub attrs: Vec<Attribute>,
+ pub id: NodeId,
+ pub span: Span,
+ pub vis: Visibility,
+ /// The name of the item.
+ /// It might be a dummy name in case of anonymous items.
+ pub ident: Ident,
+
+ pub kind: K,
+
+ /// Original tokens this item was parsed from. This isn't necessarily
+ /// available for all items, although over time more and more items should
+ /// have this be `Some`. Right now this is primarily used for procedural
+ /// macros, notably custom attributes.
+ ///
+ /// Note that the tokens here do not include the outer attributes, but will
+ /// include inner attributes.
+ pub tokens: Option<LazyTokenStream>,
+}
+
+impl Item {
+ /// Return the span that encompasses the attributes.
+ pub fn span_with_attributes(&self) -> Span {
+ self.attrs.iter().fold(self.span, |acc, attr| acc.to(attr.span))
+ }
+}
+
+/// `extern` qualifier on a function item or function type.
+#[derive(Clone, Copy, Encodable, Decodable, Debug)]
+pub enum Extern {
+ None,
+ Implicit(Span),
+ Explicit(StrLit, Span),
+}
+
+impl Extern {
+ pub fn from_abi(abi: Option<StrLit>, span: Span) -> Extern {
+ match abi {
+ Some(name) => Extern::Explicit(name, span),
+ None => Extern::Implicit(span),
+ }
+ }
+}
+
+/// A function header.
+///
+/// All the information between the visibility and the name of the function is
+/// included in this struct (e.g., `async unsafe fn` or `const extern "C" fn`).
+#[derive(Clone, Copy, Encodable, Decodable, Debug)]
+pub struct FnHeader {
+ pub unsafety: Unsafe,
+ pub asyncness: Async,
+ pub constness: Const,
+ pub ext: Extern,
+}
+
+impl FnHeader {
+ /// Does this function header have any qualifiers or is it empty?
+ pub fn has_qualifiers(&self) -> bool {
+ let Self { unsafety, asyncness, constness, ext } = self;
+ matches!(unsafety, Unsafe::Yes(_))
+ || asyncness.is_async()
+ || matches!(constness, Const::Yes(_))
+ || !matches!(ext, Extern::None)
+ }
+}
+
+impl Default for FnHeader {
+ fn default() -> FnHeader {
+ FnHeader {
+ unsafety: Unsafe::No,
+ asyncness: Async::No,
+ constness: Const::No,
+ ext: Extern::None,
+ }
+ }
+}
+
+#[derive(Clone, Encodable, Decodable, Debug)]
+pub struct Trait {
+ pub unsafety: Unsafe,
+ pub is_auto: IsAuto,
+ pub generics: Generics,
+ pub bounds: GenericBounds,
+ pub items: Vec<P<AssocItem>>,
+}
+
+/// The location of a where clause on a `TyAlias` (`Span`) and whether there was
+/// a `where` keyword (`bool`). This is split out from `WhereClause`, since there
+/// are two locations for where clause on type aliases, but their predicates
+/// are concatenated together.
+///
+/// Take this example:
+/// ```ignore (only-for-syntax-highlight)
+/// trait Foo {
+/// type Assoc<'a, 'b> where Self: 'a, Self: 'b;
+/// }
+/// impl Foo for () {
+/// type Assoc<'a, 'b> where Self: 'a = () where Self: 'b;
+/// // ^^^^^^^^^^^^^^ first where clause
+/// // ^^^^^^^^^^^^^^ second where clause
+/// }
+/// ```
+///
+/// If there is no where clause, then this is `false` with `DUMMY_SP`.
+#[derive(Copy, Clone, Encodable, Decodable, Debug, Default)]
+pub struct TyAliasWhereClause(pub bool, pub Span);
+
+#[derive(Clone, Encodable, Decodable, Debug)]
+pub struct TyAlias {
+ pub defaultness: Defaultness,
+ pub generics: Generics,
+ /// The span information for the two where clauses (before equals, after equals)
+ pub where_clauses: (TyAliasWhereClause, TyAliasWhereClause),
+ /// The index in `generics.where_clause.predicates` that would split into
+ /// predicates from the where clause before the equals and the predicates
+ /// from the where clause after the equals
+ pub where_predicates_split: usize,
+ pub bounds: GenericBounds,
+ pub ty: Option<P<Ty>>,
+}
+
+#[derive(Clone, Encodable, Decodable, Debug)]
+pub struct Impl {
+ pub defaultness: Defaultness,
+ pub unsafety: Unsafe,
+ pub generics: Generics,
+ pub constness: Const,
+ pub polarity: ImplPolarity,
+ /// The trait being implemented, if any.
+ pub of_trait: Option<TraitRef>,
+ pub self_ty: P<Ty>,
+ pub items: Vec<P<AssocItem>>,
+}
+
+#[derive(Clone, Encodable, Decodable, Debug)]
+pub struct Fn {
+ pub defaultness: Defaultness,
+ pub generics: Generics,
+ pub sig: FnSig,
+ pub body: Option<P<Block>>,
+}
+
+#[derive(Clone, Encodable, Decodable, Debug)]
+pub enum ItemKind {
+ /// An `extern crate` item, with the optional *original* crate name if the crate was renamed.
+ ///
+ /// E.g., `extern crate foo` or `extern crate foo_bar as foo`.
+ ExternCrate(Option<Symbol>),
+ /// A use declaration item (`use`).
+ ///
+ /// E.g., `use foo;`, `use foo::bar;` or `use foo::bar as FooBar;`.
+ Use(UseTree),
+ /// A static item (`static`).
+ ///
+ /// E.g., `static FOO: i32 = 42;` or `static FOO: &'static str = "bar";`.
+ Static(P<Ty>, Mutability, Option<P<Expr>>),
+ /// A constant item (`const`).
+ ///
+ /// E.g., `const FOO: i32 = 42;`.
+ Const(Defaultness, P<Ty>, Option<P<Expr>>),
+ /// A function declaration (`fn`).
+ ///
+ /// E.g., `fn foo(bar: usize) -> usize { .. }`.
+ Fn(Box<Fn>),
+ /// A module declaration (`mod`).
+ ///
+ /// E.g., `mod foo;` or `mod foo { .. }`.
+ /// `unsafe` keyword on modules is accepted syntactically for macro DSLs, but not
+ /// semantically by Rust.
+ Mod(Unsafe, ModKind),
+ /// An external module (`extern`).
+ ///
+ /// E.g., `extern {}` or `extern "C" {}`.
+ ForeignMod(ForeignMod),
+ /// Module-level inline assembly (from `global_asm!()`).
+ GlobalAsm(Box<InlineAsm>),
+ /// A type alias (`type`).
+ ///
+ /// E.g., `type Foo = Bar<u8>;`.
+ TyAlias(Box<TyAlias>),
+ /// An enum definition (`enum`).
+ ///
+ /// E.g., `enum Foo<A, B> { C<A>, D<B> }`.
+ Enum(EnumDef, Generics),
+ /// A struct definition (`struct`).
+ ///
+ /// E.g., `struct Foo<A> { x: A }`.
+ Struct(VariantData, Generics),
+ /// A union definition (`union`).
+ ///
+ /// E.g., `union Foo<A, B> { x: A, y: B }`.
+ Union(VariantData, Generics),
+ /// A trait declaration (`trait`).
+ ///
+ /// E.g., `trait Foo { .. }`, `trait Foo<T> { .. }` or `auto trait Foo {}`.
+ Trait(Box<Trait>),
+ /// Trait alias
+ ///
+ /// E.g., `trait Foo = Bar + Quux;`.
+ TraitAlias(Generics, GenericBounds),
+ /// An implementation.
+ ///
+ /// E.g., `impl<A> Foo<A> { .. }` or `impl<A> Trait for Foo<A> { .. }`.
+ Impl(Box<Impl>),
+ /// A macro invocation.
+ ///
+ /// E.g., `foo!(..)`.
+ MacCall(MacCall),
+
+ /// A macro definition.
+ MacroDef(MacroDef),
+}
+
+impl ItemKind {
+ pub fn article(&self) -> &str {
+ use ItemKind::*;
+ match self {
+ Use(..) | Static(..) | Const(..) | Fn(..) | Mod(..) | GlobalAsm(..) | TyAlias(..)
+ | Struct(..) | Union(..) | Trait(..) | TraitAlias(..) | MacroDef(..) => "a",
+ ExternCrate(..) | ForeignMod(..) | MacCall(..) | Enum(..) | Impl { .. } => "an",
+ }
+ }
+
+ pub fn descr(&self) -> &str {
+ match self {
+ ItemKind::ExternCrate(..) => "extern crate",
+ ItemKind::Use(..) => "`use` import",
+ ItemKind::Static(..) => "static item",
+ ItemKind::Const(..) => "constant item",
+ ItemKind::Fn(..) => "function",
+ ItemKind::Mod(..) => "module",
+ ItemKind::ForeignMod(..) => "extern block",
+ ItemKind::GlobalAsm(..) => "global asm item",
+ ItemKind::TyAlias(..) => "type alias",
+ ItemKind::Enum(..) => "enum",
+ ItemKind::Struct(..) => "struct",
+ ItemKind::Union(..) => "union",
+ ItemKind::Trait(..) => "trait",
+ ItemKind::TraitAlias(..) => "trait alias",
+ ItemKind::MacCall(..) => "item macro invocation",
+ ItemKind::MacroDef(..) => "macro definition",
+ ItemKind::Impl { .. } => "implementation",
+ }
+ }
+
+ pub fn generics(&self) -> Option<&Generics> {
+ match self {
+ Self::Fn(box Fn { generics, .. })
+ | Self::TyAlias(box TyAlias { generics, .. })
+ | Self::Enum(_, generics)
+ | Self::Struct(_, generics)
+ | Self::Union(_, generics)
+ | Self::Trait(box Trait { generics, .. })
+ | Self::TraitAlias(generics, _)
+ | Self::Impl(box Impl { generics, .. }) => Some(generics),
+ _ => None,
+ }
+ }
+}
+
+/// Represents associated items.
+/// These include items in `impl` and `trait` definitions.
+pub type AssocItem = Item<AssocItemKind>;
+
+/// Represents associated item kinds.
+///
+/// The term "provided" in the variants below refers to the item having a default
+/// definition / body. Meanwhile, a "required" item lacks a definition / body.
+/// In an implementation, all items must be provided.
+/// The `Option`s below denote the bodies, where `Some(_)`
+/// means "provided" and conversely `None` means "required".
+#[derive(Clone, Encodable, Decodable, Debug)]
+pub enum AssocItemKind {
+ /// An associated constant, `const $ident: $ty $def?;` where `def ::= "=" $expr? ;`.
+ /// If `def` is parsed, then the constant is provided, and otherwise required.
+ Const(Defaultness, P<Ty>, Option<P<Expr>>),
+ /// An associated function.
+ Fn(Box<Fn>),
+ /// An associated type.
+ TyAlias(Box<TyAlias>),
+ /// A macro expanding to associated items.
+ MacCall(MacCall),
+}
+
+impl AssocItemKind {
+ pub fn defaultness(&self) -> Defaultness {
+ match *self {
+ Self::Const(defaultness, ..)
+ | Self::Fn(box Fn { defaultness, .. })
+ | Self::TyAlias(box TyAlias { defaultness, .. }) => defaultness,
+ Self::MacCall(..) => Defaultness::Final,
+ }
+ }
+}
+
+impl From<AssocItemKind> for ItemKind {
+ fn from(assoc_item_kind: AssocItemKind) -> ItemKind {
+ match assoc_item_kind {
+ AssocItemKind::Const(a, b, c) => ItemKind::Const(a, b, c),
+ AssocItemKind::Fn(fn_kind) => ItemKind::Fn(fn_kind),
+ AssocItemKind::TyAlias(ty_alias_kind) => ItemKind::TyAlias(ty_alias_kind),
+ AssocItemKind::MacCall(a) => ItemKind::MacCall(a),
+ }
+ }
+}
+
+impl TryFrom<ItemKind> for AssocItemKind {
+ type Error = ItemKind;
+
+ fn try_from(item_kind: ItemKind) -> Result<AssocItemKind, ItemKind> {
+ Ok(match item_kind {
+ ItemKind::Const(a, b, c) => AssocItemKind::Const(a, b, c),
+ ItemKind::Fn(fn_kind) => AssocItemKind::Fn(fn_kind),
+ ItemKind::TyAlias(ty_alias_kind) => AssocItemKind::TyAlias(ty_alias_kind),
+ ItemKind::MacCall(a) => AssocItemKind::MacCall(a),
+ _ => return Err(item_kind),
+ })
+ }
+}
+
+/// An item in `extern` block.
+#[derive(Clone, Encodable, Decodable, Debug)]
+pub enum ForeignItemKind {
+ /// A foreign static item (`static FOO: u8`).
+ Static(P<Ty>, Mutability, Option<P<Expr>>),
+ /// An foreign function.
+ Fn(Box<Fn>),
+ /// An foreign type.
+ TyAlias(Box<TyAlias>),
+ /// A macro expanding to foreign items.
+ MacCall(MacCall),
+}
+
+impl From<ForeignItemKind> for ItemKind {
+ fn from(foreign_item_kind: ForeignItemKind) -> ItemKind {
+ match foreign_item_kind {
+ ForeignItemKind::Static(a, b, c) => ItemKind::Static(a, b, c),
+ ForeignItemKind::Fn(fn_kind) => ItemKind::Fn(fn_kind),
+ ForeignItemKind::TyAlias(ty_alias_kind) => ItemKind::TyAlias(ty_alias_kind),
+ ForeignItemKind::MacCall(a) => ItemKind::MacCall(a),
+ }
+ }
+}
+
+impl TryFrom<ItemKind> for ForeignItemKind {
+ type Error = ItemKind;
+
+ fn try_from(item_kind: ItemKind) -> Result<ForeignItemKind, ItemKind> {
+ Ok(match item_kind {
+ ItemKind::Static(a, b, c) => ForeignItemKind::Static(a, b, c),
+ ItemKind::Fn(fn_kind) => ForeignItemKind::Fn(fn_kind),
+ ItemKind::TyAlias(ty_alias_kind) => ForeignItemKind::TyAlias(ty_alias_kind),
+ ItemKind::MacCall(a) => ForeignItemKind::MacCall(a),
+ _ => return Err(item_kind),
+ })
+ }
+}
+
+pub type ForeignItem = Item<ForeignItemKind>;
+
+// Some nodes are used a lot. Make sure they don't unintentionally get bigger.
+#[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))]
+mod size_asserts {
+ use super::*;
+ // These are in alphabetical order, which is easy to maintain.
+ rustc_data_structures::static_assert_size!(AssocItemKind, 72);
+ rustc_data_structures::static_assert_size!(Attribute, 152);
+ rustc_data_structures::static_assert_size!(Block, 48);
+ rustc_data_structures::static_assert_size!(Expr, 104);
+ rustc_data_structures::static_assert_size!(Fn, 192);
+ rustc_data_structures::static_assert_size!(ForeignItemKind, 72);
+ rustc_data_structures::static_assert_size!(GenericBound, 88);
+ rustc_data_structures::static_assert_size!(Generics, 72);
+ rustc_data_structures::static_assert_size!(Impl, 200);
+ rustc_data_structures::static_assert_size!(Item, 200);
+ rustc_data_structures::static_assert_size!(ItemKind, 112);
+ rustc_data_structures::static_assert_size!(Lit, 48);
+ rustc_data_structures::static_assert_size!(Pat, 120);
+ rustc_data_structures::static_assert_size!(Path, 40);
+ rustc_data_structures::static_assert_size!(PathSegment, 24);
+ rustc_data_structures::static_assert_size!(Stmt, 32);
+ rustc_data_structures::static_assert_size!(Ty, 96);
+}
diff --git a/compiler/rustc_ast/src/ast_traits.rs b/compiler/rustc_ast/src/ast_traits.rs
new file mode 100644
index 000000000..5c30a75a1
--- /dev/null
+++ b/compiler/rustc_ast/src/ast_traits.rs
@@ -0,0 +1,442 @@
+//! A set of traits implemented for various AST nodes,
+//! typically those used in AST fragments during macro expansion.
+//! The traits are not implemented exhaustively, only when actually necessary.
+
+use crate::ptr::P;
+use crate::token::Nonterminal;
+use crate::tokenstream::LazyTokenStream;
+use crate::{Arm, Crate, ExprField, FieldDef, GenericParam, Param, PatField, Variant};
+use crate::{AssocItem, Expr, ForeignItem, Item, NodeId};
+use crate::{AttrItem, AttrKind, Block, Pat, Path, Ty, Visibility};
+use crate::{AttrVec, Attribute, Stmt, StmtKind};
+
+use rustc_span::Span;
+
+use std::fmt;
+use std::marker::PhantomData;
+
+/// A utility trait to reduce boilerplate.
+/// Standard `Deref(Mut)` cannot be reused due to coherence.
+pub trait AstDeref {
+ type Target;
+ fn ast_deref(&self) -> &Self::Target;
+ fn ast_deref_mut(&mut self) -> &mut Self::Target;
+}
+
+macro_rules! impl_not_ast_deref {
+ ($($T:ty),+ $(,)?) => {
+ $(
+ impl !AstDeref for $T {}
+ )+
+ };
+}
+
+impl_not_ast_deref!(AssocItem, Expr, ForeignItem, Item, Stmt);
+
+impl<T> AstDeref for P<T> {
+ type Target = T;
+ fn ast_deref(&self) -> &Self::Target {
+ self
+ }
+ fn ast_deref_mut(&mut self) -> &mut Self::Target {
+ self
+ }
+}
+
+/// A trait for AST nodes having an ID.
+pub trait HasNodeId {
+ fn node_id(&self) -> NodeId;
+ fn node_id_mut(&mut self) -> &mut NodeId;
+}
+
+macro_rules! impl_has_node_id {
+ ($($T:ty),+ $(,)?) => {
+ $(
+ impl HasNodeId for $T {
+ fn node_id(&self) -> NodeId {
+ self.id
+ }
+ fn node_id_mut(&mut self) -> &mut NodeId {
+ &mut self.id
+ }
+ }
+ )+
+ };
+}
+
+impl_has_node_id!(
+ Arm,
+ AssocItem,
+ Crate,
+ Expr,
+ ExprField,
+ FieldDef,
+ ForeignItem,
+ GenericParam,
+ Item,
+ Param,
+ Pat,
+ PatField,
+ Stmt,
+ Ty,
+ Variant,
+);
+
+impl<T: AstDeref<Target: HasNodeId>> HasNodeId for T {
+ fn node_id(&self) -> NodeId {
+ self.ast_deref().node_id()
+ }
+ fn node_id_mut(&mut self) -> &mut NodeId {
+ self.ast_deref_mut().node_id_mut()
+ }
+}
+
+/// A trait for AST nodes having a span.
+pub trait HasSpan {
+ fn span(&self) -> Span;
+}
+
+macro_rules! impl_has_span {
+ ($($T:ty),+ $(,)?) => {
+ $(
+ impl HasSpan for $T {
+ fn span(&self) -> Span {
+ self.span
+ }
+ }
+ )+
+ };
+}
+
+impl_has_span!(AssocItem, Block, Expr, ForeignItem, Item, Pat, Path, Stmt, Ty, Visibility);
+
+impl<T: AstDeref<Target: HasSpan>> HasSpan for T {
+ fn span(&self) -> Span {
+ self.ast_deref().span()
+ }
+}
+
+impl HasSpan for AttrItem {
+ fn span(&self) -> Span {
+ self.span()
+ }
+}
+
+/// A trait for AST nodes having (or not having) collected tokens.
+pub trait HasTokens {
+ fn tokens(&self) -> Option<&LazyTokenStream>;
+ fn tokens_mut(&mut self) -> Option<&mut Option<LazyTokenStream>>;
+}
+
+macro_rules! impl_has_tokens {
+ ($($T:ty),+ $(,)?) => {
+ $(
+ impl HasTokens for $T {
+ fn tokens(&self) -> Option<&LazyTokenStream> {
+ self.tokens.as_ref()
+ }
+ fn tokens_mut(&mut self) -> Option<&mut Option<LazyTokenStream>> {
+ Some(&mut self.tokens)
+ }
+ }
+ )+
+ };
+}
+
+macro_rules! impl_has_tokens_none {
+ ($($T:ty),+ $(,)?) => {
+ $(
+ impl HasTokens for $T {
+ fn tokens(&self) -> Option<&LazyTokenStream> {
+ None
+ }
+ fn tokens_mut(&mut self) -> Option<&mut Option<LazyTokenStream>> {
+ None
+ }
+ }
+ )+
+ };
+}
+
+impl_has_tokens!(AssocItem, AttrItem, Block, Expr, ForeignItem, Item, Pat, Path, Ty, Visibility);
+impl_has_tokens_none!(Arm, ExprField, FieldDef, GenericParam, Param, PatField, Variant);
+
+impl<T: AstDeref<Target: HasTokens>> HasTokens for T {
+ fn tokens(&self) -> Option<&LazyTokenStream> {
+ self.ast_deref().tokens()
+ }
+ fn tokens_mut(&mut self) -> Option<&mut Option<LazyTokenStream>> {
+ self.ast_deref_mut().tokens_mut()
+ }
+}
+
+impl<T: HasTokens> HasTokens for Option<T> {
+ fn tokens(&self) -> Option<&LazyTokenStream> {
+ self.as_ref().and_then(|inner| inner.tokens())
+ }
+ fn tokens_mut(&mut self) -> Option<&mut Option<LazyTokenStream>> {
+ self.as_mut().and_then(|inner| inner.tokens_mut())
+ }
+}
+
+impl HasTokens for StmtKind {
+ fn tokens(&self) -> Option<&LazyTokenStream> {
+ match self {
+ StmtKind::Local(local) => local.tokens.as_ref(),
+ StmtKind::Item(item) => item.tokens(),
+ StmtKind::Expr(expr) | StmtKind::Semi(expr) => expr.tokens(),
+ StmtKind::Empty => return None,
+ StmtKind::MacCall(mac) => mac.tokens.as_ref(),
+ }
+ }
+ fn tokens_mut(&mut self) -> Option<&mut Option<LazyTokenStream>> {
+ match self {
+ StmtKind::Local(local) => Some(&mut local.tokens),
+ StmtKind::Item(item) => item.tokens_mut(),
+ StmtKind::Expr(expr) | StmtKind::Semi(expr) => expr.tokens_mut(),
+ StmtKind::Empty => return None,
+ StmtKind::MacCall(mac) => Some(&mut mac.tokens),
+ }
+ }
+}
+
+impl HasTokens for Stmt {
+ fn tokens(&self) -> Option<&LazyTokenStream> {
+ self.kind.tokens()
+ }
+ fn tokens_mut(&mut self) -> Option<&mut Option<LazyTokenStream>> {
+ self.kind.tokens_mut()
+ }
+}
+
+impl HasTokens for Attribute {
+ fn tokens(&self) -> Option<&LazyTokenStream> {
+ match &self.kind {
+ AttrKind::Normal(_, tokens) => tokens.as_ref(),
+ kind @ AttrKind::DocComment(..) => {
+ panic!("Called tokens on doc comment attr {:?}", kind)
+ }
+ }
+ }
+ fn tokens_mut(&mut self) -> Option<&mut Option<LazyTokenStream>> {
+ Some(match &mut self.kind {
+ AttrKind::Normal(_, tokens) => tokens,
+ kind @ AttrKind::DocComment(..) => {
+ panic!("Called tokens_mut on doc comment attr {:?}", kind)
+ }
+ })
+ }
+}
+
+impl HasTokens for Nonterminal {
+ fn tokens(&self) -> Option<&LazyTokenStream> {
+ match self {
+ Nonterminal::NtItem(item) => item.tokens(),
+ Nonterminal::NtStmt(stmt) => stmt.tokens(),
+ Nonterminal::NtExpr(expr) | Nonterminal::NtLiteral(expr) => expr.tokens(),
+ Nonterminal::NtPat(pat) => pat.tokens(),
+ Nonterminal::NtTy(ty) => ty.tokens(),
+ Nonterminal::NtMeta(attr_item) => attr_item.tokens(),
+ Nonterminal::NtPath(path) => path.tokens(),
+ Nonterminal::NtVis(vis) => vis.tokens(),
+ Nonterminal::NtBlock(block) => block.tokens(),
+ Nonterminal::NtIdent(..) | Nonterminal::NtLifetime(..) => None,
+ }
+ }
+ fn tokens_mut(&mut self) -> Option<&mut Option<LazyTokenStream>> {
+ match self {
+ Nonterminal::NtItem(item) => item.tokens_mut(),
+ Nonterminal::NtStmt(stmt) => stmt.tokens_mut(),
+ Nonterminal::NtExpr(expr) | Nonterminal::NtLiteral(expr) => expr.tokens_mut(),
+ Nonterminal::NtPat(pat) => pat.tokens_mut(),
+ Nonterminal::NtTy(ty) => ty.tokens_mut(),
+ Nonterminal::NtMeta(attr_item) => attr_item.tokens_mut(),
+ Nonterminal::NtPath(path) => path.tokens_mut(),
+ Nonterminal::NtVis(vis) => vis.tokens_mut(),
+ Nonterminal::NtBlock(block) => block.tokens_mut(),
+ Nonterminal::NtIdent(..) | Nonterminal::NtLifetime(..) => None,
+ }
+ }
+}
+
+/// A trait for AST nodes having (or not having) attributes.
+pub trait HasAttrs {
+ /// This is `true` if this `HasAttrs` might support 'custom' (proc-macro) inner
+ /// attributes. Attributes like `#![cfg]` and `#![cfg_attr]` are not
+ /// considered 'custom' attributes.
+ ///
+ /// If this is `false`, then this `HasAttrs` definitely does
+ /// not support 'custom' inner attributes, which enables some optimizations
+ /// during token collection.
+ const SUPPORTS_CUSTOM_INNER_ATTRS: bool;
+ fn attrs(&self) -> &[Attribute];
+ fn visit_attrs(&mut self, f: impl FnOnce(&mut Vec<Attribute>));
+}
+
+macro_rules! impl_has_attrs {
+ (const SUPPORTS_CUSTOM_INNER_ATTRS: bool = $inner:literal, $($T:ty),+ $(,)?) => {
+ $(
+ impl HasAttrs for $T {
+ const SUPPORTS_CUSTOM_INNER_ATTRS: bool = $inner;
+
+ fn attrs(&self) -> &[Attribute] {
+ &self.attrs
+ }
+
+ fn visit_attrs(&mut self, f: impl FnOnce(&mut Vec<Attribute>)) {
+ VecOrAttrVec::visit(&mut self.attrs, f)
+ }
+ }
+ )+
+ };
+}
+
+macro_rules! impl_has_attrs_none {
+ ($($T:ty),+ $(,)?) => {
+ $(
+ impl HasAttrs for $T {
+ const SUPPORTS_CUSTOM_INNER_ATTRS: bool = false;
+ fn attrs(&self) -> &[Attribute] {
+ &[]
+ }
+ fn visit_attrs(&mut self, _f: impl FnOnce(&mut Vec<Attribute>)) {}
+ }
+ )+
+ };
+}
+
+impl_has_attrs!(
+ const SUPPORTS_CUSTOM_INNER_ATTRS: bool = true,
+ AssocItem,
+ ForeignItem,
+ Item,
+);
+impl_has_attrs!(
+ const SUPPORTS_CUSTOM_INNER_ATTRS: bool = false,
+ Arm,
+ Crate,
+ Expr,
+ ExprField,
+ FieldDef,
+ GenericParam,
+ Param,
+ PatField,
+ Variant,
+);
+impl_has_attrs_none!(Attribute, AttrItem, Block, Pat, Path, Ty, Visibility);
+
+impl<T: AstDeref<Target: HasAttrs>> HasAttrs for T {
+ const SUPPORTS_CUSTOM_INNER_ATTRS: bool = T::Target::SUPPORTS_CUSTOM_INNER_ATTRS;
+ fn attrs(&self) -> &[Attribute] {
+ self.ast_deref().attrs()
+ }
+ fn visit_attrs(&mut self, f: impl FnOnce(&mut Vec<Attribute>)) {
+ self.ast_deref_mut().visit_attrs(f)
+ }
+}
+
+impl<T: HasAttrs> HasAttrs for Option<T> {
+ const SUPPORTS_CUSTOM_INNER_ATTRS: bool = T::SUPPORTS_CUSTOM_INNER_ATTRS;
+ fn attrs(&self) -> &[Attribute] {
+ self.as_ref().map(|inner| inner.attrs()).unwrap_or(&[])
+ }
+ fn visit_attrs(&mut self, f: impl FnOnce(&mut Vec<Attribute>)) {
+ if let Some(inner) = self.as_mut() {
+ inner.visit_attrs(f);
+ }
+ }
+}
+
+impl HasAttrs for StmtKind {
+ // This might be a `StmtKind::Item`, which contains
+ // an item that supports inner attrs.
+ const SUPPORTS_CUSTOM_INNER_ATTRS: bool = true;
+
+ fn attrs(&self) -> &[Attribute] {
+ match self {
+ StmtKind::Local(local) => &local.attrs,
+ StmtKind::Expr(expr) | StmtKind::Semi(expr) => expr.attrs(),
+ StmtKind::Item(item) => item.attrs(),
+ StmtKind::Empty => &[],
+ StmtKind::MacCall(mac) => &mac.attrs,
+ }
+ }
+
+ fn visit_attrs(&mut self, f: impl FnOnce(&mut Vec<Attribute>)) {
+ match self {
+ StmtKind::Local(local) => visit_attrvec(&mut local.attrs, f),
+ StmtKind::Expr(expr) | StmtKind::Semi(expr) => expr.visit_attrs(f),
+ StmtKind::Item(item) => item.visit_attrs(f),
+ StmtKind::Empty => {}
+ StmtKind::MacCall(mac) => visit_attrvec(&mut mac.attrs, f),
+ }
+ }
+}
+
+impl HasAttrs for Stmt {
+ const SUPPORTS_CUSTOM_INNER_ATTRS: bool = StmtKind::SUPPORTS_CUSTOM_INNER_ATTRS;
+ fn attrs(&self) -> &[Attribute] {
+ self.kind.attrs()
+ }
+ fn visit_attrs(&mut self, f: impl FnOnce(&mut Vec<Attribute>)) {
+ self.kind.visit_attrs(f);
+ }
+}
+
+/// Helper trait for the impls above. Abstracts over
+/// the two types of attribute fields that AST nodes
+/// may have (`Vec<Attribute>` or `AttrVec`).
+trait VecOrAttrVec {
+ fn visit(&mut self, f: impl FnOnce(&mut Vec<Attribute>));
+}
+
+impl VecOrAttrVec for Vec<Attribute> {
+ fn visit(&mut self, f: impl FnOnce(&mut Vec<Attribute>)) {
+ f(self)
+ }
+}
+
+impl VecOrAttrVec for AttrVec {
+ fn visit(&mut self, f: impl FnOnce(&mut Vec<Attribute>)) {
+ visit_attrvec(self, f)
+ }
+}
+
+fn visit_attrvec(attrs: &mut AttrVec, f: impl FnOnce(&mut Vec<Attribute>)) {
+ crate::mut_visit::visit_clobber(attrs, |attrs| {
+ let mut vec = attrs.into();
+ f(&mut vec);
+ vec.into()
+ });
+}
+
+/// A newtype around an AST node that implements the traits above if the node implements them.
+pub struct AstNodeWrapper<Wrapped, Tag> {
+ pub wrapped: Wrapped,
+ pub tag: PhantomData<Tag>,
+}
+
+impl<Wrapped, Tag> AstNodeWrapper<Wrapped, Tag> {
+ pub fn new(wrapped: Wrapped, _tag: Tag) -> AstNodeWrapper<Wrapped, Tag> {
+ AstNodeWrapper { wrapped, tag: Default::default() }
+ }
+}
+
+impl<Wrapped, Tag> AstDeref for AstNodeWrapper<Wrapped, Tag> {
+ type Target = Wrapped;
+ fn ast_deref(&self) -> &Self::Target {
+ &self.wrapped
+ }
+ fn ast_deref_mut(&mut self) -> &mut Self::Target {
+ &mut self.wrapped
+ }
+}
+
+impl<Wrapped: fmt::Debug, Tag> fmt::Debug for AstNodeWrapper<Wrapped, Tag> {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ f.debug_struct("AstNodeWrapper")
+ .field("wrapped", &self.wrapped)
+ .field("tag", &self.tag)
+ .finish()
+ }
+}
diff --git a/compiler/rustc_ast/src/attr/mod.rs b/compiler/rustc_ast/src/attr/mod.rs
new file mode 100644
index 000000000..86af7769d
--- /dev/null
+++ b/compiler/rustc_ast/src/attr/mod.rs
@@ -0,0 +1,634 @@
+//! Functions dealing with attributes and meta items.
+
+use crate::ast;
+use crate::ast::{AttrId, AttrItem, AttrKind, AttrStyle, Attribute};
+use crate::ast::{Lit, LitKind};
+use crate::ast::{MacArgs, MacArgsEq, MacDelimiter, MetaItem, MetaItemKind, NestedMetaItem};
+use crate::ast::{Path, PathSegment};
+use crate::ptr::P;
+use crate::token::{self, CommentKind, Delimiter, Token};
+use crate::tokenstream::{AttrAnnotatedTokenStream, AttrAnnotatedTokenTree};
+use crate::tokenstream::{DelimSpan, Spacing, TokenTree};
+use crate::tokenstream::{LazyTokenStream, TokenStream};
+use crate::util::comments;
+
+use rustc_data_structures::thin_vec::ThinVec;
+use rustc_index::bit_set::GrowableBitSet;
+use rustc_span::source_map::BytePos;
+use rustc_span::symbol::{sym, Ident, Symbol};
+use rustc_span::Span;
+
+use std::iter;
+
+pub struct MarkedAttrs(GrowableBitSet<AttrId>);
+
+impl MarkedAttrs {
+ // We have no idea how many attributes there will be, so just
+ // initiate the vectors with 0 bits. We'll grow them as necessary.
+ pub fn new() -> Self {
+ MarkedAttrs(GrowableBitSet::new_empty())
+ }
+
+ pub fn mark(&mut self, attr: &Attribute) {
+ self.0.insert(attr.id);
+ }
+
+ pub fn is_marked(&self, attr: &Attribute) -> bool {
+ self.0.contains(attr.id)
+ }
+}
+
+impl NestedMetaItem {
+ /// Returns the `MetaItem` if `self` is a `NestedMetaItem::MetaItem`.
+ pub fn meta_item(&self) -> Option<&MetaItem> {
+ match *self {
+ NestedMetaItem::MetaItem(ref item) => Some(item),
+ _ => None,
+ }
+ }
+
+ /// Returns the `Lit` if `self` is a `NestedMetaItem::Literal`s.
+ pub fn literal(&self) -> Option<&Lit> {
+ match *self {
+ NestedMetaItem::Literal(ref lit) => Some(lit),
+ _ => None,
+ }
+ }
+
+ /// 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))
+ }
+
+ /// 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
+ }
+
+ /// 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 a name and single literal value tuple of the `MetaItem`.
+ pub fn name_value_literal(&self) -> Option<(Symbol, &Lit)> {
+ 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].literal()
+ {
+ return Some((ident.name, lit));
+ }
+ None
+ })
+ })
+ }
+
+ /// 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 `true` if the variant is `MetaItem`.
+ pub fn is_meta_item(&self) -> bool {
+ self.meta_item().is_some()
+ }
+
+ /// 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())
+ }
+
+ /// See [`MetaItem::name_value_literal_span`].
+ pub fn name_value_literal_span(&self) -> Option<Span> {
+ self.meta_item()?.name_value_literal_span()
+ }
+}
+
+impl Attribute {
+ #[inline]
+ pub fn has_name(&self, name: Symbol) -> bool {
+ match self.kind {
+ AttrKind::Normal(ref item, _) => item.path == name,
+ AttrKind::DocComment(..) => false,
+ }
+ }
+
+ /// For a single-segment attribute, returns its name; otherwise, returns `None`.
+ pub fn ident(&self) -> Option<Ident> {
+ match self.kind {
+ AttrKind::Normal(ref item, _) => {
+ if item.path.segments.len() == 1 {
+ Some(item.path.segments[0].ident)
+ } else {
+ None
+ }
+ }
+ AttrKind::DocComment(..) => None,
+ }
+ }
+ pub fn name_or_empty(&self) -> Symbol {
+ self.ident().unwrap_or_else(Ident::empty).name
+ }
+
+ pub fn value_str(&self) -> Option<Symbol> {
+ match self.kind {
+ AttrKind::Normal(ref item, _) => item.meta_kind().and_then(|kind| kind.value_str()),
+ AttrKind::DocComment(..) => None,
+ }
+ }
+
+ pub fn meta_item_list(&self) -> Option<Vec<NestedMetaItem>> {
+ match self.kind {
+ AttrKind::Normal(ref item, _) => match item.meta_kind() {
+ Some(MetaItemKind::List(list)) => Some(list),
+ _ => None,
+ },
+ AttrKind::DocComment(..) => None,
+ }
+ }
+
+ pub fn is_word(&self) -> bool {
+ if let AttrKind::Normal(item, _) = &self.kind {
+ matches!(item.args, MacArgs::Empty)
+ } else {
+ 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
+ }
+
+ // Example:
+ // #[attribute(name = "value")]
+ // ^^^^^^^^^^^^^^
+ pub fn name_value_literal(&self) -> Option<&Lit> {
+ match &self.kind {
+ MetaItemKind::NameValue(v) => Some(v),
+ _ => None,
+ }
+ }
+
+ pub fn value_str(&self) -> Option<Symbol> {
+ match self.kind {
+ MetaItemKind::NameValue(ref v) => match v.kind {
+ LitKind::Str(ref s, _) => Some(*s),
+ _ => None,
+ },
+ _ => None,
+ }
+ }
+
+ pub fn meta_item_list(&self) -> Option<&[NestedMetaItem]> {
+ match self.kind {
+ MetaItemKind::List(ref 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: MetaItemKind::from_mac_args(&self.args)?,
+ span,
+ })
+ }
+
+ pub fn meta_kind(&self) -> Option<MetaItemKind> {
+ MetaItemKind::from_mac_args(&self.args)
+ }
+}
+
+impl Attribute {
+ pub fn is_doc_comment(&self) -> bool {
+ match self.kind {
+ AttrKind::Normal(..) => false,
+ AttrKind::DocComment(..) => true,
+ }
+ }
+
+ pub fn doc_str_and_comment_kind(&self) -> Option<(Symbol, CommentKind)> {
+ match self.kind {
+ AttrKind::DocComment(kind, data) => Some((data, kind)),
+ AttrKind::Normal(ref item, _) if item.path == sym::doc => item
+ .meta_kind()
+ .and_then(|kind| kind.value_str())
+ .map(|data| (data, CommentKind::Line)),
+ _ => None,
+ }
+ }
+
+ pub fn doc_str(&self) -> Option<Symbol> {
+ match self.kind {
+ AttrKind::DocComment(.., data) => Some(data),
+ AttrKind::Normal(ref item, _) if item.path == sym::doc => {
+ item.meta_kind().and_then(|kind| kind.value_str())
+ }
+ _ => None,
+ }
+ }
+
+ pub fn may_have_doc_links(&self) -> bool {
+ 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(ref item, _) => item,
+ AttrKind::DocComment(..) => panic!("unexpected doc comment"),
+ }
+ }
+
+ pub fn unwrap_normal_item(self) -> AttrItem {
+ match self.kind {
+ AttrKind::Normal(item, _) => item,
+ AttrKind::DocComment(..) => panic!("unexpected doc comment"),
+ }
+ }
+
+ /// Extracts the MetaItem from inside this Attribute.
+ pub fn meta(&self) -> Option<MetaItem> {
+ match self.kind {
+ AttrKind::Normal(ref item, _) => item.meta(self.span),
+ AttrKind::DocComment(..) => None,
+ }
+ }
+
+ pub fn meta_kind(&self) -> Option<MetaItemKind> {
+ match self.kind {
+ AttrKind::Normal(ref item, _) => item.meta_kind(),
+ AttrKind::DocComment(..) => None,
+ }
+ }
+
+ pub fn tokens(&self) -> AttrAnnotatedTokenStream {
+ match self.kind {
+ AttrKind::Normal(_, ref tokens) => tokens
+ .as_ref()
+ .unwrap_or_else(|| panic!("attribute is missing tokens: {:?}", self))
+ .create_token_stream(),
+ AttrKind::DocComment(comment_kind, data) => AttrAnnotatedTokenStream::from((
+ AttrAnnotatedTokenTree::Token(Token::new(
+ token::DocComment(comment_kind, self.style, data),
+ self.span,
+ )),
+ Spacing::Alone,
+ )),
+ }
+ }
+}
+
+/* Constructors */
+
+pub fn mk_name_value_item_str(ident: Ident, str: Symbol, str_span: Span) -> MetaItem {
+ let lit_kind = LitKind::Str(str, ast::StrStyle::Cooked);
+ mk_name_value_item(ident, lit_kind, str_span)
+}
+
+pub fn mk_name_value_item(ident: Ident, lit_kind: LitKind, lit_span: Span) -> MetaItem {
+ let lit = Lit::from_lit_kind(lit_kind, lit_span);
+ let span = ident.span.to(lit_span);
+ MetaItem { path: Path::from_ident(ident), span, kind: MetaItemKind::NameValue(lit) }
+}
+
+pub fn mk_list_item(ident: Ident, items: Vec<NestedMetaItem>) -> MetaItem {
+ MetaItem { path: Path::from_ident(ident), span: ident.span, kind: MetaItemKind::List(items) }
+}
+
+pub fn mk_word_item(ident: Ident) -> MetaItem {
+ MetaItem { path: Path::from_ident(ident), span: ident.span, kind: MetaItemKind::Word }
+}
+
+pub fn mk_nested_word_item(ident: Ident) -> NestedMetaItem {
+ NestedMetaItem::MetaItem(mk_word_item(ident))
+}
+
+pub(crate) fn mk_attr_id() -> AttrId {
+ use std::sync::atomic::AtomicU32;
+ use std::sync::atomic::Ordering;
+
+ static NEXT_ATTR_ID: AtomicU32 = AtomicU32::new(0);
+
+ let id = NEXT_ATTR_ID.fetch_add(1, Ordering::SeqCst);
+ assert!(id != u32::MAX);
+ AttrId::from_u32(id)
+}
+
+pub fn mk_attr(style: AttrStyle, path: Path, args: MacArgs, span: Span) -> Attribute {
+ mk_attr_from_item(AttrItem { path, args, tokens: None }, None, style, span)
+}
+
+pub fn mk_attr_from_item(
+ item: AttrItem,
+ tokens: Option<LazyTokenStream>,
+ style: AttrStyle,
+ span: Span,
+) -> Attribute {
+ Attribute { kind: AttrKind::Normal(item, tokens), id: mk_attr_id(), style, span }
+}
+
+/// Returns an inner attribute with the given value and span.
+pub fn mk_attr_inner(item: MetaItem) -> Attribute {
+ mk_attr(AttrStyle::Inner, item.path, item.kind.mac_args(item.span), item.span)
+}
+
+/// Returns an outer attribute with the given value and span.
+pub fn mk_attr_outer(item: MetaItem) -> Attribute {
+ mk_attr(AttrStyle::Outer, item.path, item.kind.mac_args(item.span), item.span)
+}
+
+pub fn mk_doc_comment(
+ comment_kind: CommentKind,
+ style: AttrStyle,
+ data: Symbol,
+ span: Span,
+) -> Attribute {
+ Attribute { kind: AttrKind::DocComment(comment_kind, data), id: mk_attr_id(), style, span }
+}
+
+pub fn list_contains_name(items: &[NestedMetaItem], name: Symbol) -> bool {
+ items.iter().any(|item| item.has_name(name))
+}
+
+impl MetaItem {
+ fn token_trees(&self) -> Vec<TokenTree> {
+ let mut idents = vec![];
+ let mut last_pos = BytePos(0_u32);
+ for (i, segment) in self.path.segments.iter().enumerate() {
+ let is_first = i == 0;
+ if !is_first {
+ let mod_sep_span =
+ Span::new(last_pos, segment.ident.span.lo(), segment.ident.span.ctxt(), None);
+ idents.push(TokenTree::token_alone(token::ModSep, mod_sep_span));
+ }
+ idents.push(TokenTree::Token(Token::from_ast_ident(segment.ident), Spacing::Alone));
+ last_pos = segment.ident.span.hi();
+ }
+ idents.extend(self.kind.token_trees(self.span));
+ idents
+ }
+
+ fn from_tokens<I>(tokens: &mut iter::Peekable<I>) -> Option<MetaItem>
+ where
+ I: Iterator<Item = TokenTree>,
+ {
+ // FIXME: Share code with `parse_path`.
+ let path = match tokens.next().map(TokenTree::uninterpolate) {
+ Some(TokenTree::Token(
+ Token { kind: kind @ (token::Ident(..) | token::ModSep), span },
+ _,
+ )) => 'arm: {
+ let mut segments = if let token::Ident(name, _) = kind {
+ if let Some(TokenTree::Token(Token { kind: token::ModSep, .. }, _)) =
+ tokens.peek()
+ {
+ tokens.next();
+ vec![PathSegment::from_ident(Ident::new(name, span))]
+ } else {
+ break 'arm Path::from_ident(Ident::new(name, span));
+ }
+ } else {
+ vec![PathSegment::path_root(span)]
+ };
+ loop {
+ if let Some(TokenTree::Token(Token { kind: token::Ident(name, _), span }, _)) =
+ tokens.next().map(TokenTree::uninterpolate)
+ {
+ segments.push(PathSegment::from_ident(Ident::new(name, span)));
+ } else {
+ return None;
+ }
+ if let Some(TokenTree::Token(Token { kind: token::ModSep, .. }, _)) =
+ tokens.peek()
+ {
+ tokens.next();
+ } else {
+ break;
+ }
+ }
+ let span = span.with_hi(segments.last().unwrap().ident.span.hi());
+ Path { span, segments, tokens: None }
+ }
+ Some(TokenTree::Token(Token { kind: token::Interpolated(nt), .. }, _)) => match *nt {
+ token::Nonterminal::NtMeta(ref item) => return item.meta(item.path.span),
+ token::Nonterminal::NtPath(ref path) => (**path).clone(),
+ _ => return None,
+ },
+ _ => return None,
+ };
+ let list_closing_paren_pos = tokens.peek().map(|tt| tt.span().hi());
+ let kind = MetaItemKind::from_tokens(tokens)?;
+ let hi = match kind {
+ MetaItemKind::NameValue(ref lit) => lit.span.hi(),
+ MetaItemKind::List(..) => list_closing_paren_pos.unwrap_or(path.span.hi()),
+ _ => path.span.hi(),
+ };
+ let span = path.span.with_hi(hi);
+ Some(MetaItem { path, kind, span })
+ }
+}
+
+impl MetaItemKind {
+ pub fn value_str(&self) -> Option<Symbol> {
+ match self {
+ MetaItemKind::NameValue(ref v) => match v.kind {
+ LitKind::Str(ref s, _) => Some(*s),
+ _ => None,
+ },
+ _ => None,
+ }
+ }
+
+ pub fn mac_args(&self, span: Span) -> MacArgs {
+ match self {
+ MetaItemKind::Word => MacArgs::Empty,
+ MetaItemKind::NameValue(lit) => {
+ let expr = P(ast::Expr {
+ id: ast::DUMMY_NODE_ID,
+ kind: ast::ExprKind::Lit(lit.clone()),
+ span: lit.span,
+ attrs: ThinVec::new(),
+ tokens: None,
+ });
+ MacArgs::Eq(span, MacArgsEq::Ast(expr))
+ }
+ MetaItemKind::List(list) => {
+ let mut tts = Vec::new();
+ for (i, item) in list.iter().enumerate() {
+ if i > 0 {
+ tts.push(TokenTree::token_alone(token::Comma, span));
+ }
+ tts.extend(item.token_trees())
+ }
+ MacArgs::Delimited(
+ DelimSpan::from_single(span),
+ MacDelimiter::Parenthesis,
+ TokenStream::new(tts),
+ )
+ }
+ }
+ }
+
+ fn token_trees(&self, span: Span) -> Vec<TokenTree> {
+ match *self {
+ MetaItemKind::Word => vec![],
+ MetaItemKind::NameValue(ref lit) => {
+ vec![
+ TokenTree::token_alone(token::Eq, span),
+ TokenTree::Token(lit.to_token(), Spacing::Alone),
+ ]
+ }
+ MetaItemKind::List(ref list) => {
+ let mut tokens = Vec::new();
+ for (i, item) in list.iter().enumerate() {
+ if i > 0 {
+ tokens.push(TokenTree::token_alone(token::Comma, span));
+ }
+ tokens.extend(item.token_trees())
+ }
+ vec![TokenTree::Delimited(
+ DelimSpan::from_single(span),
+ Delimiter::Parenthesis,
+ TokenStream::new(tokens),
+ )]
+ }
+ }
+ }
+
+ fn list_from_tokens(tokens: TokenStream) -> Option<MetaItemKind> {
+ let mut tokens = tokens.into_trees().peekable();
+ let mut result = Vec::new();
+ while tokens.peek().is_some() {
+ let item = NestedMetaItem::from_tokens(&mut tokens)?;
+ result.push(item);
+ match tokens.next() {
+ None | Some(TokenTree::Token(Token { kind: token::Comma, .. }, _)) => {}
+ _ => return None,
+ }
+ }
+ Some(MetaItemKind::List(result))
+ }
+
+ fn name_value_from_tokens(
+ tokens: &mut impl Iterator<Item = TokenTree>,
+ ) -> Option<MetaItemKind> {
+ match tokens.next() {
+ Some(TokenTree::Delimited(_, Delimiter::Invisible, inner_tokens)) => {
+ MetaItemKind::name_value_from_tokens(&mut inner_tokens.into_trees())
+ }
+ Some(TokenTree::Token(token, _)) => {
+ Lit::from_token(&token).ok().map(MetaItemKind::NameValue)
+ }
+ _ => None,
+ }
+ }
+
+ fn from_mac_args(args: &MacArgs) -> Option<MetaItemKind> {
+ match args {
+ MacArgs::Empty => Some(MetaItemKind::Word),
+ MacArgs::Delimited(_, MacDelimiter::Parenthesis, tokens) => {
+ MetaItemKind::list_from_tokens(tokens.clone())
+ }
+ MacArgs::Delimited(..) => None,
+ MacArgs::Eq(_, MacArgsEq::Ast(expr)) => match &expr.kind {
+ ast::ExprKind::Lit(lit) => Some(MetaItemKind::NameValue(lit.clone())),
+ _ => None,
+ },
+ MacArgs::Eq(_, MacArgsEq::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 {
+ pub fn span(&self) -> Span {
+ match *self {
+ NestedMetaItem::MetaItem(ref item) => item.span,
+ NestedMetaItem::Literal(ref lit) => lit.span,
+ }
+ }
+
+ fn token_trees(&self) -> Vec<TokenTree> {
+ match *self {
+ NestedMetaItem::MetaItem(ref item) => item.token_trees(),
+ NestedMetaItem::Literal(ref lit) => {
+ vec![TokenTree::Token(lit.to_token(), Spacing::Alone)]
+ }
+ }
+ }
+
+ fn from_tokens<I>(tokens: &mut iter::Peekable<I>) -> Option<NestedMetaItem>
+ where
+ I: Iterator<Item = TokenTree>,
+ {
+ match tokens.peek() {
+ Some(TokenTree::Token(token, _))
+ if let Ok(lit) = Lit::from_token(token) =>
+ {
+ tokens.next();
+ return Some(NestedMetaItem::Literal(lit));
+ }
+ Some(TokenTree::Delimited(_, Delimiter::Invisible, inner_tokens)) => {
+ let inner_tokens = inner_tokens.clone();
+ tokens.next();
+ return NestedMetaItem::from_tokens(&mut inner_tokens.into_trees().peekable());
+ }
+ _ => {}
+ }
+ MetaItem::from_tokens(tokens).map(NestedMetaItem::MetaItem)
+ }
+}
diff --git a/compiler/rustc_ast/src/entry.rs b/compiler/rustc_ast/src/entry.rs
new file mode 100644
index 000000000..337014619
--- /dev/null
+++ b/compiler/rustc_ast/src/entry.rs
@@ -0,0 +1,8 @@
+#[derive(Debug)]
+pub enum EntryPointType {
+ None,
+ MainNamed,
+ RustcMainAttr,
+ Start,
+ OtherMain, // Not an entry point, but some other function named main
+}
diff --git a/compiler/rustc_ast/src/expand/allocator.rs b/compiler/rustc_ast/src/expand/allocator.rs
new file mode 100644
index 000000000..1976e4ad3
--- /dev/null
+++ b/compiler/rustc_ast/src/expand/allocator.rs
@@ -0,0 +1,53 @@
+use rustc_span::symbol::{sym, Symbol};
+
+#[derive(Clone, Debug, Copy, HashStable_Generic)]
+pub enum AllocatorKind {
+ Global,
+ Default,
+}
+
+impl AllocatorKind {
+ pub fn fn_name(&self, base: Symbol) -> String {
+ match *self {
+ AllocatorKind::Global => format!("__rg_{}", base),
+ AllocatorKind::Default => format!("__rdl_{}", base),
+ }
+ }
+}
+
+pub enum AllocatorTy {
+ Layout,
+ Ptr,
+ ResultPtr,
+ Unit,
+ Usize,
+}
+
+pub struct AllocatorMethod {
+ pub name: Symbol,
+ pub inputs: &'static [AllocatorTy],
+ pub output: AllocatorTy,
+}
+
+pub static ALLOCATOR_METHODS: &[AllocatorMethod] = &[
+ AllocatorMethod {
+ name: sym::alloc,
+ inputs: &[AllocatorTy::Layout],
+ output: AllocatorTy::ResultPtr,
+ },
+ AllocatorMethod {
+ name: sym::dealloc,
+ inputs: &[AllocatorTy::Ptr, AllocatorTy::Layout],
+ output: AllocatorTy::Unit,
+ },
+ AllocatorMethod {
+ name: sym::realloc,
+ inputs: &[AllocatorTy::Ptr, AllocatorTy::Layout, AllocatorTy::Usize],
+ output: AllocatorTy::ResultPtr,
+ },
+ AllocatorMethod {
+ name: sym::alloc_zeroed,
+ inputs: &[AllocatorTy::Layout],
+ output: AllocatorTy::ResultPtr,
+ },
+];
diff --git a/compiler/rustc_ast/src/expand/mod.rs b/compiler/rustc_ast/src/expand/mod.rs
new file mode 100644
index 000000000..2ee1bfe0a
--- /dev/null
+++ b/compiler/rustc_ast/src/expand/mod.rs
@@ -0,0 +1,3 @@
+//! Definitions shared by macros / syntax extensions and e.g. `rustc_middle`.
+
+pub mod allocator;
diff --git a/compiler/rustc_ast/src/lib.rs b/compiler/rustc_ast/src/lib.rs
new file mode 100644
index 000000000..4b94ec0d6
--- /dev/null
+++ b/compiler/rustc_ast/src/lib.rs
@@ -0,0 +1,63 @@
+//! The Rust Abstract Syntax Tree (AST).
+//!
+//! # Note
+//!
+//! This API is completely unstable and subject to change.
+
+#![doc(
+ html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/",
+ test(attr(deny(warnings)))
+)]
+#![feature(associated_type_bounds)]
+#![feature(box_patterns)]
+#![feature(const_default_impls)]
+#![feature(const_trait_impl)]
+#![feature(if_let_guard)]
+#![feature(label_break_value)]
+#![feature(let_chains)]
+#![feature(min_specialization)]
+#![feature(negative_impls)]
+#![feature(slice_internals)]
+#![feature(stmt_expr_attributes)]
+#![recursion_limit = "256"]
+
+#[macro_use]
+extern crate rustc_macros;
+
+pub mod util {
+ pub mod classify;
+ pub mod comments;
+ pub mod literal;
+ pub mod parser;
+ pub mod unicode;
+}
+
+pub mod ast;
+pub mod ast_traits;
+pub mod attr;
+pub mod entry;
+pub mod expand;
+pub mod mut_visit;
+pub mod node_id;
+pub mod ptr;
+pub mod token;
+pub mod tokenstream;
+pub mod visit;
+
+pub use self::ast::*;
+pub use self::ast_traits::{AstDeref, AstNodeWrapper, HasAttrs, HasNodeId, HasSpan, HasTokens};
+
+use rustc_data_structures::stable_hasher::{HashStable, StableHasher};
+
+/// Requirements for a `StableHashingContext` to be used in this crate.
+/// This is a hack to allow using the `HashStable_Generic` derive macro
+/// instead of implementing everything in `rustc_middle`.
+pub trait HashStableContext: rustc_span::HashStableContext {
+ fn hash_attr(&mut self, _: &ast::Attribute, hasher: &mut StableHasher);
+}
+
+impl<AstCtx: crate::HashStableContext> HashStable<AstCtx> for ast::Attribute {
+ fn hash_stable(&self, hcx: &mut AstCtx, hasher: &mut StableHasher) {
+ hcx.hash_attr(self, hasher)
+ }
+}
diff --git a/compiler/rustc_ast/src/mut_visit.rs b/compiler/rustc_ast/src/mut_visit.rs
new file mode 100644
index 000000000..01bd498b3
--- /dev/null
+++ b/compiler/rustc_ast/src/mut_visit.rs
@@ -0,0 +1,1601 @@
+//! A `MutVisitor` represents an AST modification; it accepts an AST piece and
+//! mutates it in place. So, for instance, macro expansion is a `MutVisitor`
+//! that walks over an AST and modifies it.
+//!
+//! Note: using a `MutVisitor` (other than the `MacroExpander` `MutVisitor`) on
+//! an AST before macro expansion is probably a bad idea. For instance,
+//! a `MutVisitor` renaming item names in a module will miss all of those
+//! that are created by the expansion of a macro.
+
+use crate::ast::*;
+use crate::ptr::P;
+use crate::token::{self, Token};
+use crate::tokenstream::*;
+
+use rustc_data_structures::map_in_place::MapInPlace;
+use rustc_data_structures::sync::Lrc;
+use rustc_data_structures::thin_vec::ThinVec;
+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};
+
+pub trait ExpectOne<A: Array> {
+ fn expect_one(self, err: &'static str) -> A::Item;
+}
+
+impl<A: Array> ExpectOne<A> for SmallVec<A> {
+ fn expect_one(self, err: &'static str) -> A::Item {
+ assert!(self.len() == 1, "{}", err);
+ self.into_iter().next().unwrap()
+ }
+}
+
+pub trait MutVisitor: Sized {
+ /// Mutable token visiting only exists for the `macro_rules` token marker and should not be
+ /// used otherwise. Token visitor would be entirely separate from the regular visitor if
+ /// the marker didn't have to visit AST fragments in nonterminal tokens.
+ const VISIT_TOKENS: bool = false;
+
+ // Methods in this trait have one of three forms:
+ //
+ // fn visit_t(&mut self, t: &mut T); // common
+ // fn flat_map_t(&mut self, t: T) -> SmallVec<[T; 1]>; // rare
+ // fn filter_map_t(&mut self, t: T) -> Option<T>; // rarest
+ //
+ // Any additions to this trait should happen in form of a call to a public
+ // `noop_*` function that only calls out to the visitor again, not other
+ // `noop_*` functions. This is a necessary API workaround to the problem of
+ // not being able to call out to the super default method in an overridden
+ // default method.
+ //
+ // When writing these methods, it is better to use destructuring like this:
+ //
+ // fn visit_abc(&mut self, ABC { a, b, c: _ }: &mut ABC) {
+ // visit_a(a);
+ // visit_b(b);
+ // }
+ //
+ // than to use field access like this:
+ //
+ // fn visit_abc(&mut self, abc: &mut ABC) {
+ // visit_a(&mut abc.a);
+ // visit_b(&mut abc.b);
+ // // ignore abc.c
+ // }
+ //
+ // As well as being more concise, the former is explicit about which fields
+ // are skipped. Furthermore, if a new field is added, the destructuring
+ // version will cause a compile error, which is good. In comparison, the
+ // field access version will continue working and it would be easy to
+ // forget to add handling for it.
+
+ fn visit_crate(&mut self, c: &mut Crate) {
+ noop_visit_crate(c, self)
+ }
+
+ fn visit_meta_list_item(&mut self, list_item: &mut NestedMetaItem) {
+ noop_visit_meta_list_item(list_item, self);
+ }
+
+ fn visit_meta_item(&mut self, meta_item: &mut MetaItem) {
+ noop_visit_meta_item(meta_item, self);
+ }
+
+ fn visit_use_tree(&mut self, use_tree: &mut UseTree) {
+ noop_visit_use_tree(use_tree, self);
+ }
+
+ fn flat_map_foreign_item(&mut self, ni: P<ForeignItem>) -> SmallVec<[P<ForeignItem>; 1]> {
+ noop_flat_map_foreign_item(ni, self)
+ }
+
+ fn flat_map_item(&mut self, i: P<Item>) -> SmallVec<[P<Item>; 1]> {
+ noop_flat_map_item(i, self)
+ }
+
+ fn visit_fn_header(&mut self, header: &mut FnHeader) {
+ noop_visit_fn_header(header, self);
+ }
+
+ fn flat_map_field_def(&mut self, fd: FieldDef) -> SmallVec<[FieldDef; 1]> {
+ noop_flat_map_field_def(fd, self)
+ }
+
+ fn visit_item_kind(&mut self, i: &mut ItemKind) {
+ noop_visit_item_kind(i, self);
+ }
+
+ fn flat_map_trait_item(&mut self, i: P<AssocItem>) -> SmallVec<[P<AssocItem>; 1]> {
+ noop_flat_map_assoc_item(i, self)
+ }
+
+ fn flat_map_impl_item(&mut self, i: P<AssocItem>) -> SmallVec<[P<AssocItem>; 1]> {
+ noop_flat_map_assoc_item(i, self)
+ }
+
+ fn visit_fn_decl(&mut self, d: &mut P<FnDecl>) {
+ noop_visit_fn_decl(d, self);
+ }
+
+ fn visit_asyncness(&mut self, a: &mut Async) {
+ noop_visit_asyncness(a, self);
+ }
+
+ fn visit_closure_binder(&mut self, b: &mut ClosureBinder) {
+ noop_visit_closure_binder(b, self);
+ }
+
+ fn visit_block(&mut self, b: &mut P<Block>) {
+ noop_visit_block(b, self);
+ }
+
+ fn flat_map_stmt(&mut self, s: Stmt) -> SmallVec<[Stmt; 1]> {
+ noop_flat_map_stmt(s, self)
+ }
+
+ fn flat_map_arm(&mut self, arm: Arm) -> SmallVec<[Arm; 1]> {
+ noop_flat_map_arm(arm, self)
+ }
+
+ fn visit_pat(&mut self, p: &mut P<Pat>) {
+ noop_visit_pat(p, self);
+ }
+
+ fn visit_anon_const(&mut self, c: &mut AnonConst) {
+ noop_visit_anon_const(c, self);
+ }
+
+ fn visit_expr(&mut self, e: &mut P<Expr>) {
+ noop_visit_expr(e, self);
+ }
+
+ fn filter_map_expr(&mut self, e: P<Expr>) -> Option<P<Expr>> {
+ noop_filter_map_expr(e, self)
+ }
+
+ fn visit_generic_arg(&mut self, arg: &mut GenericArg) {
+ noop_visit_generic_arg(arg, self);
+ }
+
+ fn visit_ty(&mut self, t: &mut P<Ty>) {
+ noop_visit_ty(t, self);
+ }
+
+ fn visit_lifetime(&mut self, l: &mut Lifetime) {
+ noop_visit_lifetime(l, self);
+ }
+
+ fn visit_constraint(&mut self, t: &mut AssocConstraint) {
+ noop_visit_constraint(t, self);
+ }
+
+ fn visit_foreign_mod(&mut self, nm: &mut ForeignMod) {
+ noop_visit_foreign_mod(nm, self);
+ }
+
+ fn flat_map_variant(&mut self, v: Variant) -> SmallVec<[Variant; 1]> {
+ noop_flat_map_variant(v, self)
+ }
+
+ fn visit_ident(&mut self, i: &mut Ident) {
+ noop_visit_ident(i, self);
+ }
+
+ fn visit_path(&mut self, p: &mut Path) {
+ noop_visit_path(p, self);
+ }
+
+ fn visit_qself(&mut self, qs: &mut Option<QSelf>) {
+ noop_visit_qself(qs, self);
+ }
+
+ fn visit_generic_args(&mut self, p: &mut GenericArgs) {
+ noop_visit_generic_args(p, self);
+ }
+
+ fn visit_angle_bracketed_parameter_data(&mut self, p: &mut AngleBracketedArgs) {
+ noop_visit_angle_bracketed_parameter_data(p, self);
+ }
+
+ fn visit_parenthesized_parameter_data(&mut self, p: &mut ParenthesizedArgs) {
+ noop_visit_parenthesized_parameter_data(p, self);
+ }
+
+ fn visit_local(&mut self, l: &mut P<Local>) {
+ noop_visit_local(l, self);
+ }
+
+ fn visit_mac_call(&mut self, mac: &mut MacCall) {
+ noop_visit_mac(mac, self);
+ }
+
+ fn visit_macro_def(&mut self, def: &mut MacroDef) {
+ noop_visit_macro_def(def, self);
+ }
+
+ fn visit_label(&mut self, label: &mut Label) {
+ noop_visit_label(label, self);
+ }
+
+ fn visit_attribute(&mut self, at: &mut Attribute) {
+ noop_visit_attribute(at, self);
+ }
+
+ fn flat_map_param(&mut self, param: Param) -> SmallVec<[Param; 1]> {
+ noop_flat_map_param(param, self)
+ }
+
+ fn visit_generics(&mut self, generics: &mut Generics) {
+ noop_visit_generics(generics, self);
+ }
+
+ fn visit_trait_ref(&mut self, tr: &mut TraitRef) {
+ noop_visit_trait_ref(tr, self);
+ }
+
+ fn visit_poly_trait_ref(&mut self, p: &mut PolyTraitRef) {
+ noop_visit_poly_trait_ref(p, self);
+ }
+
+ fn visit_variant_data(&mut self, vdata: &mut VariantData) {
+ noop_visit_variant_data(vdata, self);
+ }
+
+ fn flat_map_generic_param(&mut self, param: GenericParam) -> SmallVec<[GenericParam; 1]> {
+ noop_flat_map_generic_param(param, self)
+ }
+
+ fn visit_param_bound(&mut self, tpb: &mut GenericBound) {
+ noop_visit_param_bound(tpb, self);
+ }
+
+ fn visit_mt(&mut self, mt: &mut MutTy) {
+ noop_visit_mt(mt, self);
+ }
+
+ fn flat_map_expr_field(&mut self, f: ExprField) -> SmallVec<[ExprField; 1]> {
+ noop_flat_map_expr_field(f, self)
+ }
+
+ fn visit_where_clause(&mut self, where_clause: &mut WhereClause) {
+ noop_visit_where_clause(where_clause, self);
+ }
+
+ fn visit_where_predicate(&mut self, where_predicate: &mut WherePredicate) {
+ noop_visit_where_predicate(where_predicate, self);
+ }
+
+ fn visit_vis(&mut self, vis: &mut Visibility) {
+ noop_visit_vis(vis, self);
+ }
+
+ fn visit_id(&mut self, _id: &mut NodeId) {
+ // Do nothing.
+ }
+
+ fn visit_span(&mut self, _sp: &mut Span) {
+ // Do nothing.
+ }
+
+ fn flat_map_pat_field(&mut self, fp: PatField) -> SmallVec<[PatField; 1]> {
+ noop_flat_map_pat_field(fp, self)
+ }
+
+ fn visit_inline_asm(&mut self, asm: &mut InlineAsm) {
+ noop_visit_inline_asm(asm, self)
+ }
+
+ fn visit_inline_asm_sym(&mut self, sym: &mut InlineAsmSym) {
+ noop_visit_inline_asm_sym(sym, self)
+ }
+}
+
+/// Use a map-style function (`FnOnce(T) -> T`) to overwrite a `&mut T`. Useful
+/// when using a `flat_map_*` or `filter_map_*` method within a `visit_`
+/// method.
+//
+// No `noop_` prefix because there isn't a corresponding method in `MutVisitor`.
+pub fn visit_clobber<T: DummyAstNode>(t: &mut T, f: impl FnOnce(T) -> T) {
+ unsafe {
+ // Safe because `t` is used in a read-only fashion by `read()` before
+ // being overwritten by `write()`.
+ let old_t = ptr::read(t);
+ let new_t =
+ panic::catch_unwind(panic::AssertUnwindSafe(|| f(old_t))).unwrap_or_else(|err| {
+ // Set `t` to some valid but possible meaningless value,
+ // and pass the fatal error further.
+ ptr::write(t, T::dummy());
+ panic::resume_unwind(err);
+ });
+ ptr::write(t, new_t);
+ }
+}
+
+// No `noop_` prefix because there isn't a corresponding method in `MutVisitor`.
+#[inline]
+pub fn visit_vec<T, F>(elems: &mut Vec<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),
+{
+ if let Some(elem) = opt {
+ visit_elem(elem);
+ }
+}
+
+// No `noop_` prefix because there isn't a corresponding method in `MutVisitor`.
+pub fn visit_attrs<T: MutVisitor>(attrs: &mut Vec<Attribute>, vis: &mut T) {
+ visit_vec(attrs, |attr| vis.visit_attribute(attr));
+}
+
+// No `noop_` prefix because there isn't a corresponding method in `MutVisitor`.
+pub fn visit_thin_attrs<T: MutVisitor>(attrs: &mut AttrVec, vis: &mut T) {
+ for attr in attrs.iter_mut() {
+ vis.visit_attribute(attr);
+ }
+}
+
+// No `noop_` prefix because there isn't a corresponding method in `MutVisitor`.
+pub fn visit_exprs<T: MutVisitor>(exprs: &mut Vec<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));
+}
+
+// No `noop_` prefix because there isn't a corresponding method in `MutVisitor`.
+pub fn visit_fn_sig<T: MutVisitor>(FnSig { header, decl, span }: &mut FnSig, vis: &mut T) {
+ vis.visit_fn_header(header);
+ vis.visit_fn_decl(decl);
+ vis.visit_span(span);
+}
+
+// No `noop_` prefix because there isn't a corresponding method in `MutVisitor`.
+pub fn visit_mac_args<T: MutVisitor>(args: &mut MacArgs, vis: &mut T) {
+ match args {
+ MacArgs::Empty => {}
+ MacArgs::Delimited(dspan, _delim, tokens) => {
+ visit_delim_span(dspan, vis);
+ visit_tts(tokens, vis);
+ }
+ MacArgs::Eq(eq_span, MacArgsEq::Ast(expr)) => {
+ vis.visit_span(eq_span);
+ vis.visit_expr(expr);
+ }
+ MacArgs::Eq(_, MacArgsEq::Hir(lit)) => {
+ unreachable!("in literal form when visiting mac args eq: {:?}", lit)
+ }
+ }
+}
+
+pub fn visit_delim_span<T: MutVisitor>(dspan: &mut DelimSpan, vis: &mut T) {
+ vis.visit_span(&mut dspan.open);
+ vis.visit_span(&mut dspan.close);
+}
+
+pub fn noop_flat_map_pat_field<T: MutVisitor>(
+ mut fp: PatField,
+ vis: &mut T,
+) -> SmallVec<[PatField; 1]> {
+ let PatField { attrs, id, ident, is_placeholder: _, is_shorthand: _, pat, span } = &mut fp;
+ vis.visit_id(id);
+ vis.visit_ident(ident);
+ vis.visit_pat(pat);
+ vis.visit_span(span);
+ visit_thin_attrs(attrs, vis);
+ smallvec![fp]
+}
+
+pub fn noop_visit_use_tree<T: MutVisitor>(use_tree: &mut UseTree, vis: &mut T) {
+ let UseTree { prefix, kind, span } = use_tree;
+ vis.visit_path(prefix);
+ match kind {
+ UseTreeKind::Simple(rename, id1, id2) => {
+ visit_opt(rename, |rename| vis.visit_ident(rename));
+ vis.visit_id(id1);
+ vis.visit_id(id2);
+ }
+ UseTreeKind::Nested(items) => {
+ for (tree, id) in items {
+ vis.visit_use_tree(tree);
+ vis.visit_id(id);
+ }
+ }
+ UseTreeKind::Glob => {}
+ }
+ vis.visit_span(span);
+}
+
+pub fn noop_flat_map_arm<T: MutVisitor>(mut arm: Arm, vis: &mut T) -> SmallVec<[Arm; 1]> {
+ let Arm { attrs, pat, guard, body, span, id, is_placeholder: _ } = &mut arm;
+ visit_thin_attrs(attrs, vis);
+ vis.visit_id(id);
+ vis.visit_pat(pat);
+ visit_opt(guard, |guard| vis.visit_expr(guard));
+ vis.visit_expr(body);
+ vis.visit_span(span);
+ smallvec![arm]
+}
+
+pub fn noop_visit_constraint<T: MutVisitor>(
+ AssocConstraint { id, ident, gen_args, kind, span }: &mut AssocConstraint,
+ vis: &mut T,
+) {
+ vis.visit_id(id);
+ vis.visit_ident(ident);
+ if let Some(ref mut gen_args) = gen_args {
+ vis.visit_generic_args(gen_args);
+ }
+ match kind {
+ AssocConstraintKind::Equality { ref mut term } => match term {
+ Term::Ty(ty) => vis.visit_ty(ty),
+ Term::Const(c) => vis.visit_anon_const(c),
+ },
+ AssocConstraintKind::Bound { ref mut bounds } => visit_bounds(bounds, vis),
+ }
+ vis.visit_span(span);
+}
+
+pub fn noop_visit_ty<T: MutVisitor>(ty: &mut P<Ty>, vis: &mut T) {
+ let Ty { id, kind, span, tokens } = ty.deref_mut();
+ vis.visit_id(id);
+ match kind {
+ TyKind::Infer | TyKind::ImplicitSelf | TyKind::Err | TyKind::Never | TyKind::CVarArgs => {}
+ TyKind::Slice(ty) => vis.visit_ty(ty),
+ TyKind::Ptr(mt) => vis.visit_mt(mt),
+ TyKind::Rptr(lt, mt) => {
+ visit_opt(lt, |lt| noop_visit_lifetime(lt, vis));
+ vis.visit_mt(mt);
+ }
+ TyKind::BareFn(bft) => {
+ let BareFnTy { unsafety, ext: _, generic_params, decl, decl_span } = bft.deref_mut();
+ visit_unsafety(unsafety, vis);
+ generic_params.flat_map_in_place(|param| vis.flat_map_generic_param(param));
+ vis.visit_fn_decl(decl);
+ vis.visit_span(decl_span);
+ }
+ TyKind::Tup(tys) => visit_vec(tys, |ty| vis.visit_ty(ty)),
+ TyKind::Paren(ty) => vis.visit_ty(ty),
+ TyKind::Path(qself, path) => {
+ vis.visit_qself(qself);
+ vis.visit_path(path);
+ }
+ TyKind::Array(ty, length) => {
+ vis.visit_ty(ty);
+ vis.visit_anon_const(length);
+ }
+ TyKind::Typeof(expr) => vis.visit_anon_const(expr),
+ TyKind::TraitObject(bounds, _syntax) => {
+ visit_vec(bounds, |bound| vis.visit_param_bound(bound))
+ }
+ TyKind::ImplTrait(id, bounds) => {
+ vis.visit_id(id);
+ visit_vec(bounds, |bound| vis.visit_param_bound(bound));
+ }
+ TyKind::MacCall(mac) => vis.visit_mac_call(mac),
+ }
+ vis.visit_span(span);
+ visit_lazy_tts(tokens, vis);
+}
+
+pub fn noop_visit_foreign_mod<T: MutVisitor>(foreign_mod: &mut ForeignMod, vis: &mut T) {
+ let ForeignMod { unsafety, abi: _, items } = foreign_mod;
+ visit_unsafety(unsafety, vis);
+ items.flat_map_in_place(|item| vis.flat_map_foreign_item(item));
+}
+
+pub fn noop_flat_map_variant<T: MutVisitor>(
+ mut variant: Variant,
+ visitor: &mut T,
+) -> SmallVec<[Variant; 1]> {
+ let Variant { ident, vis, attrs, id, data, disr_expr, span, is_placeholder: _ } = &mut variant;
+ visitor.visit_ident(ident);
+ visitor.visit_vis(vis);
+ visit_thin_attrs(attrs, visitor);
+ visitor.visit_id(id);
+ visitor.visit_variant_data(data);
+ visit_opt(disr_expr, |disr_expr| visitor.visit_anon_const(disr_expr));
+ visitor.visit_span(span);
+ smallvec![variant]
+}
+
+pub fn noop_visit_ident<T: MutVisitor>(Ident { name: _, span }: &mut Ident, vis: &mut T) {
+ vis.visit_span(span);
+}
+
+pub fn noop_visit_path<T: MutVisitor>(Path { segments, span, tokens }: &mut Path, vis: &mut T) {
+ vis.visit_span(span);
+ for PathSegment { ident, id, args } in segments {
+ vis.visit_ident(ident);
+ vis.visit_id(id);
+ visit_opt(args, |args| vis.visit_generic_args(args));
+ }
+ visit_lazy_tts(tokens, vis);
+}
+
+pub fn noop_visit_qself<T: MutVisitor>(qself: &mut Option<QSelf>, vis: &mut T) {
+ visit_opt(qself, |QSelf { ty, path_span, position: _ }| {
+ vis.visit_ty(ty);
+ vis.visit_span(path_span);
+ })
+}
+
+pub fn noop_visit_generic_args<T: MutVisitor>(generic_args: &mut GenericArgs, vis: &mut T) {
+ match generic_args {
+ GenericArgs::AngleBracketed(data) => vis.visit_angle_bracketed_parameter_data(data),
+ GenericArgs::Parenthesized(data) => vis.visit_parenthesized_parameter_data(data),
+ }
+}
+
+pub fn noop_visit_generic_arg<T: MutVisitor>(arg: &mut GenericArg, vis: &mut T) {
+ match arg {
+ GenericArg::Lifetime(lt) => vis.visit_lifetime(lt),
+ GenericArg::Type(ty) => vis.visit_ty(ty),
+ GenericArg::Const(ct) => vis.visit_anon_const(ct),
+ }
+}
+
+pub fn noop_visit_angle_bracketed_parameter_data<T: MutVisitor>(
+ data: &mut AngleBracketedArgs,
+ vis: &mut T,
+) {
+ let AngleBracketedArgs { args, span } = data;
+ visit_vec(args, |arg| match arg {
+ AngleBracketedArg::Arg(arg) => vis.visit_generic_arg(arg),
+ AngleBracketedArg::Constraint(constraint) => vis.visit_constraint(constraint),
+ });
+ vis.visit_span(span);
+}
+
+pub fn noop_visit_parenthesized_parameter_data<T: MutVisitor>(
+ args: &mut ParenthesizedArgs,
+ vis: &mut T,
+) {
+ let ParenthesizedArgs { inputs, output, span, .. } = args;
+ visit_vec(inputs, |input| vis.visit_ty(input));
+ noop_visit_fn_ret_ty(output, vis);
+ vis.visit_span(span);
+}
+
+pub fn noop_visit_local<T: MutVisitor>(local: &mut P<Local>, vis: &mut T) {
+ let Local { id, pat, ty, kind, span, attrs, tokens } = local.deref_mut();
+ vis.visit_id(id);
+ vis.visit_pat(pat);
+ visit_opt(ty, |ty| vis.visit_ty(ty));
+ match kind {
+ LocalKind::Decl => {}
+ LocalKind::Init(init) => {
+ vis.visit_expr(init);
+ }
+ LocalKind::InitElse(init, els) => {
+ vis.visit_expr(init);
+ vis.visit_block(els);
+ }
+ }
+ vis.visit_span(span);
+ visit_thin_attrs(attrs, vis);
+ visit_lazy_tts(tokens, vis);
+}
+
+pub fn noop_visit_attribute<T: MutVisitor>(attr: &mut Attribute, vis: &mut T) {
+ let Attribute { kind, id: _, style: _, span } = attr;
+ match kind {
+ AttrKind::Normal(AttrItem { path, args, tokens }, attr_tokens) => {
+ vis.visit_path(path);
+ visit_mac_args(args, vis);
+ visit_lazy_tts(tokens, vis);
+ visit_lazy_tts(attr_tokens, vis);
+ }
+ AttrKind::DocComment(..) => {}
+ }
+ vis.visit_span(span);
+}
+
+pub fn noop_visit_mac<T: MutVisitor>(mac: &mut MacCall, vis: &mut T) {
+ let MacCall { path, args, prior_type_ascription: _ } = mac;
+ vis.visit_path(path);
+ visit_mac_args(args, vis);
+}
+
+pub fn noop_visit_macro_def<T: MutVisitor>(macro_def: &mut MacroDef, vis: &mut T) {
+ let MacroDef { body, macro_rules: _ } = macro_def;
+ visit_mac_args(body, vis);
+}
+
+pub fn noop_visit_meta_list_item<T: MutVisitor>(li: &mut NestedMetaItem, vis: &mut T) {
+ match li {
+ NestedMetaItem::MetaItem(mi) => vis.visit_meta_item(mi),
+ NestedMetaItem::Literal(_lit) => {}
+ }
+}
+
+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::NameValue(_s) => {}
+ }
+ vis.visit_span(span);
+}
+
+pub fn noop_flat_map_param<T: MutVisitor>(mut param: Param, vis: &mut T) -> SmallVec<[Param; 1]> {
+ let Param { attrs, id, pat, span, ty, is_placeholder: _ } = &mut param;
+ vis.visit_id(id);
+ visit_thin_attrs(attrs, vis);
+ vis.visit_pat(pat);
+ vis.visit_span(span);
+ vis.visit_ty(ty);
+ smallvec![param]
+}
+
+// No `noop_` prefix because there isn't a corresponding method in `MutVisitor`.
+pub fn visit_attr_annotated_tt<T: MutVisitor>(tt: &mut AttrAnnotatedTokenTree, vis: &mut T) {
+ match tt {
+ AttrAnnotatedTokenTree::Token(token) => {
+ visit_token(token, vis);
+ }
+ AttrAnnotatedTokenTree::Delimited(DelimSpan { open, close }, _delim, tts) => {
+ vis.visit_span(open);
+ vis.visit_span(close);
+ visit_attr_annotated_tts(tts, vis);
+ }
+ AttrAnnotatedTokenTree::Attributes(data) => {
+ for attr in &mut *data.attrs {
+ match &mut attr.kind {
+ AttrKind::Normal(_, attr_tokens) => {
+ visit_lazy_tts(attr_tokens, vis);
+ }
+ AttrKind::DocComment(..) => {
+ vis.visit_span(&mut attr.span);
+ }
+ }
+ }
+ visit_lazy_tts_opt_mut(Some(&mut data.tokens), vis);
+ }
+ }
+}
+
+// No `noop_` prefix because there isn't a corresponding method in `MutVisitor`.
+pub fn visit_tt<T: MutVisitor>(tt: &mut TokenTree, vis: &mut T) {
+ match tt {
+ TokenTree::Token(token, _) => {
+ visit_token(token, vis);
+ }
+ TokenTree::Delimited(DelimSpan { open, close }, _delim, tts) => {
+ vis.visit_span(open);
+ vis.visit_span(close);
+ visit_tts(tts, vis);
+ }
+ }
+}
+
+// No `noop_` prefix because there isn't a corresponding method in `MutVisitor`.
+pub fn visit_tts<T: MutVisitor>(TokenStream(tts): &mut TokenStream, vis: &mut T) {
+ if T::VISIT_TOKENS && !tts.is_empty() {
+ let tts = Lrc::make_mut(tts);
+ visit_vec(tts, |tree| visit_tt(tree, vis));
+ }
+}
+
+pub fn visit_attr_annotated_tts<T: MutVisitor>(
+ AttrAnnotatedTokenStream(tts): &mut AttrAnnotatedTokenStream,
+ vis: &mut T,
+) {
+ if T::VISIT_TOKENS && !tts.is_empty() {
+ let tts = Lrc::make_mut(tts);
+ visit_vec(tts, |(tree, _is_joint)| visit_attr_annotated_tt(tree, vis));
+ }
+}
+
+pub fn visit_lazy_tts_opt_mut<T: MutVisitor>(lazy_tts: Option<&mut LazyTokenStream>, vis: &mut T) {
+ if T::VISIT_TOKENS {
+ if let Some(lazy_tts) = lazy_tts {
+ let mut tts = lazy_tts.create_token_stream();
+ visit_attr_annotated_tts(&mut tts, vis);
+ *lazy_tts = LazyTokenStream::new(tts);
+ }
+ }
+}
+
+pub fn visit_lazy_tts<T: MutVisitor>(lazy_tts: &mut Option<LazyTokenStream>, vis: &mut T) {
+ visit_lazy_tts_opt_mut(lazy_tts.as_mut(), vis);
+}
+
+// No `noop_` prefix because there isn't a corresponding method in `MutVisitor`.
+// Applies ident visitor if it's an ident; applies other visits to interpolated nodes.
+// In practice the ident part is not actually used by specific visitors right now,
+// but there's a test below checking that it works.
+pub fn visit_token<T: MutVisitor>(t: &mut Token, vis: &mut T) {
+ let Token { kind, span } = t;
+ match kind {
+ token::Ident(name, _) | token::Lifetime(name) => {
+ let mut ident = Ident::new(*name, *span);
+ vis.visit_ident(&mut ident);
+ *name = ident.name;
+ *span = ident.span;
+ return; // Avoid visiting the span for the second time.
+ }
+ token::Interpolated(nt) => {
+ let mut nt = Lrc::make_mut(nt);
+ visit_nonterminal(&mut nt, vis);
+ }
+ _ => {}
+ }
+ vis.visit_span(span);
+}
+
+// No `noop_` prefix because there isn't a corresponding method in `MutVisitor`.
+/// Applies the visitor to elements of interpolated nodes.
+//
+// N.B., this can occur only when applying a visitor to partially expanded
+// code, where parsed pieces have gotten implanted ito *other* macro
+// invocations. This is relevant for macro hygiene, but possibly not elsewhere.
+//
+// One problem here occurs because the types for flat_map_item, flat_map_stmt,
+// etc., allow the visitor to return *multiple* items; this is a problem for the
+// nodes here, because they insist on having exactly one piece. One solution
+// would be to mangle the MutVisitor trait to include one-to-many and
+// one-to-one versions of these entry points, but that would probably confuse a
+// lot of people and help very few. Instead, I'm just going to put in dynamic
+// checks. I think the performance impact of this will be pretty much
+// nonexistent. The danger is that someone will apply a `MutVisitor` to a
+// partially expanded node, and will be confused by the fact that their
+// `flat_map_item` or `flat_map_stmt` isn't getting called on `NtItem` or `NtStmt`
+// nodes. Hopefully they'll wind up reading this comment, and doing something
+// appropriate.
+//
+// BTW, design choice: I considered just changing the type of, e.g., `NtItem` to
+// contain multiple items, but decided against it when I looked at
+// `parse_item_or_view_item` and tried to figure out what I would do with
+// multiple items there....
+pub fn visit_nonterminal<T: MutVisitor>(nt: &mut token::Nonterminal, vis: &mut T) {
+ match nt {
+ token::NtItem(item) => visit_clobber(item, |item| {
+ // This is probably okay, because the only visitors likely to
+ // peek inside interpolated nodes will be renamings/markings,
+ // which map single items to single items.
+ vis.flat_map_item(item).expect_one("expected visitor to produce exactly one item")
+ }),
+ token::NtBlock(block) => vis.visit_block(block),
+ token::NtStmt(stmt) => visit_clobber(stmt, |stmt| {
+ // See reasoning above.
+ stmt.map(|stmt| {
+ vis.flat_map_stmt(stmt).expect_one("expected visitor to produce exactly one item")
+ })
+ }),
+ token::NtPat(pat) => vis.visit_pat(pat),
+ token::NtExpr(expr) => vis.visit_expr(expr),
+ token::NtTy(ty) => vis.visit_ty(ty),
+ token::NtIdent(ident, _is_raw) => vis.visit_ident(ident),
+ token::NtLifetime(ident) => vis.visit_ident(ident),
+ token::NtLiteral(expr) => vis.visit_expr(expr),
+ token::NtMeta(item) => {
+ let AttrItem { path, args, tokens } = item.deref_mut();
+ vis.visit_path(path);
+ visit_mac_args(args, vis);
+ visit_lazy_tts(tokens, vis);
+ }
+ token::NtPath(path) => vis.visit_path(path),
+ token::NtVis(visib) => vis.visit_vis(visib),
+ }
+}
+
+// No `noop_` prefix because there isn't a corresponding method in `MutVisitor`.
+pub fn visit_defaultness<T: MutVisitor>(defaultness: &mut Defaultness, vis: &mut T) {
+ match defaultness {
+ Defaultness::Default(span) => vis.visit_span(span),
+ Defaultness::Final => {}
+ }
+}
+
+// No `noop_` prefix because there isn't a corresponding method in `MutVisitor`.
+pub fn visit_unsafety<T: MutVisitor>(unsafety: &mut Unsafe, vis: &mut T) {
+ match unsafety {
+ Unsafe::Yes(span) => vis.visit_span(span),
+ Unsafe::No => {}
+ }
+}
+
+// No `noop_` prefix because there isn't a corresponding method in `MutVisitor`.
+pub fn visit_polarity<T: MutVisitor>(polarity: &mut ImplPolarity, vis: &mut T) {
+ match polarity {
+ ImplPolarity::Positive => {}
+ ImplPolarity::Negative(span) => vis.visit_span(span),
+ }
+}
+
+// No `noop_` prefix because there isn't a corresponding method in `MutVisitor`.
+pub fn visit_constness<T: MutVisitor>(constness: &mut Const, vis: &mut T) {
+ match constness {
+ Const::Yes(span) => vis.visit_span(span),
+ Const::No => {}
+ }
+}
+
+pub fn noop_visit_closure_binder<T: MutVisitor>(binder: &mut ClosureBinder, vis: &mut T) {
+ 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);
+ }
+ }
+}
+
+pub fn noop_visit_asyncness<T: MutVisitor>(asyncness: &mut Async, vis: &mut T) {
+ match asyncness {
+ Async::Yes { span: _, closure_id, return_impl_trait_id } => {
+ vis.visit_id(closure_id);
+ vis.visit_id(return_impl_trait_id);
+ }
+ Async::No => {}
+ }
+}
+
+pub fn noop_visit_fn_decl<T: MutVisitor>(decl: &mut P<FnDecl>, vis: &mut T) {
+ let FnDecl { inputs, output } = decl.deref_mut();
+ inputs.flat_map_in_place(|param| vis.flat_map_param(param));
+ noop_visit_fn_ret_ty(output, vis);
+}
+
+pub fn noop_visit_fn_ret_ty<T: MutVisitor>(fn_ret_ty: &mut FnRetTy, vis: &mut T) {
+ match fn_ret_ty {
+ FnRetTy::Default(span) => vis.visit_span(span),
+ FnRetTy::Ty(ty) => vis.visit_ty(ty),
+ }
+}
+
+pub fn noop_visit_param_bound<T: MutVisitor>(pb: &mut GenericBound, vis: &mut T) {
+ match pb {
+ GenericBound::Trait(ty, _modifier) => vis.visit_poly_trait_ref(ty),
+ GenericBound::Outlives(lifetime) => noop_visit_lifetime(lifetime, vis),
+ }
+}
+
+pub fn noop_flat_map_generic_param<T: MutVisitor>(
+ mut param: GenericParam,
+ vis: &mut T,
+) -> SmallVec<[GenericParam; 1]> {
+ let GenericParam { id, ident, attrs, bounds, kind, colon_span, is_placeholder: _ } = &mut param;
+ vis.visit_id(id);
+ vis.visit_ident(ident);
+ if let Some(ref mut colon_span) = colon_span {
+ vis.visit_span(colon_span);
+ }
+ visit_thin_attrs(attrs, vis);
+ visit_vec(bounds, |bound| noop_visit_param_bound(bound, vis));
+ match kind {
+ GenericParamKind::Lifetime => {}
+ GenericParamKind::Type { default } => {
+ visit_opt(default, |default| vis.visit_ty(default));
+ }
+ GenericParamKind::Const { ty, kw_span: _, default } => {
+ vis.visit_ty(ty);
+ visit_opt(default, |default| vis.visit_anon_const(default));
+ }
+ }
+ smallvec![param]
+}
+
+pub fn noop_visit_label<T: MutVisitor>(Label { ident }: &mut Label, vis: &mut T) {
+ vis.visit_ident(ident);
+}
+
+fn noop_visit_lifetime<T: MutVisitor>(Lifetime { id, ident }: &mut Lifetime, vis: &mut T) {
+ vis.visit_id(id);
+ vis.visit_ident(ident);
+}
+
+pub fn noop_visit_generics<T: MutVisitor>(generics: &mut Generics, vis: &mut T) {
+ let Generics { params, where_clause, span } = generics;
+ params.flat_map_in_place(|param| vis.flat_map_generic_param(param));
+ vis.visit_where_clause(where_clause);
+ vis.visit_span(span);
+}
+
+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));
+ vis.visit_span(span);
+}
+
+pub fn noop_visit_where_predicate<T: MutVisitor>(pred: &mut WherePredicate, vis: &mut T) {
+ match pred {
+ WherePredicate::BoundPredicate(bp) => {
+ let WhereBoundPredicate { span, bound_generic_params, bounded_ty, bounds } = bp;
+ vis.visit_span(span);
+ bound_generic_params.flat_map_in_place(|param| vis.flat_map_generic_param(param));
+ vis.visit_ty(bounded_ty);
+ visit_vec(bounds, |bound| vis.visit_param_bound(bound));
+ }
+ WherePredicate::RegionPredicate(rp) => {
+ let WhereRegionPredicate { span, lifetime, bounds } = rp;
+ vis.visit_span(span);
+ noop_visit_lifetime(lifetime, vis);
+ visit_vec(bounds, |bound| noop_visit_param_bound(bound, vis));
+ }
+ WherePredicate::EqPredicate(ep) => {
+ let WhereEqPredicate { id, span, lhs_ty, rhs_ty } = ep;
+ vis.visit_id(id);
+ vis.visit_span(span);
+ vis.visit_ty(lhs_ty);
+ vis.visit_ty(rhs_ty);
+ }
+ }
+}
+
+pub fn noop_visit_variant_data<T: MutVisitor>(vdata: &mut VariantData, vis: &mut T) {
+ match vdata {
+ VariantData::Struct(fields, ..) => {
+ fields.flat_map_in_place(|field| vis.flat_map_field_def(field));
+ }
+ VariantData::Tuple(fields, id) => {
+ fields.flat_map_in_place(|field| vis.flat_map_field_def(field));
+ vis.visit_id(id);
+ }
+ VariantData::Unit(id) => vis.visit_id(id),
+ }
+}
+
+pub fn noop_visit_trait_ref<T: MutVisitor>(TraitRef { path, ref_id }: &mut TraitRef, vis: &mut T) {
+ vis.visit_path(path);
+ vis.visit_id(ref_id);
+}
+
+pub fn noop_visit_poly_trait_ref<T: MutVisitor>(p: &mut PolyTraitRef, vis: &mut T) {
+ let PolyTraitRef { bound_generic_params, trait_ref, span } = p;
+ bound_generic_params.flat_map_in_place(|param| vis.flat_map_generic_param(param));
+ vis.visit_trait_ref(trait_ref);
+ vis.visit_span(span);
+}
+
+pub fn noop_flat_map_field_def<T: MutVisitor>(
+ mut fd: FieldDef,
+ visitor: &mut T,
+) -> SmallVec<[FieldDef; 1]> {
+ let FieldDef { span, ident, vis, id, ty, attrs, is_placeholder: _ } = &mut fd;
+ visitor.visit_span(span);
+ visit_opt(ident, |ident| visitor.visit_ident(ident));
+ visitor.visit_vis(vis);
+ visitor.visit_id(id);
+ visitor.visit_ty(ty);
+ visit_thin_attrs(attrs, visitor);
+ smallvec![fd]
+}
+
+pub fn noop_flat_map_expr_field<T: MutVisitor>(
+ mut f: ExprField,
+ vis: &mut T,
+) -> SmallVec<[ExprField; 1]> {
+ let ExprField { ident, expr, span, is_shorthand: _, attrs, id, is_placeholder: _ } = &mut f;
+ vis.visit_ident(ident);
+ vis.visit_expr(expr);
+ vis.visit_id(id);
+ vis.visit_span(span);
+ visit_thin_attrs(attrs, vis);
+ smallvec![f]
+}
+
+pub fn noop_visit_mt<T: MutVisitor>(MutTy { ty, mutbl: _ }: &mut MutTy, vis: &mut T) {
+ vis.visit_ty(ty);
+}
+
+pub fn noop_visit_block<T: MutVisitor>(block: &mut P<Block>, vis: &mut T) {
+ let Block { id, stmts, rules: _, span, tokens, could_be_bare_literal: _ } = block.deref_mut();
+ vis.visit_id(id);
+ stmts.flat_map_in_place(|stmt| vis.flat_map_stmt(stmt));
+ vis.visit_span(span);
+ visit_lazy_tts(tokens, vis);
+}
+
+pub fn noop_visit_item_kind<T: MutVisitor>(kind: &mut ItemKind, vis: &mut T) {
+ match kind {
+ ItemKind::ExternCrate(_orig_name) => {}
+ ItemKind::Use(use_tree) => vis.visit_use_tree(use_tree),
+ ItemKind::Static(ty, _, expr) => {
+ vis.visit_ty(ty);
+ visit_opt(expr, |expr| vis.visit_expr(expr));
+ }
+ ItemKind::Const(defaultness, ty, expr) => {
+ visit_defaultness(defaultness, vis);
+ vis.visit_ty(ty);
+ visit_opt(expr, |expr| vis.visit_expr(expr));
+ }
+ ItemKind::Fn(box Fn { defaultness, generics, sig, body }) => {
+ visit_defaultness(defaultness, vis);
+ visit_fn_sig(sig, vis);
+ vis.visit_generics(generics);
+ visit_opt(body, |body| vis.visit_block(body));
+ }
+ ItemKind::Mod(unsafety, mod_kind) => {
+ visit_unsafety(unsafety, vis);
+ match mod_kind {
+ ModKind::Loaded(items, _inline, ModSpans { inner_span, inject_use_span }) => {
+ vis.visit_span(inner_span);
+ vis.visit_span(inject_use_span);
+ items.flat_map_in_place(|item| vis.flat_map_item(item));
+ }
+ ModKind::Unloaded => {}
+ }
+ }
+ ItemKind::ForeignMod(nm) => vis.visit_foreign_mod(nm),
+ ItemKind::GlobalAsm(asm) => vis.visit_inline_asm(asm),
+ ItemKind::TyAlias(box TyAlias {
+ defaultness, generics, where_clauses, bounds, ty, ..
+ }) => {
+ visit_defaultness(defaultness, vis);
+ vis.visit_generics(generics);
+ vis.visit_span(&mut where_clauses.0.1);
+ vis.visit_span(&mut where_clauses.1.1);
+ visit_bounds(bounds, vis);
+ visit_opt(ty, |ty| vis.visit_ty(ty));
+ }
+ ItemKind::Enum(EnumDef { variants }, generics) => {
+ variants.flat_map_in_place(|variant| vis.flat_map_variant(variant));
+ vis.visit_generics(generics);
+ }
+ ItemKind::Struct(variant_data, generics) | ItemKind::Union(variant_data, generics) => {
+ vis.visit_variant_data(variant_data);
+ vis.visit_generics(generics);
+ }
+ ItemKind::Impl(box Impl {
+ defaultness,
+ unsafety,
+ generics,
+ constness,
+ polarity,
+ of_trait,
+ self_ty,
+ items,
+ }) => {
+ visit_defaultness(defaultness, vis);
+ visit_unsafety(unsafety, vis);
+ vis.visit_generics(generics);
+ visit_constness(constness, vis);
+ visit_polarity(polarity, vis);
+ visit_opt(of_trait, |trait_ref| vis.visit_trait_ref(trait_ref));
+ vis.visit_ty(self_ty);
+ items.flat_map_in_place(|item| vis.flat_map_impl_item(item));
+ }
+ ItemKind::Trait(box Trait { unsafety, is_auto: _, generics, bounds, items }) => {
+ visit_unsafety(unsafety, vis);
+ vis.visit_generics(generics);
+ visit_bounds(bounds, vis);
+ items.flat_map_in_place(|item| vis.flat_map_trait_item(item));
+ }
+ ItemKind::TraitAlias(generics, bounds) => {
+ vis.visit_generics(generics);
+ visit_bounds(bounds, vis);
+ }
+ ItemKind::MacCall(m) => vis.visit_mac_call(m),
+ ItemKind::MacroDef(def) => vis.visit_macro_def(def),
+ }
+}
+
+pub fn noop_flat_map_assoc_item<T: MutVisitor>(
+ mut item: P<AssocItem>,
+ visitor: &mut T,
+) -> SmallVec<[P<AssocItem>; 1]> {
+ let Item { id, ident, vis, attrs, kind, span, tokens } = item.deref_mut();
+ visitor.visit_id(id);
+ visitor.visit_ident(ident);
+ visitor.visit_vis(vis);
+ visit_attrs(attrs, visitor);
+ match kind {
+ AssocItemKind::Const(defaultness, ty, expr) => {
+ visit_defaultness(defaultness, visitor);
+ visitor.visit_ty(ty);
+ visit_opt(expr, |expr| visitor.visit_expr(expr));
+ }
+ AssocItemKind::Fn(box Fn { defaultness, generics, sig, body }) => {
+ visit_defaultness(defaultness, visitor);
+ visitor.visit_generics(generics);
+ visit_fn_sig(sig, visitor);
+ visit_opt(body, |body| visitor.visit_block(body));
+ }
+ AssocItemKind::TyAlias(box TyAlias {
+ defaultness,
+ generics,
+ where_clauses,
+ bounds,
+ ty,
+ ..
+ }) => {
+ visit_defaultness(defaultness, visitor);
+ visitor.visit_generics(generics);
+ visitor.visit_span(&mut where_clauses.0.1);
+ visitor.visit_span(&mut where_clauses.1.1);
+ visit_bounds(bounds, visitor);
+ visit_opt(ty, |ty| visitor.visit_ty(ty));
+ }
+ AssocItemKind::MacCall(mac) => visitor.visit_mac_call(mac),
+ }
+ visitor.visit_span(span);
+ visit_lazy_tts(tokens, visitor);
+ smallvec![item]
+}
+
+pub fn noop_visit_fn_header<T: MutVisitor>(header: &mut FnHeader, vis: &mut T) {
+ let FnHeader { unsafety, asyncness, constness, ext: _ } = header;
+ visit_constness(constness, vis);
+ vis.visit_asyncness(asyncness);
+ visit_unsafety(unsafety, vis);
+}
+
+pub fn noop_visit_crate<T: MutVisitor>(krate: &mut Crate, vis: &mut T) {
+ let Crate { attrs, items, spans, id, is_placeholder: _ } = krate;
+ vis.visit_id(id);
+ visit_attrs(attrs, vis);
+ items.flat_map_in_place(|item| vis.flat_map_item(item));
+ let ModSpans { inner_span, inject_use_span } = spans;
+ vis.visit_span(inner_span);
+ vis.visit_span(inject_use_span);
+}
+
+// Mutates one item into possibly many items.
+pub fn noop_flat_map_item<T: MutVisitor>(
+ mut item: P<Item>,
+ visitor: &mut T,
+) -> SmallVec<[P<Item>; 1]> {
+ let Item { ident, attrs, id, kind, vis, span, tokens } = item.deref_mut();
+ visitor.visit_ident(ident);
+ visit_attrs(attrs, visitor);
+ visitor.visit_id(id);
+ visitor.visit_item_kind(kind);
+ visitor.visit_vis(vis);
+ visitor.visit_span(span);
+ visit_lazy_tts(tokens, visitor);
+
+ smallvec![item]
+}
+
+pub fn noop_flat_map_foreign_item<T: MutVisitor>(
+ mut item: P<ForeignItem>,
+ visitor: &mut T,
+) -> SmallVec<[P<ForeignItem>; 1]> {
+ let Item { ident, attrs, id, kind, vis, span, tokens } = item.deref_mut();
+ visitor.visit_id(id);
+ visitor.visit_ident(ident);
+ visitor.visit_vis(vis);
+ visit_attrs(attrs, visitor);
+ match kind {
+ ForeignItemKind::Static(ty, _, expr) => {
+ visitor.visit_ty(ty);
+ visit_opt(expr, |expr| visitor.visit_expr(expr));
+ }
+ ForeignItemKind::Fn(box Fn { defaultness, generics, sig, body }) => {
+ visit_defaultness(defaultness, visitor);
+ visitor.visit_generics(generics);
+ visit_fn_sig(sig, visitor);
+ visit_opt(body, |body| visitor.visit_block(body));
+ }
+ ForeignItemKind::TyAlias(box TyAlias {
+ defaultness,
+ generics,
+ where_clauses,
+ bounds,
+ ty,
+ ..
+ }) => {
+ visit_defaultness(defaultness, visitor);
+ visitor.visit_generics(generics);
+ visitor.visit_span(&mut where_clauses.0.1);
+ visitor.visit_span(&mut where_clauses.1.1);
+ visit_bounds(bounds, visitor);
+ visit_opt(ty, |ty| visitor.visit_ty(ty));
+ }
+ ForeignItemKind::MacCall(mac) => visitor.visit_mac_call(mac),
+ }
+ visitor.visit_span(span);
+ visit_lazy_tts(tokens, visitor);
+ smallvec![item]
+}
+
+pub fn noop_visit_pat<T: MutVisitor>(pat: &mut P<Pat>, vis: &mut T) {
+ let Pat { id, kind, span, tokens } = pat.deref_mut();
+ vis.visit_id(id);
+ match kind {
+ PatKind::Wild | PatKind::Rest => {}
+ PatKind::Ident(_binding_mode, ident, sub) => {
+ vis.visit_ident(ident);
+ visit_opt(sub, |sub| vis.visit_pat(sub));
+ }
+ PatKind::Lit(e) => vis.visit_expr(e),
+ PatKind::TupleStruct(qself, path, elems) => {
+ vis.visit_qself(qself);
+ vis.visit_path(path);
+ visit_vec(elems, |elem| vis.visit_pat(elem));
+ }
+ PatKind::Path(qself, path) => {
+ vis.visit_qself(qself);
+ vis.visit_path(path);
+ }
+ PatKind::Struct(qself, path, fields, _etc) => {
+ vis.visit_qself(qself);
+ vis.visit_path(path);
+ fields.flat_map_in_place(|field| vis.flat_map_pat_field(field));
+ }
+ PatKind::Box(inner) => vis.visit_pat(inner),
+ PatKind::Ref(inner, _mutbl) => vis.visit_pat(inner),
+ PatKind::Range(e1, e2, Spanned { span: _, node: _ }) => {
+ visit_opt(e1, |e| vis.visit_expr(e));
+ visit_opt(e2, |e| vis.visit_expr(e));
+ vis.visit_span(span);
+ }
+ PatKind::Tuple(elems) | PatKind::Slice(elems) | PatKind::Or(elems) => {
+ visit_vec(elems, |elem| vis.visit_pat(elem))
+ }
+ PatKind::Paren(inner) => vis.visit_pat(inner),
+ PatKind::MacCall(mac) => vis.visit_mac_call(mac),
+ }
+ vis.visit_span(span);
+ visit_lazy_tts(tokens, vis);
+}
+
+pub fn noop_visit_anon_const<T: MutVisitor>(AnonConst { id, value }: &mut AnonConst, vis: &mut T) {
+ vis.visit_id(id);
+ vis.visit_expr(value);
+}
+
+pub fn noop_visit_inline_asm<T: MutVisitor>(asm: &mut InlineAsm, vis: &mut T) {
+ for (op, _) in &mut asm.operands {
+ match op {
+ InlineAsmOperand::In { expr, .. }
+ | InlineAsmOperand::Out { expr: Some(expr), .. }
+ | InlineAsmOperand::InOut { expr, .. } => vis.visit_expr(expr),
+ InlineAsmOperand::Out { expr: None, .. } => {}
+ InlineAsmOperand::SplitInOut { in_expr, out_expr, .. } => {
+ vis.visit_expr(in_expr);
+ if let Some(out_expr) = out_expr {
+ vis.visit_expr(out_expr);
+ }
+ }
+ InlineAsmOperand::Const { anon_const } => vis.visit_anon_const(anon_const),
+ InlineAsmOperand::Sym { sym } => vis.visit_inline_asm_sym(sym),
+ }
+ }
+}
+
+pub fn noop_visit_inline_asm_sym<T: MutVisitor>(
+ InlineAsmSym { id, qself, path }: &mut InlineAsmSym,
+ vis: &mut T,
+) {
+ vis.visit_id(id);
+ vis.visit_qself(qself);
+ vis.visit_path(path);
+}
+
+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::ConstBlock(anon_const) => {
+ vis.visit_anon_const(anon_const);
+ }
+ ExprKind::Repeat(expr, count) => {
+ vis.visit_expr(expr);
+ vis.visit_anon_const(count);
+ }
+ ExprKind::Tup(exprs) => visit_exprs(exprs, vis),
+ ExprKind::Call(f, args) => {
+ vis.visit_expr(f);
+ visit_exprs(args, vis);
+ }
+ ExprKind::MethodCall(PathSegment { ident, id, args }, exprs, span) => {
+ vis.visit_ident(ident);
+ vis.visit_id(id);
+ visit_opt(args, |args| vis.visit_generic_args(args));
+ visit_exprs(exprs, vis);
+ vis.visit_span(span);
+ }
+ ExprKind::Binary(_binop, lhs, rhs) => {
+ vis.visit_expr(lhs);
+ vis.visit_expr(rhs);
+ }
+ ExprKind::Unary(_unop, ohs) => vis.visit_expr(ohs),
+ ExprKind::Cast(expr, ty) => {
+ vis.visit_expr(expr);
+ vis.visit_ty(ty);
+ }
+ ExprKind::Type(expr, ty) => {
+ vis.visit_expr(expr);
+ vis.visit_ty(ty);
+ }
+ ExprKind::AddrOf(_, _, ohs) => vis.visit_expr(ohs),
+ ExprKind::Let(pat, scrutinee, _) => {
+ vis.visit_pat(pat);
+ vis.visit_expr(scrutinee);
+ }
+ ExprKind::If(cond, tr, fl) => {
+ vis.visit_expr(cond);
+ vis.visit_block(tr);
+ visit_opt(fl, |fl| vis.visit_expr(fl));
+ }
+ ExprKind::While(cond, body, label) => {
+ vis.visit_expr(cond);
+ vis.visit_block(body);
+ visit_opt(label, |label| vis.visit_label(label));
+ }
+ ExprKind::ForLoop(pat, iter, body, label) => {
+ vis.visit_pat(pat);
+ vis.visit_expr(iter);
+ vis.visit_block(body);
+ visit_opt(label, |label| vis.visit_label(label));
+ }
+ ExprKind::Loop(body, label) => {
+ vis.visit_block(body);
+ visit_opt(label, |label| vis.visit_label(label));
+ }
+ ExprKind::Match(expr, arms) => {
+ vis.visit_expr(expr);
+ arms.flat_map_in_place(|arm| vis.flat_map_arm(arm));
+ }
+ ExprKind::Closure(binder, _capture_by, asyncness, _movability, decl, body, span) => {
+ vis.visit_closure_binder(binder);
+ vis.visit_asyncness(asyncness);
+ vis.visit_fn_decl(decl);
+ vis.visit_expr(body);
+ vis.visit_span(span);
+ }
+ ExprKind::Block(blk, label) => {
+ vis.visit_block(blk);
+ visit_opt(label, |label| vis.visit_label(label));
+ }
+ ExprKind::Async(_capture_by, node_id, body) => {
+ vis.visit_id(node_id);
+ vis.visit_block(body);
+ }
+ ExprKind::Await(expr) => vis.visit_expr(expr),
+ ExprKind::Assign(el, er, _) => {
+ vis.visit_expr(el);
+ vis.visit_expr(er);
+ }
+ ExprKind::AssignOp(_op, el, er) => {
+ vis.visit_expr(el);
+ vis.visit_expr(er);
+ }
+ ExprKind::Field(el, ident) => {
+ vis.visit_expr(el);
+ vis.visit_ident(ident);
+ }
+ ExprKind::Index(el, er) => {
+ vis.visit_expr(el);
+ vis.visit_expr(er);
+ }
+ ExprKind::Range(e1, e2, _lim) => {
+ visit_opt(e1, |e1| vis.visit_expr(e1));
+ visit_opt(e2, |e2| vis.visit_expr(e2));
+ }
+ ExprKind::Underscore => {}
+ ExprKind::Path(qself, path) => {
+ vis.visit_qself(qself);
+ vis.visit_path(path);
+ }
+ ExprKind::Break(label, expr) => {
+ visit_opt(label, |label| vis.visit_label(label));
+ visit_opt(expr, |expr| vis.visit_expr(expr));
+ }
+ ExprKind::Continue(label) => {
+ visit_opt(label, |label| vis.visit_label(label));
+ }
+ ExprKind::Ret(expr) => {
+ visit_opt(expr, |expr| vis.visit_expr(expr));
+ }
+ ExprKind::Yeet(expr) => {
+ visit_opt(expr, |expr| vis.visit_expr(expr));
+ }
+ ExprKind::InlineAsm(asm) => vis.visit_inline_asm(asm),
+ ExprKind::MacCall(mac) => vis.visit_mac_call(mac),
+ ExprKind::Struct(se) => {
+ let StructExpr { qself, path, fields, rest } = se.deref_mut();
+ vis.visit_qself(qself);
+ vis.visit_path(path);
+ fields.flat_map_in_place(|field| vis.flat_map_expr_field(field));
+ match rest {
+ StructRest::Base(expr) => vis.visit_expr(expr),
+ StructRest::Rest(_span) => {}
+ StructRest::None => {}
+ }
+ }
+ ExprKind::Paren(expr) => {
+ vis.visit_expr(expr);
+ }
+ ExprKind::Yield(expr) => {
+ visit_opt(expr, |expr| vis.visit_expr(expr));
+ }
+ ExprKind::Try(expr) => vis.visit_expr(expr),
+ ExprKind::TryBlock(body) => vis.visit_block(body),
+ ExprKind::Lit(_) | ExprKind::Err => {}
+ }
+ vis.visit_id(id);
+ vis.visit_span(span);
+ visit_thin_attrs(attrs, vis);
+ visit_lazy_tts(tokens, vis);
+}
+
+pub fn noop_filter_map_expr<T: MutVisitor>(mut e: P<Expr>, vis: &mut T) -> Option<P<Expr>> {
+ Some({
+ vis.visit_expr(&mut e);
+ e
+ })
+}
+
+pub fn noop_flat_map_stmt<T: MutVisitor>(
+ Stmt { kind, mut span, mut id }: Stmt,
+ vis: &mut T,
+) -> SmallVec<[Stmt; 1]> {
+ vis.visit_id(&mut id);
+ vis.visit_span(&mut span);
+ let stmts: SmallVec<_> = noop_flat_map_stmt_kind(kind, vis)
+ .into_iter()
+ .map(|kind| Stmt { id, kind, span })
+ .collect();
+ if stmts.len() > 1 {
+ panic!(
+ "cloning statement `NodeId`s is prohibited by default, \
+ the visitor should implement custom statement visiting"
+ );
+ }
+ stmts
+}
+
+pub fn noop_flat_map_stmt_kind<T: MutVisitor>(
+ kind: StmtKind,
+ vis: &mut T,
+) -> SmallVec<[StmtKind; 1]> {
+ match kind {
+ StmtKind::Local(mut local) => smallvec![StmtKind::Local({
+ vis.visit_local(&mut local);
+ local
+ })],
+ StmtKind::Item(item) => vis.flat_map_item(item).into_iter().map(StmtKind::Item).collect(),
+ StmtKind::Expr(expr) => vis.filter_map_expr(expr).into_iter().map(StmtKind::Expr).collect(),
+ StmtKind::Semi(expr) => vis.filter_map_expr(expr).into_iter().map(StmtKind::Semi).collect(),
+ StmtKind::Empty => smallvec![StmtKind::Empty],
+ StmtKind::MacCall(mut mac) => {
+ let MacCallStmt { mac: mac_, style: _, attrs, tokens } = mac.deref_mut();
+ vis.visit_mac_call(mac_);
+ visit_thin_attrs(attrs, vis);
+ visit_lazy_tts(tokens, vis);
+ smallvec![StmtKind::MacCall(mac)]
+ }
+ }
+}
+
+pub fn noop_visit_vis<T: MutVisitor>(visibility: &mut Visibility, vis: &mut T) {
+ match &mut visibility.kind {
+ VisibilityKind::Public | VisibilityKind::Inherited => {}
+ VisibilityKind::Restricted { path, id } => {
+ vis.visit_path(path);
+ vis.visit_id(id);
+ }
+ }
+ vis.visit_span(&mut visibility.span);
+}
+
+/// Some value for the AST node that is valid but possibly meaningless.
+pub trait DummyAstNode {
+ fn dummy() -> Self;
+}
+
+impl<T> DummyAstNode for Option<T> {
+ fn dummy() -> Self {
+ Default::default()
+ }
+}
+
+impl<T: DummyAstNode + 'static> DummyAstNode for P<T> {
+ fn dummy() -> Self {
+ P(DummyAstNode::dummy())
+ }
+}
+
+impl<T> DummyAstNode for ThinVec<T> {
+ fn dummy() -> Self {
+ Default::default()
+ }
+}
+
+impl DummyAstNode for Item {
+ fn dummy() -> Self {
+ Item {
+ attrs: Default::default(),
+ id: DUMMY_NODE_ID,
+ span: Default::default(),
+ vis: Visibility {
+ kind: VisibilityKind::Public,
+ span: Default::default(),
+ tokens: Default::default(),
+ },
+ ident: Ident::empty(),
+ kind: ItemKind::ExternCrate(None),
+ tokens: Default::default(),
+ }
+ }
+}
+
+impl DummyAstNode for Expr {
+ fn dummy() -> Self {
+ Expr {
+ id: DUMMY_NODE_ID,
+ kind: ExprKind::Err,
+ span: Default::default(),
+ attrs: Default::default(),
+ tokens: Default::default(),
+ }
+ }
+}
+
+impl DummyAstNode for Ty {
+ fn dummy() -> Self {
+ Ty {
+ id: DUMMY_NODE_ID,
+ kind: TyKind::Err,
+ span: Default::default(),
+ tokens: Default::default(),
+ }
+ }
+}
+
+impl DummyAstNode for Pat {
+ fn dummy() -> Self {
+ Pat {
+ id: DUMMY_NODE_ID,
+ kind: PatKind::Wild,
+ span: Default::default(),
+ tokens: Default::default(),
+ }
+ }
+}
+
+impl DummyAstNode for Stmt {
+ fn dummy() -> Self {
+ Stmt { id: DUMMY_NODE_ID, kind: StmtKind::Empty, span: Default::default() }
+ }
+}
+
+impl DummyAstNode for Block {
+ fn dummy() -> Self {
+ Block {
+ stmts: Default::default(),
+ id: DUMMY_NODE_ID,
+ rules: BlockCheckMode::Default,
+ span: Default::default(),
+ tokens: Default::default(),
+ could_be_bare_literal: Default::default(),
+ }
+ }
+}
+
+impl DummyAstNode for Crate {
+ fn dummy() -> Self {
+ Crate {
+ attrs: Default::default(),
+ items: Default::default(),
+ spans: Default::default(),
+ id: DUMMY_NODE_ID,
+ is_placeholder: Default::default(),
+ }
+ }
+}
diff --git a/compiler/rustc_ast/src/node_id.rs b/compiler/rustc_ast/src/node_id.rs
new file mode 100644
index 000000000..7f928cb57
--- /dev/null
+++ b/compiler/rustc_ast/src/node_id.rs
@@ -0,0 +1,40 @@
+use rustc_span::LocalExpnId;
+use std::fmt;
+
+rustc_index::newtype_index! {
+ /// Identifies an AST node.
+ ///
+ /// This identifies top-level definitions, expressions, and everything in between.
+ /// This is later turned into [`DefId`] and `HirId` for the HIR.
+ ///
+ /// [`DefId`]: rustc_span::def_id::DefId
+ pub struct NodeId {
+ DEBUG_FORMAT = "NodeId({})"
+ }
+}
+
+rustc_data_structures::define_id_collections!(NodeMap, NodeSet, NodeId);
+
+/// The [`NodeId`] used to represent the root of the crate.
+pub const CRATE_NODE_ID: NodeId = NodeId::from_u32(0);
+
+/// When parsing and at the beginning of doing expansions, we initially give all AST nodes
+/// this dummy AST [`NodeId`]. Then, during a later phase of expansion, we renumber them
+/// to have small, positive IDs.
+pub const DUMMY_NODE_ID: NodeId = NodeId::MAX;
+
+impl NodeId {
+ pub fn placeholder_from_expn_id(expn_id: LocalExpnId) -> Self {
+ NodeId::from_u32(expn_id.as_u32())
+ }
+
+ pub fn placeholder_to_expn_id(self) -> LocalExpnId {
+ LocalExpnId::from_u32(self.as_u32())
+ }
+}
+
+impl fmt::Display for NodeId {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ fmt::Display::fmt(&self.as_u32(), f)
+ }
+}
diff --git a/compiler/rustc_ast/src/ptr.rs b/compiler/rustc_ast/src/ptr.rs
new file mode 100644
index 000000000..30481eddf
--- /dev/null
+++ b/compiler/rustc_ast/src/ptr.rs
@@ -0,0 +1,212 @@
+//! The AST pointer.
+//!
+//! Provides `P<T>`, a frozen owned smart pointer.
+//!
+//! # Motivations and benefits
+//!
+//! * **Identity**: sharing AST nodes is problematic for the various analysis
+//! passes (e.g., one may be able to bypass the borrow checker with a shared
+//! `ExprKind::AddrOf` node taking a mutable borrow).
+//!
+//! * **Immutability**: `P<T>` disallows mutating its inner `T`, unlike `Box<T>`
+//! (unless it contains an `Unsafe` interior, but that may be denied later).
+//! This mainly prevents mistakes, but also enforces a kind of "purity".
+//!
+//! * **Efficiency**: folding can reuse allocation space for `P<T>` and `Vec<T>`,
+//! the latter even when the input and output types differ (as it would be the
+//! case with arenas or a GADT AST using type parameters to toggle features).
+//!
+//! * **Maintainability**: `P<T>` provides a fixed interface - `Deref`,
+//! `and_then` and `map` - which can remain fully functional even if the
+//! implementation changes (using a special thread-local heap, for example).
+//! Moreover, a switch to, e.g., `P<'a, T>` would be easy and mostly automated.
+
+use std::fmt::{self, Debug, Display};
+use std::iter::FromIterator;
+use std::ops::{Deref, DerefMut};
+use std::{slice, vec};
+
+use rustc_serialize::{Decodable, Decoder, Encodable, Encoder};
+
+use rustc_data_structures::stable_hasher::{HashStable, StableHasher};
+/// An owned smart pointer.
+pub struct P<T: ?Sized> {
+ ptr: Box<T>,
+}
+
+/// Construct a `P<T>` from a `T` value.
+#[allow(non_snake_case)]
+pub fn P<T: 'static>(value: T) -> P<T> {
+ P { ptr: Box::new(value) }
+}
+
+impl<T: 'static> P<T> {
+ /// Move out of the pointer.
+ /// Intended for chaining transformations not covered by `map`.
+ pub fn and_then<U, F>(self, f: F) -> U
+ where
+ F: FnOnce(T) -> U,
+ {
+ f(*self.ptr)
+ }
+
+ /// Equivalent to `and_then(|x| x)`.
+ pub fn into_inner(self) -> T {
+ *self.ptr
+ }
+
+ /// Produce a new `P<T>` from `self` without reallocating.
+ pub fn map<F>(mut self, f: F) -> P<T>
+ where
+ F: FnOnce(T) -> T,
+ {
+ let x = f(*self.ptr);
+ *self.ptr = x;
+
+ self
+ }
+
+ /// Optionally produce a new `P<T>` from `self` without reallocating.
+ pub fn filter_map<F>(mut self, f: F) -> Option<P<T>>
+ where
+ F: FnOnce(T) -> Option<T>,
+ {
+ *self.ptr = f(*self.ptr)?;
+ Some(self)
+ }
+}
+
+impl<T: ?Sized> Deref for P<T> {
+ type Target = T;
+
+ fn deref(&self) -> &T {
+ &self.ptr
+ }
+}
+
+impl<T: ?Sized> DerefMut for P<T> {
+ fn deref_mut(&mut self) -> &mut T {
+ &mut self.ptr
+ }
+}
+
+impl<T: 'static + Clone> Clone for P<T> {
+ fn clone(&self) -> P<T> {
+ P((**self).clone())
+ }
+}
+
+impl<T: ?Sized + Debug> Debug for P<T> {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ Debug::fmt(&self.ptr, f)
+ }
+}
+
+impl<T: Display> Display for P<T> {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ Display::fmt(&**self, f)
+ }
+}
+
+impl<T> fmt::Pointer for P<T> {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ fmt::Pointer::fmt(&self.ptr, f)
+ }
+}
+
+impl<D: Decoder, T: 'static + Decodable<D>> Decodable<D> for P<T> {
+ fn decode(d: &mut D) -> P<T> {
+ P(Decodable::decode(d))
+ }
+}
+
+impl<S: Encoder, T: Encodable<S>> Encodable<S> for P<T> {
+ fn encode(&self, s: &mut S) {
+ (**self).encode(s);
+ }
+}
+
+impl<T> P<[T]> {
+ pub const fn new() -> P<[T]> {
+ P { ptr: Box::default() }
+ }
+
+ #[inline(never)]
+ pub fn from_vec(v: Vec<T>) -> P<[T]> {
+ P { ptr: v.into_boxed_slice() }
+ }
+
+ #[inline(never)]
+ pub fn into_vec(self) -> Vec<T> {
+ self.ptr.into_vec()
+ }
+}
+
+impl<T> Default for P<[T]> {
+ /// Creates an empty `P<[T]>`.
+ fn default() -> P<[T]> {
+ P::new()
+ }
+}
+
+impl<T: Clone> Clone for P<[T]> {
+ fn clone(&self) -> P<[T]> {
+ P::from_vec(self.to_vec())
+ }
+}
+
+impl<T> From<Vec<T>> for P<[T]> {
+ fn from(v: Vec<T>) -> Self {
+ P::from_vec(v)
+ }
+}
+
+impl<T> Into<Vec<T>> for P<[T]> {
+ fn into(self) -> Vec<T> {
+ self.into_vec()
+ }
+}
+
+impl<T> FromIterator<T> for P<[T]> {
+ fn from_iter<I: IntoIterator<Item = T>>(iter: I) -> P<[T]> {
+ P::from_vec(iter.into_iter().collect())
+ }
+}
+
+impl<T> IntoIterator for P<[T]> {
+ type Item = T;
+ type IntoIter = vec::IntoIter<T>;
+
+ fn into_iter(self) -> Self::IntoIter {
+ self.into_vec().into_iter()
+ }
+}
+
+impl<'a, T> IntoIterator for &'a P<[T]> {
+ type Item = &'a T;
+ type IntoIter = slice::Iter<'a, T>;
+ fn into_iter(self) -> Self::IntoIter {
+ self.ptr.into_iter()
+ }
+}
+
+impl<S: Encoder, T: Encodable<S>> Encodable<S> for P<[T]> {
+ fn encode(&self, s: &mut S) {
+ Encodable::encode(&**self, s);
+ }
+}
+
+impl<D: Decoder, T: Decodable<D>> Decodable<D> for P<[T]> {
+ fn decode(d: &mut D) -> P<[T]> {
+ P::from_vec(Decodable::decode(d))
+ }
+}
+
+impl<CTX, T> HashStable<CTX> for P<T>
+where
+ T: ?Sized + HashStable<CTX>,
+{
+ fn hash_stable(&self, hcx: &mut CTX, hasher: &mut StableHasher) {
+ (**self).hash_stable(hcx, hasher);
+ }
+}
diff --git a/compiler/rustc_ast/src/token.rs b/compiler/rustc_ast/src/token.rs
new file mode 100644
index 000000000..85d9687c6
--- /dev/null
+++ b/compiler/rustc_ast/src/token.rs
@@ -0,0 +1,851 @@
+pub use BinOpToken::*;
+pub use LitKind::*;
+pub use Nonterminal::*;
+pub use TokenKind::*;
+
+use crate::ast;
+use crate::ptr::P;
+
+use rustc_data_structures::stable_hasher::{HashStable, StableHasher};
+use rustc_data_structures::sync::Lrc;
+use rustc_macros::HashStable_Generic;
+use rustc_span::symbol::{kw, sym};
+use rustc_span::symbol::{Ident, Symbol};
+use rustc_span::{self, edition::Edition, Span, DUMMY_SP};
+use std::borrow::Cow;
+use std::{fmt, mem};
+
+#[derive(Clone, Copy, PartialEq, Encodable, Decodable, Debug, HashStable_Generic)]
+pub enum CommentKind {
+ Line,
+ Block,
+}
+
+#[derive(Clone, PartialEq, Encodable, Decodable, Hash, Debug, Copy)]
+#[derive(HashStable_Generic)]
+pub enum BinOpToken {
+ Plus,
+ Minus,
+ Star,
+ Slash,
+ Percent,
+ Caret,
+ And,
+ Or,
+ Shl,
+ Shr,
+}
+
+/// Describes how a sequence of token trees is delimited.
+/// Cannot use `proc_macro::Delimiter` directly because this
+/// structure should implement some additional traits.
+/// The `None` variant is also renamed to `Invisible` to be
+/// less confusing and better convey the semantics.
+#[derive(Copy, Clone, Debug, PartialEq, Eq)]
+#[derive(Encodable, Decodable, Hash, HashStable_Generic)]
+pub enum Delimiter {
+ /// `( ... )`
+ Parenthesis,
+ /// `{ ... }`
+ Brace,
+ /// `[ ... ]`
+ Bracket,
+ /// `Ø ... Ø`
+ /// An invisible delimiter, that may, for example, appear around tokens coming from a
+ /// "macro variable" `$var`. It is important to preserve operator priorities in cases like
+ /// `$var * 3` where `$var` is `1 + 2`.
+ /// Invisible delimiters might not survive roundtrip of a token stream through a string.
+ Invisible,
+}
+
+#[derive(Clone, Copy, PartialEq, Encodable, Decodable, Debug, HashStable_Generic)]
+pub enum LitKind {
+ Bool, // AST only, must never appear in a `Token`
+ Byte,
+ Char,
+ Integer,
+ Float,
+ Str,
+ StrRaw(u8), // raw string delimited by `n` hash symbols
+ ByteStr,
+ ByteStrRaw(u8), // raw byte string delimited by `n` hash symbols
+ Err,
+}
+
+/// A literal token.
+#[derive(Clone, Copy, PartialEq, Encodable, Decodable, Debug, HashStable_Generic)]
+pub struct Lit {
+ pub kind: LitKind,
+ pub symbol: Symbol,
+ pub suffix: Option<Symbol>,
+}
+
+impl fmt::Display for Lit {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ let Lit { kind, symbol, suffix } = *self;
+ match kind {
+ Byte => write!(f, "b'{}'", symbol)?,
+ Char => write!(f, "'{}'", symbol)?,
+ Str => write!(f, "\"{}\"", symbol)?,
+ StrRaw(n) => write!(
+ f,
+ "r{delim}\"{string}\"{delim}",
+ delim = "#".repeat(n as usize),
+ string = symbol
+ )?,
+ ByteStr => write!(f, "b\"{}\"", symbol)?,
+ ByteStrRaw(n) => write!(
+ f,
+ "br{delim}\"{string}\"{delim}",
+ delim = "#".repeat(n as usize),
+ string = symbol
+ )?,
+ Integer | Float | Bool | Err => write!(f, "{}", symbol)?,
+ }
+
+ if let Some(suffix) = suffix {
+ write!(f, "{}", suffix)?;
+ }
+
+ Ok(())
+ }
+}
+
+impl LitKind {
+ /// An English article for the literal token kind.
+ pub fn article(self) -> &'static str {
+ match self {
+ Integer | Err => "an",
+ _ => "a",
+ }
+ }
+
+ pub fn descr(self) -> &'static str {
+ match self {
+ Bool => panic!("literal token contains `Lit::Bool`"),
+ Byte => "byte",
+ Char => "char",
+ Integer => "integer",
+ Float => "float",
+ Str | StrRaw(..) => "string",
+ ByteStr | ByteStrRaw(..) => "byte string",
+ Err => "error",
+ }
+ }
+
+ pub(crate) fn may_have_suffix(self) -> bool {
+ matches!(self, Integer | Float | Err)
+ }
+}
+
+impl Lit {
+ pub fn new(kind: LitKind, symbol: Symbol, suffix: Option<Symbol>) -> Lit {
+ Lit { kind, symbol, suffix }
+ }
+}
+
+pub fn ident_can_begin_expr(name: Symbol, span: Span, is_raw: bool) -> bool {
+ let ident_token = Token::new(Ident(name, is_raw), span);
+
+ !ident_token.is_reserved_ident()
+ || ident_token.is_path_segment_keyword()
+ || [
+ kw::Async,
+ kw::Do,
+ kw::Box,
+ kw::Break,
+ kw::Const,
+ kw::Continue,
+ kw::False,
+ kw::For,
+ kw::If,
+ kw::Let,
+ kw::Loop,
+ kw::Match,
+ kw::Move,
+ kw::Return,
+ kw::True,
+ kw::Try,
+ kw::Unsafe,
+ kw::While,
+ kw::Yield,
+ kw::Static,
+ ]
+ .contains(&name)
+}
+
+fn ident_can_begin_type(name: Symbol, span: Span, is_raw: bool) -> bool {
+ let ident_token = Token::new(Ident(name, is_raw), span);
+
+ !ident_token.is_reserved_ident()
+ || ident_token.is_path_segment_keyword()
+ || [kw::Underscore, kw::For, kw::Impl, kw::Fn, kw::Unsafe, kw::Extern, kw::Typeof, kw::Dyn]
+ .contains(&name)
+}
+
+#[derive(Clone, PartialEq, Encodable, Decodable, Debug, HashStable_Generic)]
+pub enum TokenKind {
+ /* Expression-operator symbols. */
+ Eq,
+ Lt,
+ Le,
+ EqEq,
+ Ne,
+ Ge,
+ Gt,
+ AndAnd,
+ OrOr,
+ Not,
+ Tilde,
+ BinOp(BinOpToken),
+ BinOpEq(BinOpToken),
+
+ /* Structural symbols */
+ At,
+ Dot,
+ DotDot,
+ DotDotDot,
+ DotDotEq,
+ Comma,
+ Semi,
+ Colon,
+ ModSep,
+ RArrow,
+ LArrow,
+ FatArrow,
+ Pound,
+ Dollar,
+ Question,
+ /// Used by proc macros for representing lifetimes, not generated by lexer right now.
+ SingleQuote,
+ /// An opening delimiter (e.g., `{`).
+ OpenDelim(Delimiter),
+ /// A closing delimiter (e.g., `}`).
+ CloseDelim(Delimiter),
+
+ /* Literals */
+ Literal(Lit),
+
+ /// Identifier token.
+ /// Do not forget about `NtIdent` when you want to match on identifiers.
+ /// It's recommended to use `Token::(ident,uninterpolate,uninterpolated_span)` to
+ /// treat regular and interpolated identifiers in the same way.
+ Ident(Symbol, /* is_raw */ bool),
+ /// Lifetime identifier token.
+ /// Do not forget about `NtLifetime` when you want to match on lifetime identifiers.
+ /// It's recommended to use `Token::(lifetime,uninterpolate,uninterpolated_span)` to
+ /// treat regular and interpolated lifetime identifiers in the same way.
+ Lifetime(Symbol),
+
+ /// An embedded AST node, as produced by a macro. This only exists for
+ /// historical reasons. We'd like to get rid of it, for multiple reasons.
+ /// - It's conceptually very strange. Saying a token can contain an AST
+ /// node is like saying, in natural language, that a word can contain a
+ /// sentence.
+ /// - It requires special handling in a bunch of places in the parser.
+ /// - It prevents `Token` from implementing `Copy`.
+ /// It adds complexity and likely slows things down. Please don't add new
+ /// occurrences of this token kind!
+ Interpolated(Lrc<Nonterminal>),
+
+ /// A doc comment token.
+ /// `Symbol` is the doc comment's data excluding its "quotes" (`///`, `/**`, etc)
+ /// similarly to symbols in string literal tokens.
+ DocComment(CommentKind, ast::AttrStyle, Symbol),
+
+ Eof,
+}
+
+// `TokenKind` is used a lot. Make sure it doesn't unintentionally get bigger.
+#[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))]
+rustc_data_structures::static_assert_size!(TokenKind, 16);
+
+#[derive(Clone, PartialEq, Encodable, Decodable, Debug, HashStable_Generic)]
+pub struct Token {
+ pub kind: TokenKind,
+ pub span: Span,
+}
+
+impl TokenKind {
+ pub fn lit(kind: LitKind, symbol: Symbol, suffix: Option<Symbol>) -> TokenKind {
+ Literal(Lit::new(kind, symbol, suffix))
+ }
+
+ // An approximation to proc-macro-style single-character operators used by rustc parser.
+ // If the operator token can be broken into two tokens, the first of which is single-character,
+ // then this function performs that operation, otherwise it returns `None`.
+ pub fn break_two_token_op(&self) -> Option<(TokenKind, TokenKind)> {
+ Some(match *self {
+ Le => (Lt, Eq),
+ EqEq => (Eq, Eq),
+ Ne => (Not, Eq),
+ Ge => (Gt, Eq),
+ AndAnd => (BinOp(And), BinOp(And)),
+ OrOr => (BinOp(Or), BinOp(Or)),
+ BinOp(Shl) => (Lt, Lt),
+ BinOp(Shr) => (Gt, Gt),
+ BinOpEq(Plus) => (BinOp(Plus), Eq),
+ BinOpEq(Minus) => (BinOp(Minus), Eq),
+ BinOpEq(Star) => (BinOp(Star), Eq),
+ BinOpEq(Slash) => (BinOp(Slash), Eq),
+ BinOpEq(Percent) => (BinOp(Percent), Eq),
+ BinOpEq(Caret) => (BinOp(Caret), Eq),
+ BinOpEq(And) => (BinOp(And), Eq),
+ BinOpEq(Or) => (BinOp(Or), Eq),
+ BinOpEq(Shl) => (Lt, Le),
+ BinOpEq(Shr) => (Gt, Ge),
+ DotDot => (Dot, Dot),
+ DotDotDot => (Dot, DotDot),
+ ModSep => (Colon, Colon),
+ RArrow => (BinOp(Minus), Gt),
+ LArrow => (Lt, BinOp(Minus)),
+ FatArrow => (Eq, Gt),
+ _ => return None,
+ })
+ }
+
+ /// Returns tokens that are likely to be typed accidentally instead of the current token.
+ /// Enables better error recovery when the wrong token is found.
+ pub fn similar_tokens(&self) -> Option<Vec<TokenKind>> {
+ match *self {
+ Comma => Some(vec![Dot, Lt, Semi]),
+ Semi => Some(vec![Colon, Comma]),
+ FatArrow => Some(vec![Eq, RArrow]),
+ _ => None,
+ }
+ }
+
+ pub fn should_end_const_arg(&self) -> bool {
+ matches!(self, Gt | Ge | BinOp(Shr) | BinOpEq(Shr))
+ }
+}
+
+impl Token {
+ pub fn new(kind: TokenKind, span: Span) -> Self {
+ Token { kind, span }
+ }
+
+ /// Some token that will be thrown away later.
+ pub fn dummy() -> Self {
+ Token::new(TokenKind::Question, DUMMY_SP)
+ }
+
+ /// Recovers a `Token` from an `Ident`. This creates a raw identifier if necessary.
+ pub fn from_ast_ident(ident: Ident) -> Self {
+ Token::new(Ident(ident.name, ident.is_raw_guess()), ident.span)
+ }
+
+ /// Return this token by value and leave a dummy token in its place.
+ pub fn take(&mut self) -> Self {
+ mem::replace(self, Token::dummy())
+ }
+
+ /// For interpolated tokens, returns a span of the fragment to which the interpolated
+ /// token refers. For all other tokens this is just a regular span.
+ /// It is particularly important to use this for identifiers and lifetimes
+ /// for which spans affect name resolution and edition checks.
+ /// Note that keywords are also identifiers, so they should use this
+ /// if they keep spans or perform edition checks.
+ pub fn uninterpolated_span(&self) -> Span {
+ match &self.kind {
+ Interpolated(nt) => nt.span(),
+ _ => self.span,
+ }
+ }
+
+ pub fn is_op(&self) -> bool {
+ !matches!(
+ self.kind,
+ OpenDelim(..)
+ | CloseDelim(..)
+ | Literal(..)
+ | DocComment(..)
+ | Ident(..)
+ | Lifetime(..)
+ | Interpolated(..)
+ | Eof
+ )
+ }
+
+ pub fn is_like_plus(&self) -> bool {
+ matches!(self.kind, BinOp(Plus) | BinOpEq(Plus))
+ }
+
+ /// Returns `true` if the token can appear at the start of an expression.
+ pub fn can_begin_expr(&self) -> bool {
+ match self.uninterpolate().kind {
+ Ident(name, is_raw) =>
+ ident_can_begin_expr(name, self.span, is_raw), // value name or keyword
+ OpenDelim(..) | // tuple, array or block
+ Literal(..) | // literal
+ Not | // operator not
+ BinOp(Minus) | // unary minus
+ BinOp(Star) | // dereference
+ BinOp(Or) | OrOr | // closure
+ BinOp(And) | // reference
+ AndAnd | // double reference
+ // DotDotDot is no longer supported, but we need some way to display the error
+ DotDot | DotDotDot | DotDotEq | // range notation
+ Lt | BinOp(Shl) | // associated path
+ ModSep | // global path
+ Lifetime(..) | // labeled loop
+ Pound => true, // expression attributes
+ Interpolated(ref nt) => matches!(**nt, NtLiteral(..) |
+ NtExpr(..) |
+ NtBlock(..) |
+ NtPath(..)),
+ _ => false,
+ }
+ }
+
+ /// Returns `true` if the token can appear at the start of a type.
+ pub fn can_begin_type(&self) -> bool {
+ match self.uninterpolate().kind {
+ Ident(name, is_raw) =>
+ ident_can_begin_type(name, self.span, is_raw), // type name or keyword
+ OpenDelim(Delimiter::Parenthesis) | // tuple
+ OpenDelim(Delimiter::Bracket) | // array
+ Not | // never
+ BinOp(Star) | // raw pointer
+ BinOp(And) | // reference
+ AndAnd | // double reference
+ Question | // maybe bound in trait object
+ Lifetime(..) | // lifetime bound in trait object
+ Lt | BinOp(Shl) | // associated path
+ ModSep => true, // global path
+ Interpolated(ref nt) => matches!(**nt, NtTy(..) | NtPath(..)),
+ _ => false,
+ }
+ }
+
+ /// Returns `true` if the token can appear at the start of a const param.
+ pub fn can_begin_const_arg(&self) -> bool {
+ match self.kind {
+ OpenDelim(Delimiter::Brace) => true,
+ Interpolated(ref nt) => matches!(**nt, NtExpr(..) | NtBlock(..) | NtLiteral(..)),
+ _ => self.can_begin_literal_maybe_minus(),
+ }
+ }
+
+ /// Returns `true` if the token can appear at the start of a generic bound.
+ pub fn can_begin_bound(&self) -> bool {
+ self.is_path_start()
+ || self.is_lifetime()
+ || self.is_keyword(kw::For)
+ || self == &Question
+ || self == &OpenDelim(Delimiter::Parenthesis)
+ }
+
+ /// Returns `true` if the token is any literal.
+ pub fn is_lit(&self) -> bool {
+ matches!(self.kind, Literal(..))
+ }
+
+ /// Returns `true` if the token is any literal, a minus (which can prefix a literal,
+ /// for example a '-42', or one of the boolean idents).
+ ///
+ /// In other words, would this token be a valid start of `parse_literal_maybe_minus`?
+ ///
+ /// Keep this in sync with and `Lit::from_token`, excluding unary negation.
+ pub fn can_begin_literal_maybe_minus(&self) -> bool {
+ match self.uninterpolate().kind {
+ Literal(..) | BinOp(Minus) => true,
+ Ident(name, false) if name.is_bool_lit() => true,
+ Interpolated(ref nt) => match &**nt {
+ NtLiteral(_) => true,
+ NtExpr(e) => match &e.kind {
+ ast::ExprKind::Lit(_) => true,
+ ast::ExprKind::Unary(ast::UnOp::Neg, e) => {
+ matches!(&e.kind, ast::ExprKind::Lit(_))
+ }
+ _ => false,
+ },
+ _ => false,
+ },
+ _ => false,
+ }
+ }
+
+ // A convenience function for matching on identifiers during parsing.
+ // Turns interpolated identifier (`$i: ident`) or lifetime (`$l: lifetime`) token
+ // into the regular identifier or lifetime token it refers to,
+ // otherwise returns the original token.
+ pub fn uninterpolate(&self) -> Cow<'_, Token> {
+ match &self.kind {
+ Interpolated(nt) => match **nt {
+ NtIdent(ident, is_raw) => {
+ Cow::Owned(Token::new(Ident(ident.name, is_raw), ident.span))
+ }
+ NtLifetime(ident) => Cow::Owned(Token::new(Lifetime(ident.name), ident.span)),
+ _ => Cow::Borrowed(self),
+ },
+ _ => Cow::Borrowed(self),
+ }
+ }
+
+ /// Returns an identifier if this token is an identifier.
+ #[inline]
+ pub fn ident(&self) -> Option<(Ident, /* is_raw */ bool)> {
+ // We avoid using `Token::uninterpolate` here because it's slow.
+ match &self.kind {
+ &Ident(name, is_raw) => Some((Ident::new(name, self.span), is_raw)),
+ Interpolated(nt) => match **nt {
+ NtIdent(ident, is_raw) => Some((ident, is_raw)),
+ _ => None,
+ },
+ _ => None,
+ }
+ }
+
+ /// Returns a lifetime identifier if this token is a lifetime.
+ #[inline]
+ pub fn lifetime(&self) -> Option<Ident> {
+ // We avoid using `Token::uninterpolate` here because it's slow.
+ match &self.kind {
+ &Lifetime(name) => Some(Ident::new(name, self.span)),
+ Interpolated(nt) => match **nt {
+ NtLifetime(ident) => Some(ident),
+ _ => None,
+ },
+ _ => None,
+ }
+ }
+
+ /// Returns `true` if the token is an identifier.
+ pub fn is_ident(&self) -> bool {
+ self.ident().is_some()
+ }
+
+ /// Returns `true` if the token is a lifetime.
+ pub fn is_lifetime(&self) -> bool {
+ self.lifetime().is_some()
+ }
+
+ /// Returns `true` if the token is an identifier whose name is the given
+ /// string slice.
+ pub fn is_ident_named(&self, name: Symbol) -> bool {
+ self.ident().map_or(false, |(ident, _)| ident.name == name)
+ }
+
+ /// Returns `true` if the token is an interpolated path.
+ fn is_path(&self) -> bool {
+ if let Interpolated(ref nt) = self.kind && let NtPath(..) = **nt {
+ return true;
+ }
+ false
+ }
+
+ /// Would `maybe_whole_expr` in `parser.rs` return `Ok(..)`?
+ /// That is, is this a pre-parsed expression dropped into the token stream
+ /// (which happens while parsing the result of macro expansion)?
+ pub fn is_whole_expr(&self) -> bool {
+ if let Interpolated(ref nt) = self.kind
+ && let NtExpr(_) | NtLiteral(_) | NtPath(_) | NtBlock(_) = **nt
+ {
+ return true;
+ }
+
+ false
+ }
+
+ // Is the token an interpolated block (`$b:block`)?
+ pub fn is_whole_block(&self) -> bool {
+ if let Interpolated(ref nt) = self.kind && let NtBlock(..) = **nt {
+ return true;
+ }
+ false
+ }
+
+ /// Returns `true` if the token is either the `mut` or `const` keyword.
+ pub fn is_mutability(&self) -> bool {
+ self.is_keyword(kw::Mut) || self.is_keyword(kw::Const)
+ }
+
+ pub fn is_qpath_start(&self) -> bool {
+ self == &Lt || self == &BinOp(Shl)
+ }
+
+ pub fn is_path_start(&self) -> bool {
+ self == &ModSep
+ || self.is_qpath_start()
+ || self.is_path()
+ || self.is_path_segment_keyword()
+ || self.is_ident() && !self.is_reserved_ident()
+ }
+
+ /// Returns `true` if the token is a given keyword, `kw`.
+ pub fn is_keyword(&self, kw: Symbol) -> bool {
+ self.is_non_raw_ident_where(|id| id.name == kw)
+ }
+
+ pub fn is_path_segment_keyword(&self) -> bool {
+ self.is_non_raw_ident_where(Ident::is_path_segment_keyword)
+ }
+
+ // Returns true for reserved identifiers used internally for elided lifetimes,
+ // unnamed method parameters, crate root module, error recovery etc.
+ pub fn is_special_ident(&self) -> bool {
+ self.is_non_raw_ident_where(Ident::is_special)
+ }
+
+ /// Returns `true` if the token is a keyword used in the language.
+ pub fn is_used_keyword(&self) -> bool {
+ self.is_non_raw_ident_where(Ident::is_used_keyword)
+ }
+
+ /// Returns `true` if the token is a keyword reserved for possible future use.
+ pub fn is_unused_keyword(&self) -> bool {
+ self.is_non_raw_ident_where(Ident::is_unused_keyword)
+ }
+
+ /// Returns `true` if the token is either a special identifier or a keyword.
+ pub fn is_reserved_ident(&self) -> bool {
+ self.is_non_raw_ident_where(Ident::is_reserved)
+ }
+
+ /// Returns `true` if the token is the identifier `true` or `false`.
+ pub fn is_bool_lit(&self) -> bool {
+ self.is_non_raw_ident_where(|id| id.name.is_bool_lit())
+ }
+
+ pub fn is_numeric_lit(&self) -> bool {
+ matches!(
+ self.kind,
+ Literal(Lit { kind: LitKind::Integer, .. }) | Literal(Lit { kind: LitKind::Float, .. })
+ )
+ }
+
+ /// Returns `true` if the token is a non-raw identifier for which `pred` holds.
+ pub fn is_non_raw_ident_where(&self, pred: impl FnOnce(Ident) -> bool) -> bool {
+ match self.ident() {
+ Some((id, false)) => pred(id),
+ _ => false,
+ }
+ }
+
+ pub fn glue(&self, joint: &Token) -> Option<Token> {
+ let kind = match self.kind {
+ Eq => match joint.kind {
+ Eq => EqEq,
+ Gt => FatArrow,
+ _ => return None,
+ },
+ Lt => match joint.kind {
+ Eq => Le,
+ Lt => BinOp(Shl),
+ Le => BinOpEq(Shl),
+ BinOp(Minus) => LArrow,
+ _ => return None,
+ },
+ Gt => match joint.kind {
+ Eq => Ge,
+ Gt => BinOp(Shr),
+ Ge => BinOpEq(Shr),
+ _ => return None,
+ },
+ Not => match joint.kind {
+ Eq => Ne,
+ _ => return None,
+ },
+ BinOp(op) => match joint.kind {
+ Eq => BinOpEq(op),
+ BinOp(And) if op == And => AndAnd,
+ BinOp(Or) if op == Or => OrOr,
+ Gt if op == Minus => RArrow,
+ _ => return None,
+ },
+ Dot => match joint.kind {
+ Dot => DotDot,
+ DotDot => DotDotDot,
+ _ => return None,
+ },
+ DotDot => match joint.kind {
+ Dot => DotDotDot,
+ Eq => DotDotEq,
+ _ => return None,
+ },
+ Colon => match joint.kind {
+ Colon => ModSep,
+ _ => return None,
+ },
+ SingleQuote => match joint.kind {
+ Ident(name, false) => Lifetime(Symbol::intern(&format!("'{}", name))),
+ _ => return None,
+ },
+
+ Le | EqEq | Ne | Ge | AndAnd | OrOr | Tilde | BinOpEq(..) | At | DotDotDot
+ | DotDotEq | Comma | Semi | ModSep | RArrow | LArrow | FatArrow | Pound | Dollar
+ | Question | OpenDelim(..) | CloseDelim(..) | Literal(..) | Ident(..)
+ | Lifetime(..) | Interpolated(..) | DocComment(..) | Eof => return None,
+ };
+
+ Some(Token::new(kind, self.span.to(joint.span)))
+ }
+}
+
+impl PartialEq<TokenKind> for Token {
+ fn eq(&self, rhs: &TokenKind) -> bool {
+ self.kind == *rhs
+ }
+}
+
+#[derive(Clone, Encodable, Decodable)]
+/// For interpolation during macro expansion.
+pub enum Nonterminal {
+ NtItem(P<ast::Item>),
+ NtBlock(P<ast::Block>),
+ NtStmt(P<ast::Stmt>),
+ NtPat(P<ast::Pat>),
+ NtExpr(P<ast::Expr>),
+ NtTy(P<ast::Ty>),
+ NtIdent(Ident, /* is_raw */ bool),
+ NtLifetime(Ident),
+ NtLiteral(P<ast::Expr>),
+ /// Stuff inside brackets for attributes
+ NtMeta(P<ast::AttrItem>),
+ NtPath(P<ast::Path>),
+ NtVis(P<ast::Visibility>),
+}
+
+// `Nonterminal` is used a lot. Make sure it doesn't unintentionally get bigger.
+#[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))]
+rustc_data_structures::static_assert_size!(Nonterminal, 16);
+
+#[derive(Debug, Copy, Clone, PartialEq, Encodable, Decodable)]
+pub enum NonterminalKind {
+ Item,
+ Block,
+ Stmt,
+ PatParam {
+ /// Keep track of whether the user used `:pat_param` or `:pat` and we inferred it from the
+ /// edition of the span. This is used for diagnostics.
+ inferred: bool,
+ },
+ PatWithOr,
+ Expr,
+ Ty,
+ Ident,
+ Lifetime,
+ Literal,
+ Meta,
+ Path,
+ Vis,
+ TT,
+}
+
+impl NonterminalKind {
+ /// The `edition` closure is used to get the edition for the given symbol. Doing
+ /// `span.edition()` is expensive, so we do it lazily.
+ pub fn from_symbol(
+ symbol: Symbol,
+ edition: impl FnOnce() -> Edition,
+ ) -> Option<NonterminalKind> {
+ Some(match symbol {
+ sym::item => NonterminalKind::Item,
+ sym::block => NonterminalKind::Block,
+ sym::stmt => NonterminalKind::Stmt,
+ sym::pat => match edition() {
+ Edition::Edition2015 | Edition::Edition2018 => {
+ NonterminalKind::PatParam { inferred: true }
+ }
+ Edition::Edition2021 | Edition::Edition2024 => NonterminalKind::PatWithOr,
+ },
+ sym::pat_param => NonterminalKind::PatParam { inferred: false },
+ sym::expr => NonterminalKind::Expr,
+ sym::ty => NonterminalKind::Ty,
+ sym::ident => NonterminalKind::Ident,
+ sym::lifetime => NonterminalKind::Lifetime,
+ sym::literal => NonterminalKind::Literal,
+ sym::meta => NonterminalKind::Meta,
+ sym::path => NonterminalKind::Path,
+ sym::vis => NonterminalKind::Vis,
+ sym::tt => NonterminalKind::TT,
+ _ => return None,
+ })
+ }
+ fn symbol(self) -> Symbol {
+ match self {
+ NonterminalKind::Item => sym::item,
+ NonterminalKind::Block => sym::block,
+ NonterminalKind::Stmt => sym::stmt,
+ NonterminalKind::PatParam { inferred: false } => sym::pat_param,
+ NonterminalKind::PatParam { inferred: true } | NonterminalKind::PatWithOr => sym::pat,
+ NonterminalKind::Expr => sym::expr,
+ NonterminalKind::Ty => sym::ty,
+ NonterminalKind::Ident => sym::ident,
+ NonterminalKind::Lifetime => sym::lifetime,
+ NonterminalKind::Literal => sym::literal,
+ NonterminalKind::Meta => sym::meta,
+ NonterminalKind::Path => sym::path,
+ NonterminalKind::Vis => sym::vis,
+ NonterminalKind::TT => sym::tt,
+ }
+ }
+}
+
+impl fmt::Display for NonterminalKind {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ write!(f, "{}", self.symbol())
+ }
+}
+
+impl Nonterminal {
+ pub fn span(&self) -> Span {
+ match self {
+ NtItem(item) => item.span,
+ NtBlock(block) => block.span,
+ NtStmt(stmt) => stmt.span,
+ NtPat(pat) => pat.span,
+ NtExpr(expr) | NtLiteral(expr) => expr.span,
+ NtTy(ty) => ty.span,
+ NtIdent(ident, _) | NtLifetime(ident) => ident.span,
+ NtMeta(attr_item) => attr_item.span(),
+ NtPath(path) => path.span,
+ NtVis(vis) => vis.span,
+ }
+ }
+}
+
+impl PartialEq for Nonterminal {
+ fn eq(&self, rhs: &Self) -> bool {
+ match (self, rhs) {
+ (NtIdent(ident_lhs, is_raw_lhs), NtIdent(ident_rhs, is_raw_rhs)) => {
+ ident_lhs == ident_rhs && is_raw_lhs == is_raw_rhs
+ }
+ (NtLifetime(ident_lhs), NtLifetime(ident_rhs)) => ident_lhs == ident_rhs,
+ // FIXME: Assume that all "complex" nonterminal are not equal, we can't compare them
+ // correctly based on data from AST. This will prevent them from matching each other
+ // in macros. The comparison will become possible only when each nonterminal has an
+ // attached token stream from which it was parsed.
+ _ => false,
+ }
+ }
+}
+
+impl fmt::Debug for Nonterminal {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ match *self {
+ NtItem(..) => f.pad("NtItem(..)"),
+ NtBlock(..) => f.pad("NtBlock(..)"),
+ NtStmt(..) => f.pad("NtStmt(..)"),
+ NtPat(..) => f.pad("NtPat(..)"),
+ NtExpr(..) => f.pad("NtExpr(..)"),
+ NtTy(..) => f.pad("NtTy(..)"),
+ NtIdent(..) => f.pad("NtIdent(..)"),
+ NtLiteral(..) => f.pad("NtLiteral(..)"),
+ NtMeta(..) => f.pad("NtMeta(..)"),
+ NtPath(..) => f.pad("NtPath(..)"),
+ NtVis(..) => f.pad("NtVis(..)"),
+ NtLifetime(..) => f.pad("NtLifetime(..)"),
+ }
+ }
+}
+
+impl<CTX> HashStable<CTX> for Nonterminal
+where
+ CTX: crate::HashStableContext,
+{
+ fn hash_stable(&self, _hcx: &mut CTX, _hasher: &mut StableHasher) {
+ panic!("interpolated tokens should not be present in the HIR")
+ }
+}
diff --git a/compiler/rustc_ast/src/tokenstream.rs b/compiler/rustc_ast/src/tokenstream.rs
new file mode 100644
index 000000000..9e4a22e1f
--- /dev/null
+++ b/compiler/rustc_ast/src/tokenstream.rs
@@ -0,0 +1,681 @@
+//! # Token Streams
+//!
+//! `TokenStream`s represent syntactic objects before they are converted into ASTs.
+//! A `TokenStream` is, roughly speaking, a sequence of [`TokenTree`]s,
+//! which are themselves a single [`Token`] or a `Delimited` subsequence of tokens.
+//!
+//! ## Ownership
+//!
+//! `TokenStream`s are persistent data structures constructed as ropes with reference
+//! counted-children. In general, this means that calling an operation on a `TokenStream`
+//! (such as `slice`) produces an entirely new `TokenStream` from the borrowed reference to
+//! the original. This essentially coerces `TokenStream`s into "views" of their subparts,
+//! and a borrowed `TokenStream` is sufficient to build an owned `TokenStream` without taking
+//! ownership of the original.
+
+use crate::ast::StmtKind;
+use crate::ast_traits::{HasAttrs, HasSpan, HasTokens};
+use crate::token::{self, Delimiter, Nonterminal, Token, TokenKind};
+use crate::AttrVec;
+
+use rustc_data_structures::stable_hasher::{HashStable, StableHasher};
+use rustc_data_structures::sync::{self, Lrc};
+use rustc_macros::HashStable_Generic;
+use rustc_serialize::{Decodable, Decoder, Encodable, Encoder};
+use rustc_span::{Span, DUMMY_SP};
+use smallvec::{smallvec, SmallVec};
+
+use std::{fmt, iter};
+
+/// When the main Rust parser encounters a syntax-extension invocation, it
+/// parses the arguments to the invocation as a token tree. This is a very
+/// loose structure, such that all sorts of different AST fragments can
+/// be passed to syntax extensions using a uniform type.
+///
+/// If the syntax extension is an MBE macro, it will attempt to match its
+/// LHS token tree against the provided token tree, and if it finds a
+/// match, will transcribe the RHS token tree, splicing in any captured
+/// `macro_parser::matched_nonterminals` into the `SubstNt`s it finds.
+///
+/// The RHS of an MBE macro is the only place `SubstNt`s are substituted.
+/// Nothing special happens to misnamed or misplaced `SubstNt`s.
+#[derive(Debug, Clone, PartialEq, Encodable, Decodable, HashStable_Generic)]
+pub enum TokenTree {
+ /// A single token.
+ Token(Token, Spacing),
+ /// A delimited sequence of token trees.
+ Delimited(DelimSpan, Delimiter, TokenStream),
+}
+
+// This type is used a lot. Make sure it doesn't unintentionally get bigger.
+#[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))]
+rustc_data_structures::static_assert_size!(TokenTree, 32);
+
+// Ensure all fields of `TokenTree` is `Send` and `Sync`.
+#[cfg(parallel_compiler)]
+fn _dummy()
+where
+ Token: Send + Sync,
+ DelimSpan: Send + Sync,
+ Delimiter: Send + Sync,
+ TokenStream: Send + Sync,
+{
+}
+
+impl TokenTree {
+ /// Checks if this `TokenTree` is equal to the other, regardless of span information.
+ pub fn eq_unspanned(&self, other: &TokenTree) -> bool {
+ match (self, other) {
+ (TokenTree::Token(token, _), TokenTree::Token(token2, _)) => token.kind == token2.kind,
+ (TokenTree::Delimited(_, delim, tts), TokenTree::Delimited(_, delim2, tts2)) => {
+ delim == delim2 && tts.eq_unspanned(&tts2)
+ }
+ _ => false,
+ }
+ }
+
+ /// Retrieves the `TokenTree`'s span.
+ pub fn span(&self) -> Span {
+ match self {
+ TokenTree::Token(token, _) => token.span,
+ TokenTree::Delimited(sp, ..) => sp.entire(),
+ }
+ }
+
+ /// Modify the `TokenTree`'s span in-place.
+ pub fn set_span(&mut self, span: Span) {
+ match self {
+ TokenTree::Token(token, _) => token.span = span,
+ TokenTree::Delimited(dspan, ..) => *dspan = DelimSpan::from_single(span),
+ }
+ }
+
+ // Create a `TokenTree::Token` with alone spacing.
+ pub fn token_alone(kind: TokenKind, span: Span) -> TokenTree {
+ TokenTree::Token(Token::new(kind, span), Spacing::Alone)
+ }
+
+ // Create a `TokenTree::Token` with joint spacing.
+ pub fn token_joint(kind: TokenKind, span: Span) -> TokenTree {
+ TokenTree::Token(Token::new(kind, span), Spacing::Joint)
+ }
+
+ pub fn uninterpolate(self) -> TokenTree {
+ match self {
+ TokenTree::Token(token, spacing) => {
+ TokenTree::Token(token.uninterpolate().into_owned(), spacing)
+ }
+ tt => tt,
+ }
+ }
+}
+
+impl<CTX> HashStable<CTX> for TokenStream
+where
+ CTX: crate::HashStableContext,
+{
+ fn hash_stable(&self, hcx: &mut CTX, hasher: &mut StableHasher) {
+ for sub_tt in self.trees() {
+ sub_tt.hash_stable(hcx, hasher);
+ }
+ }
+}
+
+pub trait CreateTokenStream: sync::Send + sync::Sync {
+ fn create_token_stream(&self) -> AttrAnnotatedTokenStream;
+}
+
+impl CreateTokenStream for AttrAnnotatedTokenStream {
+ fn create_token_stream(&self) -> AttrAnnotatedTokenStream {
+ self.clone()
+ }
+}
+
+/// A lazy version of [`TokenStream`], which defers creation
+/// of an actual `TokenStream` until it is needed.
+/// `Box` is here only to reduce the structure size.
+#[derive(Clone)]
+pub struct LazyTokenStream(Lrc<Box<dyn CreateTokenStream>>);
+
+impl LazyTokenStream {
+ pub fn new(inner: impl CreateTokenStream + 'static) -> LazyTokenStream {
+ LazyTokenStream(Lrc::new(Box::new(inner)))
+ }
+
+ pub fn create_token_stream(&self) -> AttrAnnotatedTokenStream {
+ self.0.create_token_stream()
+ }
+}
+
+impl fmt::Debug for LazyTokenStream {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ write!(f, "LazyTokenStream({:?})", self.create_token_stream())
+ }
+}
+
+impl<S: Encoder> Encodable<S> for LazyTokenStream {
+ fn encode(&self, s: &mut S) {
+ // Used by AST json printing.
+ Encodable::encode(&self.create_token_stream(), s);
+ }
+}
+
+impl<D: Decoder> Decodable<D> for LazyTokenStream {
+ fn decode(_d: &mut D) -> Self {
+ panic!("Attempted to decode LazyTokenStream");
+ }
+}
+
+impl<CTX> HashStable<CTX> for LazyTokenStream {
+ fn hash_stable(&self, _hcx: &mut CTX, _hasher: &mut StableHasher) {
+ panic!("Attempted to compute stable hash for LazyTokenStream");
+ }
+}
+
+/// A `AttrAnnotatedTokenStream` is similar to a `TokenStream`, but with extra
+/// information about the tokens for attribute targets. This is used
+/// during expansion to perform early cfg-expansion, and to process attributes
+/// during proc-macro invocations.
+#[derive(Clone, Debug, Default, Encodable, Decodable)]
+pub struct AttrAnnotatedTokenStream(pub Lrc<Vec<(AttrAnnotatedTokenTree, Spacing)>>);
+
+/// Like `TokenTree`, but for `AttrAnnotatedTokenStream`
+#[derive(Clone, Debug, Encodable, Decodable)]
+pub enum AttrAnnotatedTokenTree {
+ Token(Token),
+ Delimited(DelimSpan, Delimiter, AttrAnnotatedTokenStream),
+ /// Stores the attributes for an attribute target,
+ /// along with the tokens for that attribute target.
+ /// See `AttributesData` for more information
+ Attributes(AttributesData),
+}
+
+impl AttrAnnotatedTokenStream {
+ pub fn new(tokens: Vec<(AttrAnnotatedTokenTree, Spacing)>) -> AttrAnnotatedTokenStream {
+ AttrAnnotatedTokenStream(Lrc::new(tokens))
+ }
+
+ /// Converts this `AttrAnnotatedTokenStream` to a plain `TokenStream
+ /// During conversion, `AttrAnnotatedTokenTree::Attributes` get 'flattened'
+ /// back to a `TokenStream` of the form `outer_attr attr_target`.
+ /// If there are inner attributes, they are inserted into the proper
+ /// place in the attribute target tokens.
+ pub fn to_tokenstream(&self) -> TokenStream {
+ let trees: Vec<_> = self
+ .0
+ .iter()
+ .flat_map(|tree| match &tree.0 {
+ AttrAnnotatedTokenTree::Token(inner) => {
+ smallvec![TokenTree::Token(inner.clone(), tree.1)].into_iter()
+ }
+ AttrAnnotatedTokenTree::Delimited(span, delim, stream) => {
+ smallvec![TokenTree::Delimited(*span, *delim, stream.to_tokenstream()),]
+ .into_iter()
+ }
+ AttrAnnotatedTokenTree::Attributes(data) => {
+ let mut outer_attrs = Vec::new();
+ let mut inner_attrs = Vec::new();
+ for attr in &data.attrs {
+ match attr.style {
+ crate::AttrStyle::Outer => {
+ outer_attrs.push(attr);
+ }
+ crate::AttrStyle::Inner => {
+ inner_attrs.push(attr);
+ }
+ }
+ }
+
+ let mut target_tokens: Vec<_> = data
+ .tokens
+ .create_token_stream()
+ .to_tokenstream()
+ .0
+ .iter()
+ .cloned()
+ .collect();
+ if !inner_attrs.is_empty() {
+ let mut found = false;
+ // Check the last two trees (to account for a trailing semi)
+ for tree in target_tokens.iter_mut().rev().take(2) {
+ if let TokenTree::Delimited(span, delim, delim_tokens) = tree {
+ // Inner attributes are only supported on extern blocks, functions, impls,
+ // and modules. All of these have their inner attributes placed at
+ // the beginning of the rightmost outermost braced group:
+ // e.g. fn foo() { #![my_attr} }
+ //
+ // Therefore, we can insert them back into the right location
+ // without needing to do any extra position tracking.
+ //
+ // Note: Outline modules are an exception - they can
+ // have attributes like `#![my_attr]` at the start of a file.
+ // Support for custom attributes in this position is not
+ // properly implemented - we always synthesize fake tokens,
+ // so we never reach this code.
+
+ let mut builder = TokenStreamBuilder::new();
+ for inner_attr in inner_attrs {
+ builder.push(inner_attr.tokens().to_tokenstream());
+ }
+ builder.push(delim_tokens.clone());
+ *tree = TokenTree::Delimited(*span, *delim, builder.build());
+ found = true;
+ break;
+ }
+ }
+
+ assert!(
+ found,
+ "Failed to find trailing delimited group in: {:?}",
+ target_tokens
+ );
+ }
+ let mut flat: SmallVec<[_; 1]> = SmallVec::new();
+ for attr in outer_attrs {
+ // FIXME: Make this more efficient
+ flat.extend(attr.tokens().to_tokenstream().0.clone().iter().cloned());
+ }
+ flat.extend(target_tokens);
+ flat.into_iter()
+ }
+ })
+ .collect();
+ TokenStream::new(trees)
+ }
+}
+
+/// Stores the tokens for an attribute target, along
+/// with its attributes.
+///
+/// This is constructed during parsing when we need to capture
+/// tokens.
+///
+/// For example, `#[cfg(FALSE)] struct Foo {}` would
+/// have an `attrs` field containing the `#[cfg(FALSE)]` attr,
+/// and a `tokens` field storing the (unparsed) tokens `struct Foo {}`
+#[derive(Clone, Debug, Encodable, Decodable)]
+pub struct AttributesData {
+ /// Attributes, both outer and inner.
+ /// These are stored in the original order that they were parsed in.
+ pub attrs: AttrVec,
+ /// The underlying tokens for the attribute target that `attrs`
+ /// are applied to
+ pub tokens: LazyTokenStream,
+}
+
+/// A `TokenStream` is an abstract sequence of tokens, organized into [`TokenTree`]s.
+///
+/// The goal is for procedural macros to work with `TokenStream`s and `TokenTree`s
+/// instead of a representation of the abstract syntax tree.
+/// Today's `TokenTree`s can still contain AST via `token::Interpolated` for
+/// backwards compatibility.
+#[derive(Clone, Debug, Default, Encodable, Decodable)]
+pub struct TokenStream(pub(crate) Lrc<Vec<TokenTree>>);
+
+// `TokenStream` is used a lot. Make sure it doesn't unintentionally get bigger.
+#[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))]
+rustc_data_structures::static_assert_size!(TokenStream, 8);
+
+#[derive(Clone, Copy, Debug, PartialEq, Encodable, Decodable, HashStable_Generic)]
+pub enum Spacing {
+ Alone,
+ Joint,
+}
+
+impl TokenStream {
+ /// Given a `TokenStream` with a `Stream` of only two arguments, return a new `TokenStream`
+ /// separating the two arguments with a comma for diagnostic suggestions.
+ pub fn add_comma(&self) -> Option<(TokenStream, Span)> {
+ // Used to suggest if a user writes `foo!(a b);`
+ let mut suggestion = None;
+ let mut iter = self.0.iter().enumerate().peekable();
+ while let Some((pos, ts)) = iter.next() {
+ if let Some((_, next)) = iter.peek() {
+ let sp = match (&ts, &next) {
+ (_, TokenTree::Token(Token { kind: token::Comma, .. }, _)) => continue,
+ (
+ TokenTree::Token(token_left, Spacing::Alone),
+ TokenTree::Token(token_right, _),
+ ) if ((token_left.is_ident() && !token_left.is_reserved_ident())
+ || token_left.is_lit())
+ && ((token_right.is_ident() && !token_right.is_reserved_ident())
+ || token_right.is_lit()) =>
+ {
+ token_left.span
+ }
+ (TokenTree::Delimited(sp, ..), _) => sp.entire(),
+ _ => continue,
+ };
+ let sp = sp.shrink_to_hi();
+ let comma = TokenTree::token_alone(token::Comma, sp);
+ suggestion = Some((pos, comma, sp));
+ }
+ }
+ if let Some((pos, comma, sp)) = suggestion {
+ let mut new_stream = Vec::with_capacity(self.0.len() + 1);
+ let parts = self.0.split_at(pos + 1);
+ new_stream.extend_from_slice(parts.0);
+ new_stream.push(comma);
+ new_stream.extend_from_slice(parts.1);
+ return Some((TokenStream::new(new_stream), sp));
+ }
+ None
+ }
+}
+
+impl From<(AttrAnnotatedTokenTree, Spacing)> for AttrAnnotatedTokenStream {
+ fn from((tree, spacing): (AttrAnnotatedTokenTree, Spacing)) -> AttrAnnotatedTokenStream {
+ AttrAnnotatedTokenStream::new(vec![(tree, spacing)])
+ }
+}
+
+impl iter::FromIterator<TokenTree> for TokenStream {
+ fn from_iter<I: IntoIterator<Item = TokenTree>>(iter: I) -> Self {
+ TokenStream::new(iter.into_iter().collect::<Vec<TokenTree>>())
+ }
+}
+
+impl Eq for TokenStream {}
+
+impl PartialEq<TokenStream> for TokenStream {
+ fn eq(&self, other: &TokenStream) -> bool {
+ self.trees().eq(other.trees())
+ }
+}
+
+impl TokenStream {
+ pub fn new(streams: Vec<TokenTree>) -> TokenStream {
+ TokenStream(Lrc::new(streams))
+ }
+
+ pub fn is_empty(&self) -> bool {
+ self.0.is_empty()
+ }
+
+ pub fn len(&self) -> usize {
+ self.0.len()
+ }
+
+ pub fn trees(&self) -> CursorRef<'_> {
+ CursorRef::new(self)
+ }
+
+ pub fn into_trees(self) -> Cursor {
+ Cursor::new(self)
+ }
+
+ /// Compares two `TokenStream`s, checking equality without regarding span information.
+ pub fn eq_unspanned(&self, other: &TokenStream) -> bool {
+ let mut t1 = self.trees();
+ let mut t2 = other.trees();
+ for (t1, t2) in iter::zip(&mut t1, &mut t2) {
+ if !t1.eq_unspanned(&t2) {
+ return false;
+ }
+ }
+ t1.next().is_none() && t2.next().is_none()
+ }
+
+ pub fn map_enumerated<F: FnMut(usize, &TokenTree) -> TokenTree>(self, mut f: F) -> TokenStream {
+ TokenStream(Lrc::new(self.0.iter().enumerate().map(|(i, tree)| f(i, tree)).collect()))
+ }
+
+ fn opt_from_ast(node: &(impl HasAttrs + HasTokens)) -> Option<TokenStream> {
+ let tokens = node.tokens()?;
+ let attrs = node.attrs();
+ let attr_annotated = if attrs.is_empty() {
+ tokens.create_token_stream()
+ } else {
+ let attr_data = AttributesData { attrs: attrs.to_vec().into(), tokens: tokens.clone() };
+ AttrAnnotatedTokenStream::new(vec![(
+ AttrAnnotatedTokenTree::Attributes(attr_data),
+ Spacing::Alone,
+ )])
+ };
+ Some(attr_annotated.to_tokenstream())
+ }
+
+ // Create a token stream containing a single token with alone spacing.
+ pub fn token_alone(kind: TokenKind, span: Span) -> TokenStream {
+ TokenStream::new(vec![TokenTree::token_alone(kind, span)])
+ }
+
+ // Create a token stream containing a single token with joint spacing.
+ pub fn token_joint(kind: TokenKind, span: Span) -> TokenStream {
+ TokenStream::new(vec![TokenTree::token_joint(kind, span)])
+ }
+
+ // Create a token stream containing a single `Delimited`.
+ pub fn delimited(span: DelimSpan, delim: Delimiter, tts: TokenStream) -> TokenStream {
+ TokenStream::new(vec![TokenTree::Delimited(span, delim, tts)])
+ }
+
+ pub fn from_ast(node: &(impl HasAttrs + HasSpan + HasTokens + fmt::Debug)) -> TokenStream {
+ TokenStream::opt_from_ast(node)
+ .unwrap_or_else(|| panic!("missing tokens for node at {:?}: {:?}", node.span(), node))
+ }
+
+ pub fn from_nonterminal_ast(nt: &Nonterminal) -> TokenStream {
+ match nt {
+ Nonterminal::NtIdent(ident, is_raw) => {
+ TokenStream::token_alone(token::Ident(ident.name, *is_raw), ident.span)
+ }
+ Nonterminal::NtLifetime(ident) => {
+ TokenStream::token_alone(token::Lifetime(ident.name), ident.span)
+ }
+ Nonterminal::NtItem(item) => TokenStream::from_ast(item),
+ Nonterminal::NtBlock(block) => TokenStream::from_ast(block),
+ Nonterminal::NtStmt(stmt) if let StmtKind::Empty = stmt.kind => {
+ // FIXME: Properly collect tokens for empty statements.
+ TokenStream::token_alone(token::Semi, stmt.span)
+ }
+ Nonterminal::NtStmt(stmt) => TokenStream::from_ast(stmt),
+ Nonterminal::NtPat(pat) => TokenStream::from_ast(pat),
+ Nonterminal::NtTy(ty) => TokenStream::from_ast(ty),
+ Nonterminal::NtMeta(attr) => TokenStream::from_ast(attr),
+ Nonterminal::NtPath(path) => TokenStream::from_ast(path),
+ Nonterminal::NtVis(vis) => TokenStream::from_ast(vis),
+ Nonterminal::NtExpr(expr) | Nonterminal::NtLiteral(expr) => TokenStream::from_ast(expr),
+ }
+ }
+
+ fn flatten_token(token: &Token, spacing: Spacing) -> TokenTree {
+ match &token.kind {
+ token::Interpolated(nt) if let token::NtIdent(ident, is_raw) = **nt => {
+ TokenTree::Token(Token::new(token::Ident(ident.name, is_raw), ident.span), spacing)
+ }
+ token::Interpolated(nt) => TokenTree::Delimited(
+ DelimSpan::from_single(token.span),
+ Delimiter::Invisible,
+ TokenStream::from_nonterminal_ast(&nt).flattened(),
+ ),
+ _ => TokenTree::Token(token.clone(), spacing),
+ }
+ }
+
+ fn flatten_token_tree(tree: &TokenTree) -> TokenTree {
+ match tree {
+ TokenTree::Token(token, spacing) => TokenStream::flatten_token(token, *spacing),
+ TokenTree::Delimited(span, delim, tts) => {
+ TokenTree::Delimited(*span, *delim, tts.flattened())
+ }
+ }
+ }
+
+ #[must_use]
+ pub fn flattened(&self) -> TokenStream {
+ fn can_skip(stream: &TokenStream) -> bool {
+ stream.trees().all(|tree| match tree {
+ TokenTree::Token(token, _) => !matches!(token.kind, token::Interpolated(_)),
+ TokenTree::Delimited(_, _, inner) => can_skip(inner),
+ })
+ }
+
+ if can_skip(self) {
+ return self.clone();
+ }
+
+ self.trees().map(|tree| TokenStream::flatten_token_tree(tree)).collect()
+ }
+}
+
+// 99.5%+ of the time we have 1 or 2 elements in this vector.
+#[derive(Clone)]
+pub struct TokenStreamBuilder(SmallVec<[TokenStream; 2]>);
+
+impl TokenStreamBuilder {
+ pub fn new() -> TokenStreamBuilder {
+ TokenStreamBuilder(SmallVec::new())
+ }
+
+ pub fn push(&mut self, stream: TokenStream) {
+ self.0.push(stream);
+ }
+
+ pub fn build(self) -> TokenStream {
+ let mut streams = self.0;
+ match streams.len() {
+ 0 => TokenStream::default(),
+ 1 => streams.pop().unwrap(),
+ _ => {
+ // We will extend the first stream in `streams` with the
+ // elements from the subsequent streams. This requires using
+ // `make_mut()` on the first stream, and in practice this
+ // doesn't cause cloning 99.9% of the time.
+ //
+ // One very common use case is when `streams` has two elements,
+ // where the first stream has any number of elements within
+ // (often 1, but sometimes many more) and the second stream has
+ // a single element within.
+
+ // Determine how much the first stream will be extended.
+ // Needed to avoid quadratic blow up from on-the-fly
+ // reallocations (#57735).
+ let num_appends = streams.iter().skip(1).map(|ts| ts.len()).sum();
+
+ // Get the first stream, which will become the result stream.
+ // If it's `None`, create an empty stream.
+ let mut iter = streams.drain(..);
+ let mut res_stream_lrc = iter.next().unwrap().0;
+
+ // Append the subsequent elements to the result stream, after
+ // reserving space for them.
+ let res_vec_mut = Lrc::make_mut(&mut res_stream_lrc);
+ res_vec_mut.reserve(num_appends);
+ for stream in iter {
+ let stream_iter = stream.0.iter().cloned();
+
+ // If (a) `res_mut_vec` is not empty and the last tree
+ // within it is a token tree marked with `Joint`, and (b)
+ // `stream` is not empty and the first tree within it is a
+ // token tree, and (c) the two tokens can be glued
+ // together...
+ if let Some(TokenTree::Token(last_tok, Spacing::Joint)) = res_vec_mut.last()
+ && let Some(TokenTree::Token(tok, spacing)) = stream.0.first()
+ && let Some(glued_tok) = last_tok.glue(&tok)
+ {
+ // ...then overwrite the last token tree in
+ // `res_vec_mut` with the glued token, and skip the
+ // first token tree from `stream`.
+ *res_vec_mut.last_mut().unwrap() = TokenTree::Token(glued_tok, *spacing);
+ res_vec_mut.extend(stream_iter.skip(1));
+ } else {
+ // Append all of `stream`.
+ res_vec_mut.extend(stream_iter);
+ }
+ }
+
+ TokenStream(res_stream_lrc)
+ }
+ }
+ }
+}
+
+/// By-reference iterator over a [`TokenStream`].
+#[derive(Clone)]
+pub struct CursorRef<'t> {
+ stream: &'t TokenStream,
+ index: usize,
+}
+
+impl<'t> CursorRef<'t> {
+ fn new(stream: &'t TokenStream) -> Self {
+ CursorRef { stream, index: 0 }
+ }
+
+ pub fn look_ahead(&self, n: usize) -> Option<&TokenTree> {
+ self.stream.0.get(self.index + n)
+ }
+}
+
+impl<'t> Iterator for CursorRef<'t> {
+ type Item = &'t TokenTree;
+
+ fn next(&mut self) -> Option<&'t TokenTree> {
+ self.stream.0.get(self.index).map(|tree| {
+ self.index += 1;
+ tree
+ })
+ }
+}
+
+/// Owning by-value iterator over a [`TokenStream`].
+// FIXME: Many uses of this can be replaced with by-reference iterator to avoid clones.
+#[derive(Clone)]
+pub struct Cursor {
+ pub stream: TokenStream,
+ index: usize,
+}
+
+impl Iterator for Cursor {
+ type Item = TokenTree;
+
+ fn next(&mut self) -> Option<TokenTree> {
+ self.stream.0.get(self.index).map(|tree| {
+ self.index += 1;
+ tree.clone()
+ })
+ }
+}
+
+impl Cursor {
+ fn new(stream: TokenStream) -> Self {
+ Cursor { stream, index: 0 }
+ }
+
+ #[inline]
+ pub fn next_ref(&mut self) -> Option<&TokenTree> {
+ self.stream.0.get(self.index).map(|tree| {
+ self.index += 1;
+ tree
+ })
+ }
+
+ pub fn look_ahead(&self, n: usize) -> Option<&TokenTree> {
+ self.stream.0.get(self.index + n)
+ }
+}
+
+#[derive(Debug, Copy, Clone, PartialEq, Encodable, Decodable, HashStable_Generic)]
+pub struct DelimSpan {
+ pub open: Span,
+ pub close: Span,
+}
+
+impl DelimSpan {
+ pub fn from_single(sp: Span) -> Self {
+ DelimSpan { open: sp, close: sp }
+ }
+
+ pub fn from_pair(open: Span, close: Span) -> Self {
+ DelimSpan { open, close }
+ }
+
+ pub fn dummy() -> Self {
+ Self::from_single(DUMMY_SP)
+ }
+
+ pub fn entire(self) -> Span {
+ self.open.with_hi(self.close.hi())
+ }
+}
diff --git a/compiler/rustc_ast/src/util/classify.rs b/compiler/rustc_ast/src/util/classify.rs
new file mode 100644
index 000000000..6ea3db6d3
--- /dev/null
+++ b/compiler/rustc_ast/src/util/classify.rs
@@ -0,0 +1,52 @@
+//! Routines the parser uses to classify AST nodes
+
+// Predicates on exprs and stmts that the pretty-printer and parser use
+
+use crate::ast;
+
+/// Does this expression require a semicolon to be treated
+/// as a statement? The negation of this: 'can this expression
+/// be used as a statement without a semicolon' -- is used
+/// as an early-bail-out in the parser so that, for instance,
+/// if true {...} else {...}
+/// |x| 5
+/// isn't parsed as (if true {...} else {...} | x) | 5
+pub fn expr_requires_semi_to_be_stmt(e: &ast::Expr) -> bool {
+ !matches!(
+ e.kind,
+ ast::ExprKind::If(..)
+ | ast::ExprKind::Match(..)
+ | ast::ExprKind::Block(..)
+ | ast::ExprKind::While(..)
+ | ast::ExprKind::Loop(..)
+ | ast::ExprKind::ForLoop(..)
+ | ast::ExprKind::TryBlock(..)
+ )
+}
+
+/// If an expression ends with `}`, returns the innermost expression ending in the `}`
+pub fn expr_trailing_brace(mut expr: &ast::Expr) -> Option<&ast::Expr> {
+ use ast::ExprKind::*;
+
+ loop {
+ match &expr.kind {
+ AddrOf(_, _, e)
+ | Assign(_, e, _)
+ | AssignOp(_, _, e)
+ | Binary(_, _, e)
+ | Box(e)
+ | Break(_, Some(e))
+ | Closure(.., e, _)
+ | Let(_, e, _)
+ | Range(_, Some(e), _)
+ | Ret(Some(e))
+ | Unary(_, e)
+ | Yield(Some(e)) => {
+ expr = e;
+ }
+ Async(..) | Block(..) | ForLoop(..) | If(..) | Loop(..) | Match(..) | Struct(..)
+ | TryBlock(..) | While(..) => break Some(expr),
+ _ => break None,
+ }
+ }
+}
diff --git a/compiler/rustc_ast/src/util/comments.rs b/compiler/rustc_ast/src/util/comments.rs
new file mode 100644
index 000000000..c96474ccb
--- /dev/null
+++ b/compiler/rustc_ast/src/util/comments.rs
@@ -0,0 +1,255 @@
+use crate::token::CommentKind;
+use rustc_span::source_map::SourceMap;
+use rustc_span::{BytePos, CharPos, FileName, Pos, Symbol};
+
+#[cfg(test)]
+mod tests;
+
+#[derive(Clone, Copy, PartialEq, Debug)]
+pub enum CommentStyle {
+ /// No code on either side of each line of the comment
+ Isolated,
+ /// Code exists to the left of the comment
+ Trailing,
+ /// Code before /* foo */ and after the comment
+ Mixed,
+ /// Just a manual blank line "\n\n", for layout
+ BlankLine,
+}
+
+#[derive(Clone)]
+pub struct Comment {
+ pub style: CommentStyle,
+ pub lines: Vec<String>,
+ pub pos: BytePos,
+}
+
+/// A fast conservative estimate on whether the string can contain documentation links.
+/// A pair of square brackets `[]` must exist in the string, but we only search for the
+/// opening bracket because brackets always go in pairs in practice.
+#[inline]
+pub fn may_have_doc_links(s: &str) -> bool {
+ s.contains('[')
+}
+
+/// Makes a doc string more presentable to users.
+/// Used by rustdoc and perhaps other tools, but not by rustc.
+pub fn beautify_doc_string(data: Symbol, kind: CommentKind) -> Symbol {
+ fn get_vertical_trim(lines: &[&str]) -> Option<(usize, usize)> {
+ let mut i = 0;
+ let mut j = lines.len();
+ // first line of all-stars should be omitted
+ if !lines.is_empty() && lines[0].chars().all(|c| c == '*') {
+ i += 1;
+ }
+
+ // like the first, a last line of all stars should be omitted
+ if j > i && !lines[j - 1].is_empty() && lines[j - 1].chars().all(|c| c == '*') {
+ j -= 1;
+ }
+
+ if i != 0 || j != lines.len() { Some((i, j)) } else { None }
+ }
+
+ fn get_horizontal_trim<'a>(lines: &'a [&str], kind: CommentKind) -> Option<String> {
+ let mut i = usize::MAX;
+ let mut first = true;
+
+ // 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;
+ }
+ &lines[i..j]
+ } else {
+ lines
+ };
+
+ for line in lines {
+ for (j, c) in line.chars().enumerate() {
+ if j > i || !"* \t".contains(c) {
+ return None;
+ }
+ if c == '*' {
+ if first {
+ i = j;
+ first = false;
+ } else if i != j {
+ return None;
+ }
+ break;
+ }
+ }
+ if i >= line.len() {
+ return None;
+ }
+ }
+ if lines.is_empty() { None } else { Some(lines[0][..i].into()) }
+ }
+
+ let data_s = data.as_str();
+ if data_s.contains('\n') {
+ let mut lines = data_s.lines().collect::<Vec<&str>>();
+ let mut changes = false;
+ let lines = if let Some((i, j)) = get_vertical_trim(&lines) {
+ changes = true;
+ // remove whitespace-only lines from the start/end of lines
+ &mut lines[i..j]
+ } else {
+ &mut lines
+ };
+ if let Some(horizontal) = get_horizontal_trim(&lines, kind) {
+ changes = true;
+ // remove a "[ \t]*\*" block from each line, if possible
+ for line in lines.iter_mut() {
+ if let Some(tmp) = line.strip_prefix(&horizontal) {
+ *line = tmp;
+ if kind == CommentKind::Block
+ && (*line == "*" || line.starts_with("* ") || line.starts_with("**"))
+ {
+ *line = &line[1..];
+ }
+ }
+ }
+ }
+ if changes {
+ return Symbol::intern(&lines.join("\n"));
+ }
+ }
+ data
+}
+
+/// Returns `None` if the first `col` chars of `s` contain a non-whitespace char.
+/// Otherwise returns `Some(k)` where `k` is first char offset after that leading
+/// whitespace. Note that `k` may be outside bounds of `s`.
+fn all_whitespace(s: &str, col: CharPos) -> Option<usize> {
+ let mut idx = 0;
+ for (i, ch) in s.char_indices().take(col.to_usize()) {
+ if !ch.is_whitespace() {
+ return None;
+ }
+ idx = i + ch.len_utf8();
+ }
+ Some(idx)
+}
+
+fn trim_whitespace_prefix(s: &str, col: CharPos) -> &str {
+ let len = s.len();
+ match all_whitespace(&s, col) {
+ Some(col) => {
+ if col < len {
+ &s[col..]
+ } else {
+ ""
+ }
+ }
+ None => s,
+ }
+}
+
+fn split_block_comment_into_lines(text: &str, col: CharPos) -> Vec<String> {
+ let mut res: Vec<String> = vec![];
+ let mut lines = text.lines();
+ // just push the first line
+ res.extend(lines.next().map(|it| it.to_string()));
+ // for other lines, strip common whitespace prefix
+ for line in lines {
+ res.push(trim_whitespace_prefix(line, col).to_string())
+ }
+ res
+}
+
+// it appears this function is called only from pprust... that's
+// probably not a good thing.
+pub fn gather_comments(sm: &SourceMap, path: FileName, src: String) -> Vec<Comment> {
+ let sm = SourceMap::new(sm.path_mapping().clone());
+ let source_file = sm.new_source_file(path, src);
+ let text = (*source_file.src.as_ref().unwrap()).clone();
+
+ let text: &str = text.as_str();
+ let start_bpos = source_file.start_pos;
+ let mut pos = 0;
+ let mut comments: Vec<Comment> = Vec::new();
+ let mut code_to_the_left = false;
+
+ if let Some(shebang_len) = rustc_lexer::strip_shebang(text) {
+ comments.push(Comment {
+ style: CommentStyle::Isolated,
+ lines: vec![text[..shebang_len].to_string()],
+ pos: start_bpos,
+ });
+ pos += shebang_len;
+ }
+
+ for token in rustc_lexer::tokenize(&text[pos..]) {
+ let token_text = &text[pos..pos + token.len as usize];
+ match token.kind {
+ rustc_lexer::TokenKind::Whitespace => {
+ if let Some(mut idx) = token_text.find('\n') {
+ code_to_the_left = false;
+ while let Some(next_newline) = &token_text[idx + 1..].find('\n') {
+ idx += 1 + next_newline;
+ comments.push(Comment {
+ style: CommentStyle::BlankLine,
+ lines: vec![],
+ pos: start_bpos + BytePos((pos + idx) as u32),
+ });
+ }
+ }
+ }
+ rustc_lexer::TokenKind::BlockComment { doc_style, .. } => {
+ if doc_style.is_none() {
+ let code_to_the_right = !matches!(
+ text[pos + token.len as usize..].chars().next(),
+ Some('\r' | '\n')
+ );
+ let style = match (code_to_the_left, code_to_the_right) {
+ (_, true) => CommentStyle::Mixed,
+ (false, false) => CommentStyle::Isolated,
+ (true, false) => CommentStyle::Trailing,
+ };
+
+ // Count the number of chars since the start of the line by rescanning.
+ let pos_in_file = start_bpos + BytePos(pos as u32);
+ let line_begin_in_file = source_file.line_begin_pos(pos_in_file);
+ let line_begin_pos = (line_begin_in_file - start_bpos).to_usize();
+ let col = CharPos(text[line_begin_pos..pos].chars().count());
+
+ let lines = split_block_comment_into_lines(token_text, col);
+ comments.push(Comment { style, lines, pos: pos_in_file })
+ }
+ }
+ rustc_lexer::TokenKind::LineComment { doc_style } => {
+ if doc_style.is_none() {
+ comments.push(Comment {
+ style: if code_to_the_left {
+ CommentStyle::Trailing
+ } else {
+ CommentStyle::Isolated
+ },
+ lines: vec![token_text.to_string()],
+ pos: start_bpos + BytePos(pos as u32),
+ })
+ }
+ }
+ _ => {
+ code_to_the_left = true;
+ }
+ }
+ pos += token.len as usize;
+ }
+
+ comments
+}
diff --git a/compiler/rustc_ast/src/util/comments/tests.rs b/compiler/rustc_ast/src/util/comments/tests.rs
new file mode 100644
index 000000000..11d50603a
--- /dev/null
+++ b/compiler/rustc_ast/src/util/comments/tests.rs
@@ -0,0 +1,61 @@
+use super::*;
+use rustc_span::create_default_session_globals_then;
+
+#[test]
+fn test_block_doc_comment_1() {
+ create_default_session_globals_then(|| {
+ let comment = "\n * Test \n ** Test\n * Test\n";
+ let stripped = beautify_doc_string(Symbol::intern(comment), CommentKind::Block);
+ assert_eq!(stripped.as_str(), " Test \n* Test\n Test");
+ })
+}
+
+#[test]
+fn test_block_doc_comment_2() {
+ create_default_session_globals_then(|| {
+ let comment = "\n * Test\n * Test\n";
+ let stripped = beautify_doc_string(Symbol::intern(comment), CommentKind::Block);
+ assert_eq!(stripped.as_str(), " Test\n Test");
+ })
+}
+
+#[test]
+fn test_block_doc_comment_3() {
+ create_default_session_globals_then(|| {
+ let comment = "\n let a: *i32;\n *a = 5;\n";
+ let stripped = beautify_doc_string(Symbol::intern(comment), CommentKind::Block);
+ assert_eq!(stripped.as_str(), "let a: *i32;\n*a = 5;");
+ })
+}
+
+#[test]
+fn test_line_doc_comment() {
+ create_default_session_globals_then(|| {
+ let stripped = beautify_doc_string(Symbol::intern(" test"), CommentKind::Line);
+ assert_eq!(stripped.as_str(), " test");
+ let stripped = beautify_doc_string(Symbol::intern("! test"), CommentKind::Line);
+ assert_eq!(stripped.as_str(), "! test");
+ let stripped = beautify_doc_string(Symbol::intern("test"), CommentKind::Line);
+ assert_eq!(stripped.as_str(), "test");
+ let stripped = beautify_doc_string(Symbol::intern("!test"), CommentKind::Line);
+ assert_eq!(stripped.as_str(), "!test");
+ })
+}
+
+#[test]
+fn test_doc_blocks() {
+ create_default_session_globals_then(|| {
+ let stripped =
+ beautify_doc_string(Symbol::intern(" # Returns\n *\n "), CommentKind::Block);
+ assert_eq!(stripped.as_str(), " # Returns\n\n");
+
+ let stripped = beautify_doc_string(
+ Symbol::intern("\n * # Returns\n *\n "),
+ CommentKind::Block,
+ );
+ assert_eq!(stripped.as_str(), " # Returns\n\n");
+
+ let stripped = beautify_doc_string(Symbol::intern("\n * a\n "), CommentKind::Block);
+ assert_eq!(stripped.as_str(), " a\n");
+ })
+}
diff --git a/compiler/rustc_ast/src/util/literal.rs b/compiler/rustc_ast/src/util/literal.rs
new file mode 100644
index 000000000..9c18f55c0
--- /dev/null
+++ b/compiler/rustc_ast/src/util/literal.rs
@@ -0,0 +1,336 @@
+//! Code related to parsing literals.
+
+use crate::ast::{self, Lit, LitKind};
+use crate::token::{self, Token};
+
+use rustc_lexer::unescape::{unescape_byte, unescape_char};
+use rustc_lexer::unescape::{unescape_byte_literal, unescape_literal, Mode};
+use rustc_span::symbol::{kw, sym, Symbol};
+use rustc_span::Span;
+
+use std::ascii;
+use tracing::debug;
+
+pub enum LitError {
+ NotLiteral,
+ LexerError,
+ InvalidSuffix,
+ InvalidIntSuffix,
+ InvalidFloatSuffix,
+ NonDecimalFloat(u32),
+ IntTooLarge,
+}
+
+impl LitKind {
+ /// Converts literal token into a semantic literal.
+ pub fn from_lit_token(lit: token::Lit) -> Result<LitKind, LitError> {
+ let token::Lit { kind, symbol, suffix } = lit;
+ if suffix.is_some() && !kind.may_have_suffix() {
+ return Err(LitError::InvalidSuffix);
+ }
+
+ Ok(match kind {
+ token::Bool => {
+ assert!(symbol.is_bool_lit());
+ LitKind::Bool(symbol == kw::True)
+ }
+ token::Byte => {
+ return unescape_byte(symbol.as_str())
+ .map(LitKind::Byte)
+ .map_err(|_| LitError::LexerError);
+ }
+ token::Char => {
+ return unescape_char(symbol.as_str())
+ .map(LitKind::Char)
+ .map_err(|_| LitError::LexerError);
+ }
+
+ // There are some valid suffixes for integer and float literals,
+ // so all the handling is done internally.
+ token::Integer => return integer_lit(symbol, suffix),
+ token::Float => return float_lit(symbol, suffix),
+
+ token::Str => {
+ // If there are no characters requiring special treatment we can
+ // reuse the symbol from the token. Otherwise, we must generate a
+ // new symbol because the string in the LitKind is different to the
+ // string in the token.
+ let s = symbol.as_str();
+ let symbol = if s.contains(&['\\', '\r']) {
+ let mut buf = String::with_capacity(s.len());
+ let mut error = Ok(());
+ // Force-inlining here is aggressive but the closure is
+ // called on every char in the string, so it can be
+ // hot in programs with many long strings.
+ unescape_literal(
+ &s,
+ Mode::Str,
+ &mut #[inline(always)]
+ |_, unescaped_char| match unescaped_char {
+ Ok(c) => buf.push(c),
+ Err(err) => {
+ if err.is_fatal() {
+ error = Err(LitError::LexerError);
+ }
+ }
+ },
+ );
+ error?;
+ Symbol::intern(&buf)
+ } else {
+ symbol
+ };
+ LitKind::Str(symbol, ast::StrStyle::Cooked)
+ }
+ token::StrRaw(n) => {
+ // Ditto.
+ let s = symbol.as_str();
+ let symbol =
+ if s.contains('\r') {
+ let mut buf = String::with_capacity(s.len());
+ let mut error = Ok(());
+ unescape_literal(&s, Mode::RawStr, &mut |_, unescaped_char| {
+ match unescaped_char {
+ Ok(c) => buf.push(c),
+ Err(err) => {
+ if err.is_fatal() {
+ error = Err(LitError::LexerError);
+ }
+ }
+ }
+ });
+ error?;
+ Symbol::intern(&buf)
+ } else {
+ symbol
+ };
+ LitKind::Str(symbol, ast::StrStyle::Raw(n))
+ }
+ token::ByteStr => {
+ let s = symbol.as_str();
+ let mut buf = Vec::with_capacity(s.len());
+ let mut error = Ok(());
+ unescape_byte_literal(&s, Mode::ByteStr, &mut |_, unescaped_byte| {
+ match unescaped_byte {
+ Ok(c) => buf.push(c),
+ Err(err) => {
+ if err.is_fatal() {
+ error = Err(LitError::LexerError);
+ }
+ }
+ }
+ });
+ error?;
+ LitKind::ByteStr(buf.into())
+ }
+ token::ByteStrRaw(_) => {
+ let s = symbol.as_str();
+ let bytes = if s.contains('\r') {
+ let mut buf = Vec::with_capacity(s.len());
+ let mut error = Ok(());
+ unescape_byte_literal(&s, Mode::RawByteStr, &mut |_, unescaped_byte| {
+ match unescaped_byte {
+ Ok(c) => buf.push(c),
+ Err(err) => {
+ if err.is_fatal() {
+ error = Err(LitError::LexerError);
+ }
+ }
+ }
+ });
+ error?;
+ buf
+ } else {
+ symbol.to_string().into_bytes()
+ };
+
+ LitKind::ByteStr(bytes.into())
+ }
+ token::Err => LitKind::Err(symbol),
+ })
+ }
+
+ /// Attempts to recover a token from semantic literal.
+ /// This function is used when the original token doesn't exist (e.g. the literal is created
+ /// by an AST-based macro) or unavailable (e.g. from HIR pretty-printing).
+ pub fn to_lit_token(&self) -> token::Lit {
+ let (kind, symbol, suffix) = match *self {
+ LitKind::Str(symbol, ast::StrStyle::Cooked) => {
+ // Don't re-intern unless the escaped string is different.
+ let s = symbol.as_str();
+ let escaped = s.escape_default().to_string();
+ let symbol = if s == escaped { symbol } else { Symbol::intern(&escaped) };
+ (token::Str, symbol, None)
+ }
+ LitKind::Str(symbol, ast::StrStyle::Raw(n)) => (token::StrRaw(n), symbol, None),
+ LitKind::ByteStr(ref bytes) => {
+ let string = bytes
+ .iter()
+ .cloned()
+ .flat_map(ascii::escape_default)
+ .map(Into::<char>::into)
+ .collect::<String>();
+ (token::ByteStr, Symbol::intern(&string), None)
+ }
+ LitKind::Byte(byte) => {
+ let string: String = ascii::escape_default(byte).map(Into::<char>::into).collect();
+ (token::Byte, Symbol::intern(&string), None)
+ }
+ LitKind::Char(ch) => {
+ let string: String = ch.escape_default().map(Into::<char>::into).collect();
+ (token::Char, Symbol::intern(&string), None)
+ }
+ LitKind::Int(n, ty) => {
+ let suffix = match ty {
+ ast::LitIntType::Unsigned(ty) => Some(ty.name()),
+ ast::LitIntType::Signed(ty) => Some(ty.name()),
+ ast::LitIntType::Unsuffixed => None,
+ };
+ (token::Integer, sym::integer(n), suffix)
+ }
+ LitKind::Float(symbol, ty) => {
+ let suffix = match ty {
+ ast::LitFloatType::Suffixed(ty) => Some(ty.name()),
+ ast::LitFloatType::Unsuffixed => None,
+ };
+ (token::Float, symbol, suffix)
+ }
+ LitKind::Bool(value) => {
+ let symbol = if value { kw::True } else { kw::False };
+ (token::Bool, symbol, None)
+ }
+ LitKind::Err(symbol) => (token::Err, symbol, None),
+ };
+
+ token::Lit::new(kind, symbol, suffix)
+ }
+}
+
+impl Lit {
+ /// Converts literal token into an AST literal.
+ pub fn from_lit_token(token: token::Lit, span: Span) -> Result<Lit, LitError> {
+ Ok(Lit { token, kind: LitKind::from_lit_token(token)?, span })
+ }
+
+ /// Converts arbitrary token into an AST literal.
+ ///
+ /// Keep this in sync with `Token::can_begin_literal_or_bool` excluding unary negation.
+ pub fn from_token(token: &Token) -> Result<Lit, LitError> {
+ let lit = match token.uninterpolate().kind {
+ token::Ident(name, false) if name.is_bool_lit() => {
+ token::Lit::new(token::Bool, name, None)
+ }
+ token::Literal(lit) => lit,
+ token::Interpolated(ref nt) => {
+ if let token::NtExpr(expr) | token::NtLiteral(expr) = &**nt
+ && let ast::ExprKind::Lit(lit) = &expr.kind
+ {
+ return Ok(lit.clone());
+ }
+ return Err(LitError::NotLiteral);
+ }
+ _ => return Err(LitError::NotLiteral),
+ };
+
+ Lit::from_lit_token(lit, token.span)
+ }
+
+ /// Attempts to recover an AST literal from semantic literal.
+ /// This function is used when the original token doesn't exist (e.g. the literal is created
+ /// by an AST-based macro) or unavailable (e.g. from HIR pretty-printing).
+ pub fn from_lit_kind(kind: LitKind, span: Span) -> Lit {
+ Lit { token: kind.to_lit_token(), kind, span }
+ }
+
+ /// Losslessly convert an AST literal into a token.
+ pub fn to_token(&self) -> Token {
+ let kind = match self.token.kind {
+ token::Bool => token::Ident(self.token.symbol, false),
+ _ => token::Literal(self.token),
+ };
+ Token::new(kind, self.span)
+ }
+}
+
+fn strip_underscores(symbol: Symbol) -> Symbol {
+ // Do not allocate a new string unless necessary.
+ let s = symbol.as_str();
+ if s.contains('_') {
+ let mut s = s.to_string();
+ s.retain(|c| c != '_');
+ return Symbol::intern(&s);
+ }
+ symbol
+}
+
+fn filtered_float_lit(
+ symbol: Symbol,
+ suffix: Option<Symbol>,
+ base: u32,
+) -> Result<LitKind, LitError> {
+ debug!("filtered_float_lit: {:?}, {:?}, {:?}", symbol, suffix, base);
+ if base != 10 {
+ return Err(LitError::NonDecimalFloat(base));
+ }
+ Ok(match suffix {
+ Some(suf) => LitKind::Float(
+ symbol,
+ ast::LitFloatType::Suffixed(match suf {
+ sym::f32 => ast::FloatTy::F32,
+ sym::f64 => ast::FloatTy::F64,
+ _ => return Err(LitError::InvalidFloatSuffix),
+ }),
+ ),
+ None => LitKind::Float(symbol, ast::LitFloatType::Unsuffixed),
+ })
+}
+
+fn float_lit(symbol: Symbol, suffix: Option<Symbol>) -> Result<LitKind, LitError> {
+ debug!("float_lit: {:?}, {:?}", symbol, suffix);
+ filtered_float_lit(strip_underscores(symbol), suffix, 10)
+}
+
+fn integer_lit(symbol: Symbol, suffix: Option<Symbol>) -> Result<LitKind, LitError> {
+ debug!("integer_lit: {:?}, {:?}", symbol, suffix);
+ let symbol = strip_underscores(symbol);
+ let s = symbol.as_str();
+
+ let base = match s.as_bytes() {
+ [b'0', b'x', ..] => 16,
+ [b'0', b'o', ..] => 8,
+ [b'0', b'b', ..] => 2,
+ _ => 10,
+ };
+
+ let ty = match suffix {
+ Some(suf) => match suf {
+ sym::isize => ast::LitIntType::Signed(ast::IntTy::Isize),
+ sym::i8 => ast::LitIntType::Signed(ast::IntTy::I8),
+ sym::i16 => ast::LitIntType::Signed(ast::IntTy::I16),
+ sym::i32 => ast::LitIntType::Signed(ast::IntTy::I32),
+ sym::i64 => ast::LitIntType::Signed(ast::IntTy::I64),
+ sym::i128 => ast::LitIntType::Signed(ast::IntTy::I128),
+ sym::usize => ast::LitIntType::Unsigned(ast::UintTy::Usize),
+ sym::u8 => ast::LitIntType::Unsigned(ast::UintTy::U8),
+ sym::u16 => ast::LitIntType::Unsigned(ast::UintTy::U16),
+ sym::u32 => ast::LitIntType::Unsigned(ast::UintTy::U32),
+ sym::u64 => ast::LitIntType::Unsigned(ast::UintTy::U64),
+ sym::u128 => ast::LitIntType::Unsigned(ast::UintTy::U128),
+ // `1f64` and `2f32` etc. are valid float literals, and
+ // `fxxx` looks more like an invalid float literal than invalid integer literal.
+ _ if suf.as_str().starts_with('f') => return filtered_float_lit(symbol, suffix, base),
+ _ => return Err(LitError::InvalidIntSuffix),
+ },
+ _ => ast::LitIntType::Unsuffixed,
+ };
+
+ let s = &s[if base != 10 { 2 } else { 0 }..];
+ u128::from_str_radix(s, base).map(|i| LitKind::Int(i, ty)).map_err(|_| {
+ // Small bases are lexed as if they were base 10, e.g, the string
+ // might be `0b10201`. This will cause the conversion above to fail,
+ // but these kinds of errors are already reported by the lexer.
+ let from_lexer =
+ base < 10 && s.chars().any(|c| c.to_digit(10).map_or(false, |d| d >= base));
+ if from_lexer { LitError::LexerError } else { LitError::IntTooLarge }
+ })
+}
diff --git a/compiler/rustc_ast/src/util/parser.rs b/compiler/rustc_ast/src/util/parser.rs
new file mode 100644
index 000000000..74b7fe9e2
--- /dev/null
+++ b/compiler/rustc_ast/src/util/parser.rs
@@ -0,0 +1,406 @@
+use crate::ast::{self, BinOpKind};
+use crate::token::{self, BinOpToken, Token};
+use rustc_span::symbol::kw;
+
+/// Associative operator with precedence.
+///
+/// This is the enum which specifies operator precedence and fixity to the parser.
+#[derive(Copy, Clone, PartialEq, Debug)]
+pub enum AssocOp {
+ /// `+`
+ Add,
+ /// `-`
+ Subtract,
+ /// `*`
+ Multiply,
+ /// `/`
+ Divide,
+ /// `%`
+ Modulus,
+ /// `&&`
+ LAnd,
+ /// `||`
+ LOr,
+ /// `^`
+ BitXor,
+ /// `&`
+ BitAnd,
+ /// `|`
+ BitOr,
+ /// `<<`
+ ShiftLeft,
+ /// `>>`
+ ShiftRight,
+ /// `==`
+ Equal,
+ /// `<`
+ Less,
+ /// `<=`
+ LessEqual,
+ /// `!=`
+ NotEqual,
+ /// `>`
+ Greater,
+ /// `>=`
+ GreaterEqual,
+ /// `=`
+ Assign,
+ /// `?=` where ? is one of the BinOpToken
+ AssignOp(BinOpToken),
+ /// `as`
+ As,
+ /// `..` range
+ DotDot,
+ /// `..=` range
+ DotDotEq,
+ /// `:`
+ Colon,
+}
+
+#[derive(PartialEq, Debug)]
+pub enum Fixity {
+ /// The operator is left-associative
+ Left,
+ /// The operator is right-associative
+ Right,
+ /// The operator is not associative
+ None,
+}
+
+impl AssocOp {
+ /// Creates a new AssocOP from a token
+ pub fn from_token(t: &Token) -> Option<AssocOp> {
+ use AssocOp::*;
+ match t.kind {
+ token::BinOpEq(k) => Some(AssignOp(k)),
+ token::Eq => Some(Assign),
+ token::BinOp(BinOpToken::Star) => Some(Multiply),
+ token::BinOp(BinOpToken::Slash) => Some(Divide),
+ token::BinOp(BinOpToken::Percent) => Some(Modulus),
+ token::BinOp(BinOpToken::Plus) => Some(Add),
+ token::BinOp(BinOpToken::Minus) => Some(Subtract),
+ token::BinOp(BinOpToken::Shl) => Some(ShiftLeft),
+ token::BinOp(BinOpToken::Shr) => Some(ShiftRight),
+ token::BinOp(BinOpToken::And) => Some(BitAnd),
+ token::BinOp(BinOpToken::Caret) => Some(BitXor),
+ token::BinOp(BinOpToken::Or) => Some(BitOr),
+ token::Lt => Some(Less),
+ token::Le => Some(LessEqual),
+ token::Ge => Some(GreaterEqual),
+ token::Gt => Some(Greater),
+ token::EqEq => Some(Equal),
+ token::Ne => Some(NotEqual),
+ token::AndAnd => Some(LAnd),
+ token::OrOr => Some(LOr),
+ token::DotDot => Some(DotDot),
+ token::DotDotEq => Some(DotDotEq),
+ // DotDotDot is no longer supported, but we need some way to display the error
+ token::DotDotDot => Some(DotDotEq),
+ token::Colon => Some(Colon),
+ // `<-` should probably be `< -`
+ token::LArrow => Some(Less),
+ _ if t.is_keyword(kw::As) => Some(As),
+ _ => None,
+ }
+ }
+
+ /// Creates a new AssocOp from ast::BinOpKind.
+ pub fn from_ast_binop(op: BinOpKind) -> Self {
+ use AssocOp::*;
+ match op {
+ BinOpKind::Lt => Less,
+ BinOpKind::Gt => Greater,
+ BinOpKind::Le => LessEqual,
+ BinOpKind::Ge => GreaterEqual,
+ BinOpKind::Eq => Equal,
+ BinOpKind::Ne => NotEqual,
+ BinOpKind::Mul => Multiply,
+ BinOpKind::Div => Divide,
+ BinOpKind::Rem => Modulus,
+ BinOpKind::Add => Add,
+ BinOpKind::Sub => Subtract,
+ BinOpKind::Shl => ShiftLeft,
+ BinOpKind::Shr => ShiftRight,
+ BinOpKind::BitAnd => BitAnd,
+ BinOpKind::BitXor => BitXor,
+ BinOpKind::BitOr => BitOr,
+ BinOpKind::And => LAnd,
+ BinOpKind::Or => LOr,
+ }
+ }
+
+ /// Gets the precedence of this operator
+ pub fn precedence(&self) -> usize {
+ use AssocOp::*;
+ match *self {
+ As | Colon => 14,
+ Multiply | Divide | Modulus => 13,
+ Add | Subtract => 12,
+ ShiftLeft | ShiftRight => 11,
+ BitAnd => 10,
+ BitXor => 9,
+ BitOr => 8,
+ Less | Greater | LessEqual | GreaterEqual | Equal | NotEqual => 7,
+ LAnd => 6,
+ LOr => 5,
+ DotDot | DotDotEq => 4,
+ Assign | AssignOp(_) => 2,
+ }
+ }
+
+ /// Gets the fixity of this operator
+ pub fn fixity(&self) -> Fixity {
+ use AssocOp::*;
+ // NOTE: it is a bug to have an operators that has same precedence but different fixities!
+ match *self {
+ Assign | AssignOp(_) => Fixity::Right,
+ As | Multiply | Divide | Modulus | Add | Subtract | ShiftLeft | ShiftRight | BitAnd
+ | BitXor | BitOr | Less | Greater | LessEqual | GreaterEqual | Equal | NotEqual
+ | LAnd | LOr | Colon => Fixity::Left,
+ DotDot | DotDotEq => Fixity::None,
+ }
+ }
+
+ pub fn is_comparison(&self) -> bool {
+ use AssocOp::*;
+ match *self {
+ Less | Greater | LessEqual | GreaterEqual | Equal | NotEqual => true,
+ Assign | AssignOp(_) | As | Multiply | Divide | Modulus | Add | Subtract
+ | ShiftLeft | ShiftRight | BitAnd | BitXor | BitOr | LAnd | LOr | DotDot | DotDotEq
+ | Colon => false,
+ }
+ }
+
+ pub fn is_assign_like(&self) -> bool {
+ use AssocOp::*;
+ match *self {
+ Assign | AssignOp(_) => true,
+ Less | Greater | LessEqual | GreaterEqual | Equal | NotEqual | As | Multiply
+ | Divide | Modulus | Add | Subtract | ShiftLeft | ShiftRight | BitAnd | BitXor
+ | BitOr | LAnd | LOr | DotDot | DotDotEq | Colon => false,
+ }
+ }
+
+ pub fn to_ast_binop(&self) -> Option<BinOpKind> {
+ use AssocOp::*;
+ match *self {
+ Less => Some(BinOpKind::Lt),
+ Greater => Some(BinOpKind::Gt),
+ LessEqual => Some(BinOpKind::Le),
+ GreaterEqual => Some(BinOpKind::Ge),
+ Equal => Some(BinOpKind::Eq),
+ NotEqual => Some(BinOpKind::Ne),
+ Multiply => Some(BinOpKind::Mul),
+ Divide => Some(BinOpKind::Div),
+ Modulus => Some(BinOpKind::Rem),
+ Add => Some(BinOpKind::Add),
+ Subtract => Some(BinOpKind::Sub),
+ ShiftLeft => Some(BinOpKind::Shl),
+ ShiftRight => Some(BinOpKind::Shr),
+ BitAnd => Some(BinOpKind::BitAnd),
+ BitXor => Some(BinOpKind::BitXor),
+ BitOr => Some(BinOpKind::BitOr),
+ LAnd => Some(BinOpKind::And),
+ LOr => Some(BinOpKind::Or),
+ Assign | AssignOp(_) | As | DotDot | DotDotEq | Colon => None,
+ }
+ }
+
+ /// This operator could be used to follow a block unambiguously.
+ ///
+ /// This is used for error recovery at the moment, providing a suggestion to wrap blocks with
+ /// parentheses while having a high degree of confidence on the correctness of the suggestion.
+ pub fn can_continue_expr_unambiguously(&self) -> bool {
+ use AssocOp::*;
+ matches!(
+ self,
+ BitXor | // `{ 42 } ^ 3`
+ Assign | // `{ 42 } = { 42 }`
+ Divide | // `{ 42 } / 42`
+ Modulus | // `{ 42 } % 2`
+ ShiftRight | // `{ 42 } >> 2`
+ LessEqual | // `{ 42 } <= 3`
+ Greater | // `{ 42 } > 3`
+ GreaterEqual | // `{ 42 } >= 3`
+ AssignOp(_) | // `{ 42 } +=`
+ As | // `{ 42 } as usize`
+ // Equal | // `{ 42 } == { 42 }` Accepting these here would regress incorrect
+ // NotEqual | // `{ 42 } != { 42 } struct literals parser recovery.
+ Colon, // `{ 42 }: usize`
+ )
+ }
+}
+
+pub const PREC_CLOSURE: i8 = -40;
+pub const PREC_JUMP: i8 = -30;
+pub const PREC_RANGE: i8 = -10;
+// The range 2..=14 is reserved for AssocOp binary operator precedences.
+pub const PREC_PREFIX: i8 = 50;
+pub const PREC_POSTFIX: i8 = 60;
+pub const PREC_PAREN: i8 = 99;
+pub const PREC_FORCE_PAREN: i8 = 100;
+
+#[derive(Debug, Clone, Copy)]
+pub enum ExprPrecedence {
+ Closure,
+ Break,
+ Continue,
+ Ret,
+ Yield,
+ Yeet,
+
+ Range,
+
+ Binary(BinOpKind),
+
+ Cast,
+ Type,
+
+ Assign,
+ AssignOp,
+
+ Box,
+ AddrOf,
+ Let,
+ Unary,
+
+ Call,
+ MethodCall,
+ Field,
+ Index,
+ Try,
+ InlineAsm,
+ Mac,
+
+ Array,
+ Repeat,
+ Tup,
+ Lit,
+ Path,
+ Paren,
+ If,
+ While,
+ ForLoop,
+ Loop,
+ Match,
+ ConstBlock,
+ Block,
+ TryBlock,
+ Struct,
+ Async,
+ Await,
+ Err,
+}
+
+impl ExprPrecedence {
+ pub fn order(self) -> i8 {
+ match self {
+ ExprPrecedence::Closure => PREC_CLOSURE,
+
+ ExprPrecedence::Break |
+ ExprPrecedence::Continue |
+ ExprPrecedence::Ret |
+ ExprPrecedence::Yield |
+ ExprPrecedence::Yeet => PREC_JUMP,
+
+ // `Range` claims to have higher precedence than `Assign`, but `x .. x = x` fails to
+ // parse, instead of parsing as `(x .. x) = x`. Giving `Range` a lower precedence
+ // ensures that `pprust` will add parentheses in the right places to get the desired
+ // parse.
+ ExprPrecedence::Range => PREC_RANGE,
+
+ // Binop-like expr kinds, handled by `AssocOp`.
+ ExprPrecedence::Binary(op) => AssocOp::from_ast_binop(op).precedence() as i8,
+ ExprPrecedence::Cast => AssocOp::As.precedence() as i8,
+ ExprPrecedence::Type => AssocOp::Colon.precedence() as i8,
+
+ ExprPrecedence::Assign |
+ ExprPrecedence::AssignOp => AssocOp::Assign.precedence() as i8,
+
+ // Unary, prefix
+ ExprPrecedence::Box |
+ ExprPrecedence::AddrOf |
+ // Here `let pats = expr` has `let pats =` as a "unary" prefix of `expr`.
+ // However, this is not exactly right. When `let _ = a` is the LHS of a binop we
+ // need parens sometimes. E.g. we can print `(let _ = a) && b` as `let _ = a && b`
+ // but we need to print `(let _ = a) < b` as-is with parens.
+ ExprPrecedence::Let |
+ ExprPrecedence::Unary => PREC_PREFIX,
+
+ // Unary, postfix
+ ExprPrecedence::Await |
+ ExprPrecedence::Call |
+ ExprPrecedence::MethodCall |
+ ExprPrecedence::Field |
+ ExprPrecedence::Index |
+ ExprPrecedence::Try |
+ ExprPrecedence::InlineAsm |
+ ExprPrecedence::Mac => PREC_POSTFIX,
+
+ // Never need parens
+ ExprPrecedence::Array |
+ ExprPrecedence::Repeat |
+ ExprPrecedence::Tup |
+ ExprPrecedence::Lit |
+ ExprPrecedence::Path |
+ ExprPrecedence::Paren |
+ ExprPrecedence::If |
+ ExprPrecedence::While |
+ ExprPrecedence::ForLoop |
+ ExprPrecedence::Loop |
+ ExprPrecedence::Match |
+ ExprPrecedence::ConstBlock |
+ ExprPrecedence::Block |
+ ExprPrecedence::TryBlock |
+ ExprPrecedence::Async |
+ ExprPrecedence::Struct |
+ ExprPrecedence::Err => PREC_PAREN,
+ }
+ }
+}
+
+/// In `let p = e`, operators with precedence `<=` this one requires parentheses in `e`.
+pub fn prec_let_scrutinee_needs_par() -> usize {
+ AssocOp::LAnd.precedence()
+}
+
+/// Suppose we have `let _ = e` and the `order` of `e`.
+/// Is the `order` such that `e` in `let _ = e` needs parentheses when it is on the RHS?
+///
+/// Conversely, suppose that we have `(let _ = a) OP b` and `order` is that of `OP`.
+/// Can we print this as `let _ = a OP b`?
+pub fn needs_par_as_let_scrutinee(order: i8) -> bool {
+ order <= prec_let_scrutinee_needs_par() as i8
+}
+
+/// Expressions that syntactically contain an "exterior" struct literal i.e., not surrounded by any
+/// parens or other delimiters, e.g., `X { y: 1 }`, `X { y: 1 }.method()`, `foo == X { y: 1 }` and
+/// `X { y: 1 } == foo` all do, but `(X { y: 1 }) == foo` does not.
+pub fn contains_exterior_struct_lit(value: &ast::Expr) -> bool {
+ match value.kind {
+ ast::ExprKind::Struct(..) => true,
+
+ ast::ExprKind::Assign(ref lhs, ref rhs, _)
+ | ast::ExprKind::AssignOp(_, ref lhs, ref rhs)
+ | ast::ExprKind::Binary(_, ref lhs, ref rhs) => {
+ // X { y: 1 } + X { y: 2 }
+ contains_exterior_struct_lit(&lhs) || contains_exterior_struct_lit(&rhs)
+ }
+ ast::ExprKind::Await(ref x)
+ | ast::ExprKind::Unary(_, ref x)
+ | ast::ExprKind::Cast(ref x, _)
+ | ast::ExprKind::Type(ref x, _)
+ | ast::ExprKind::Field(ref x, _)
+ | ast::ExprKind::Index(ref x, _) => {
+ // &X { y: 1 }, X { y: 1 }.y
+ contains_exterior_struct_lit(&x)
+ }
+
+ ast::ExprKind::MethodCall(.., ref exprs, _) => {
+ // X { y: 1 }.bar(...)
+ contains_exterior_struct_lit(&exprs[0])
+ }
+
+ _ => false,
+ }
+}
diff --git a/compiler/rustc_ast/src/util/unicode.rs b/compiler/rustc_ast/src/util/unicode.rs
new file mode 100644
index 000000000..f009f7b30
--- /dev/null
+++ b/compiler/rustc_ast/src/util/unicode.rs
@@ -0,0 +1,35 @@
+pub const TEXT_FLOW_CONTROL_CHARS: &[char] = &[
+ '\u{202A}', '\u{202B}', '\u{202D}', '\u{202E}', '\u{2066}', '\u{2067}', '\u{2068}', '\u{202C}',
+ '\u{2069}',
+];
+
+#[inline]
+pub fn contains_text_flow_control_chars(s: &str) -> bool {
+ // Char - UTF-8
+ // U+202A - E2 80 AA
+ // U+202B - E2 80 AB
+ // U+202C - E2 80 AC
+ // U+202D - E2 80 AD
+ // U+202E - E2 80 AE
+ // U+2066 - E2 81 A6
+ // U+2067 - E2 81 A7
+ // U+2068 - E2 81 A8
+ // U+2069 - E2 81 A9
+ let mut bytes = s.as_bytes();
+ loop {
+ match core::slice::memchr::memchr(0xE2, &bytes) {
+ Some(idx) => {
+ // bytes are valid UTF-8 -> E2 must be followed by two bytes
+ let ch = &bytes[idx..idx + 3];
+ match ch {
+ [_, 0x80, 0xAA..=0xAE] | [_, 0x81, 0xA6..=0xA9] => break true,
+ _ => {}
+ }
+ bytes = &bytes[idx + 3..];
+ }
+ None => {
+ break false;
+ }
+ }
+ }
+}
diff --git a/compiler/rustc_ast/src/visit.rs b/compiler/rustc_ast/src/visit.rs
new file mode 100644
index 000000000..d9594b323
--- /dev/null
+++ b/compiler/rustc_ast/src/visit.rs
@@ -0,0 +1,959 @@
+//! AST walker. Each overridden visit method has full control over what
+//! happens with its node, it can do its own traversal of the node's children,
+//! call `visit::walk_*` to apply the default traversal algorithm, or prevent
+//! deeper traversal by doing nothing.
+//!
+//! Note: it is an important invariant that the default visitor walks the body
+//! of a function in "execution order" (more concretely, reverse post-order
+//! with respect to the CFG implied by the AST), meaning that if AST node A may
+//! execute before AST node B, then A is visited first. The borrow checker in
+//! particular relies on this property.
+//!
+//! Note: walking an AST before macro expansion is probably a bad idea. For
+//! instance, a walker looking for item names in a module will miss all of
+//! those that are created by the expansion of a macro.
+
+use crate::ast::*;
+
+use rustc_span::symbol::Ident;
+use rustc_span::Span;
+
+#[derive(Copy, Clone, Debug, PartialEq)]
+pub enum AssocCtxt {
+ Trait,
+ Impl,
+}
+
+#[derive(Copy, Clone, Debug, PartialEq)]
+pub enum FnCtxt {
+ Free,
+ Foreign,
+ Assoc(AssocCtxt),
+}
+
+#[derive(Copy, Clone, Debug)]
+pub enum BoundKind {
+ /// Trait bounds in generics bounds and type/trait alias.
+ /// E.g., `<T: Bound>`, `type A: Bound`, or `where T: Bound`.
+ Bound,
+
+ /// Trait bounds in `impl` type.
+ /// E.g., `type Foo = impl Bound1 + Bound2 + Bound3`.
+ Impl,
+
+ /// Trait bounds in trait object type.
+ /// E.g., `dyn Bound1 + Bound2 + Bound3`.
+ TraitObject,
+
+ /// Super traits of a trait.
+ /// E.g., `trait A: B`
+ SuperTraits,
+}
+
+#[derive(Copy, Clone, Debug)]
+pub enum FnKind<'a> {
+ /// E.g., `fn foo()`, `fn foo(&self)`, or `extern "Abi" fn foo()`.
+ Fn(FnCtxt, Ident, &'a FnSig, &'a Visibility, &'a Generics, Option<&'a Block>),
+
+ /// E.g., `|x, y| body`.
+ Closure(&'a ClosureBinder, &'a FnDecl, &'a Expr),
+}
+
+impl<'a> FnKind<'a> {
+ pub fn header(&self) -> Option<&'a FnHeader> {
+ match *self {
+ FnKind::Fn(_, _, sig, _, _, _) => Some(&sig.header),
+ FnKind::Closure(_, _, _) => None,
+ }
+ }
+
+ pub fn ident(&self) -> Option<&Ident> {
+ match self {
+ FnKind::Fn(_, ident, ..) => Some(ident),
+ _ => None,
+ }
+ }
+
+ pub fn decl(&self) -> &'a FnDecl {
+ match self {
+ FnKind::Fn(_, _, sig, _, _, _) => &sig.decl,
+ FnKind::Closure(_, decl, _) => decl,
+ }
+ }
+
+ pub fn ctxt(&self) -> Option<FnCtxt> {
+ match self {
+ FnKind::Fn(ctxt, ..) => Some(*ctxt),
+ FnKind::Closure(..) => None,
+ }
+ }
+}
+
+#[derive(Copy, Clone, Debug)]
+pub enum LifetimeCtxt {
+ /// Appears in a reference type.
+ Rptr,
+ /// Appears as a bound on a type or another lifetime.
+ Bound,
+ /// Appears as a generic argument.
+ GenericArg,
+}
+
+/// Each method of the `Visitor` trait is a hook to be potentially
+/// overridden. Each method's default implementation recursively visits
+/// the substructure of the input via the corresponding `walk` method;
+/// e.g., the `visit_item` method by default calls `visit::walk_item`.
+///
+/// If you want to ensure that your code handles every variant
+/// explicitly, you need to override each method. (And you also need
+/// to monitor future changes to `Visitor` in case a new method with a
+/// new default implementation gets introduced.)
+pub trait Visitor<'ast>: Sized {
+ fn visit_ident(&mut self, _ident: Ident) {}
+ fn visit_foreign_item(&mut self, i: &'ast ForeignItem) {
+ walk_foreign_item(self, i)
+ }
+ fn visit_item(&mut self, i: &'ast Item) {
+ walk_item(self, i)
+ }
+ fn visit_local(&mut self, l: &'ast Local) {
+ walk_local(self, l)
+ }
+ fn visit_block(&mut self, b: &'ast Block) {
+ walk_block(self, b)
+ }
+ fn visit_stmt(&mut self, s: &'ast Stmt) {
+ walk_stmt(self, s)
+ }
+ fn visit_param(&mut self, param: &'ast Param) {
+ walk_param(self, param)
+ }
+ fn visit_arm(&mut self, a: &'ast Arm) {
+ walk_arm(self, a)
+ }
+ fn visit_pat(&mut self, p: &'ast Pat) {
+ walk_pat(self, p)
+ }
+ fn visit_anon_const(&mut self, c: &'ast AnonConst) {
+ walk_anon_const(self, c)
+ }
+ fn visit_expr(&mut self, ex: &'ast Expr) {
+ walk_expr(self, ex)
+ }
+ fn visit_expr_post(&mut self, _ex: &'ast Expr) {}
+ fn visit_ty(&mut self, t: &'ast Ty) {
+ walk_ty(self, t)
+ }
+ fn visit_generic_param(&mut self, param: &'ast GenericParam) {
+ walk_generic_param(self, param)
+ }
+ fn visit_generics(&mut self, g: &'ast Generics) {
+ walk_generics(self, g)
+ }
+ fn visit_closure_binder(&mut self, b: &'ast ClosureBinder) {
+ walk_closure_binder(self, b)
+ }
+ fn visit_where_predicate(&mut self, p: &'ast WherePredicate) {
+ walk_where_predicate(self, p)
+ }
+ fn visit_fn(&mut self, fk: FnKind<'ast>, s: Span, _: NodeId) {
+ walk_fn(self, fk, s)
+ }
+ fn visit_assoc_item(&mut self, i: &'ast AssocItem, ctxt: AssocCtxt) {
+ walk_assoc_item(self, i, ctxt)
+ }
+ fn visit_trait_ref(&mut self, t: &'ast TraitRef) {
+ walk_trait_ref(self, t)
+ }
+ fn visit_param_bound(&mut self, bounds: &'ast GenericBound, _ctxt: BoundKind) {
+ walk_param_bound(self, bounds)
+ }
+ fn visit_poly_trait_ref(&mut self, t: &'ast PolyTraitRef, m: &'ast TraitBoundModifier) {
+ walk_poly_trait_ref(self, t, m)
+ }
+ fn visit_variant_data(&mut self, s: &'ast VariantData) {
+ walk_struct_def(self, s)
+ }
+ fn visit_field_def(&mut self, s: &'ast FieldDef) {
+ walk_field_def(self, s)
+ }
+ fn visit_enum_def(
+ &mut self,
+ enum_definition: &'ast EnumDef,
+ generics: &'ast Generics,
+ item_id: NodeId,
+ _: Span,
+ ) {
+ walk_enum_def(self, enum_definition, generics, item_id)
+ }
+ fn visit_variant(&mut self, v: &'ast Variant) {
+ walk_variant(self, v)
+ }
+ fn visit_label(&mut self, label: &'ast Label) {
+ walk_label(self, label)
+ }
+ fn visit_lifetime(&mut self, lifetime: &'ast Lifetime, _: LifetimeCtxt) {
+ walk_lifetime(self, lifetime)
+ }
+ fn visit_mac_call(&mut self, mac: &'ast MacCall) {
+ walk_mac(self, mac)
+ }
+ fn visit_mac_def(&mut self, _mac: &'ast MacroDef, _id: NodeId) {
+ // Nothing to do
+ }
+ fn visit_path(&mut self, path: &'ast Path, _id: NodeId) {
+ walk_path(self, path)
+ }
+ fn visit_use_tree(&mut self, use_tree: &'ast UseTree, id: NodeId, _nested: bool) {
+ walk_use_tree(self, use_tree, id)
+ }
+ fn visit_path_segment(&mut self, path_span: Span, path_segment: &'ast PathSegment) {
+ walk_path_segment(self, path_span, path_segment)
+ }
+ fn visit_generic_args(&mut self, path_span: Span, generic_args: &'ast GenericArgs) {
+ walk_generic_args(self, path_span, generic_args)
+ }
+ fn visit_generic_arg(&mut self, generic_arg: &'ast GenericArg) {
+ walk_generic_arg(self, generic_arg)
+ }
+ fn visit_assoc_constraint(&mut self, constraint: &'ast AssocConstraint) {
+ walk_assoc_constraint(self, constraint)
+ }
+ fn visit_attribute(&mut self, attr: &'ast Attribute) {
+ walk_attribute(self, attr)
+ }
+ fn visit_vis(&mut self, vis: &'ast Visibility) {
+ walk_vis(self, vis)
+ }
+ fn visit_fn_ret_ty(&mut self, ret_ty: &'ast FnRetTy) {
+ walk_fn_ret_ty(self, ret_ty)
+ }
+ fn visit_fn_header(&mut self, _header: &'ast FnHeader) {
+ // Nothing to do
+ }
+ fn visit_expr_field(&mut self, f: &'ast ExprField) {
+ walk_expr_field(self, f)
+ }
+ fn visit_pat_field(&mut self, fp: &'ast PatField) {
+ walk_pat_field(self, fp)
+ }
+ fn visit_crate(&mut self, krate: &'ast Crate) {
+ walk_crate(self, krate)
+ }
+ fn visit_inline_asm(&mut self, asm: &'ast InlineAsm) {
+ walk_inline_asm(self, asm)
+ }
+ fn visit_inline_asm_sym(&mut self, sym: &'ast InlineAsmSym) {
+ walk_inline_asm_sym(self, sym)
+ }
+}
+
+#[macro_export]
+macro_rules! walk_list {
+ ($visitor: expr, $method: ident, $list: expr) => {
+ for elem in $list {
+ $visitor.$method(elem)
+ }
+ };
+ ($visitor: expr, $method: ident, $list: expr, $($extra_args: expr),*) => {
+ for elem in $list {
+ $visitor.$method(elem, $($extra_args,)*)
+ }
+ }
+}
+
+pub fn walk_crate<'a, V: Visitor<'a>>(visitor: &mut V, krate: &'a Crate) {
+ walk_list!(visitor, visit_item, &krate.items);
+ walk_list!(visitor, visit_attribute, &krate.attrs);
+}
+
+pub fn walk_local<'a, V: Visitor<'a>>(visitor: &mut V, local: &'a Local) {
+ for attr in local.attrs.iter() {
+ visitor.visit_attribute(attr);
+ }
+ visitor.visit_pat(&local.pat);
+ walk_list!(visitor, visit_ty, &local.ty);
+ if let Some((init, els)) = local.kind.init_else_opt() {
+ visitor.visit_expr(init);
+ walk_list!(visitor, visit_block, els);
+ }
+}
+
+pub fn walk_label<'a, V: Visitor<'a>>(visitor: &mut V, label: &'a Label) {
+ visitor.visit_ident(label.ident);
+}
+
+pub fn walk_lifetime<'a, V: Visitor<'a>>(visitor: &mut V, lifetime: &'a Lifetime) {
+ visitor.visit_ident(lifetime.ident);
+}
+
+pub fn walk_poly_trait_ref<'a, V>(
+ visitor: &mut V,
+ trait_ref: &'a PolyTraitRef,
+ _: &TraitBoundModifier,
+) where
+ V: Visitor<'a>,
+{
+ walk_list!(visitor, visit_generic_param, &trait_ref.bound_generic_params);
+ visitor.visit_trait_ref(&trait_ref.trait_ref);
+}
+
+pub fn walk_trait_ref<'a, V: Visitor<'a>>(visitor: &mut V, trait_ref: &'a TraitRef) {
+ visitor.visit_path(&trait_ref.path, trait_ref.ref_id)
+}
+
+pub fn walk_item<'a, V: Visitor<'a>>(visitor: &mut V, item: &'a Item) {
+ visitor.visit_vis(&item.vis);
+ visitor.visit_ident(item.ident);
+ match item.kind {
+ ItemKind::ExternCrate(_) => {}
+ ItemKind::Use(ref use_tree) => visitor.visit_use_tree(use_tree, item.id, false),
+ ItemKind::Static(ref typ, _, ref expr) | ItemKind::Const(_, ref typ, ref expr) => {
+ visitor.visit_ty(typ);
+ walk_list!(visitor, visit_expr, expr);
+ }
+ ItemKind::Fn(box Fn { defaultness: _, ref generics, ref sig, ref body }) => {
+ let kind =
+ FnKind::Fn(FnCtxt::Free, item.ident, sig, &item.vis, generics, body.as_deref());
+ visitor.visit_fn(kind, item.span, item.id)
+ }
+ ItemKind::Mod(_unsafety, ref mod_kind) => match mod_kind {
+ ModKind::Loaded(items, _inline, _inner_span) => {
+ walk_list!(visitor, visit_item, items)
+ }
+ ModKind::Unloaded => {}
+ },
+ ItemKind::ForeignMod(ref foreign_module) => {
+ walk_list!(visitor, visit_foreign_item, &foreign_module.items);
+ }
+ ItemKind::GlobalAsm(ref asm) => visitor.visit_inline_asm(asm),
+ ItemKind::TyAlias(box TyAlias { ref generics, ref bounds, ref ty, .. }) => {
+ visitor.visit_generics(generics);
+ walk_list!(visitor, visit_param_bound, bounds, BoundKind::Bound);
+ walk_list!(visitor, visit_ty, ty);
+ }
+ ItemKind::Enum(ref enum_definition, ref generics) => {
+ visitor.visit_generics(generics);
+ visitor.visit_enum_def(enum_definition, generics, item.id, item.span)
+ }
+ ItemKind::Impl(box Impl {
+ defaultness: _,
+ unsafety: _,
+ ref generics,
+ constness: _,
+ polarity: _,
+ ref of_trait,
+ ref self_ty,
+ ref items,
+ }) => {
+ visitor.visit_generics(generics);
+ walk_list!(visitor, visit_trait_ref, of_trait);
+ visitor.visit_ty(self_ty);
+ walk_list!(visitor, visit_assoc_item, items, AssocCtxt::Impl);
+ }
+ ItemKind::Struct(ref struct_definition, ref generics)
+ | ItemKind::Union(ref struct_definition, ref generics) => {
+ visitor.visit_generics(generics);
+ visitor.visit_variant_data(struct_definition);
+ }
+ ItemKind::Trait(box Trait {
+ unsafety: _,
+ is_auto: _,
+ ref generics,
+ ref bounds,
+ ref items,
+ }) => {
+ visitor.visit_generics(generics);
+ walk_list!(visitor, visit_param_bound, bounds, BoundKind::SuperTraits);
+ walk_list!(visitor, visit_assoc_item, items, AssocCtxt::Trait);
+ }
+ ItemKind::TraitAlias(ref generics, ref bounds) => {
+ visitor.visit_generics(generics);
+ walk_list!(visitor, visit_param_bound, bounds, BoundKind::Bound);
+ }
+ ItemKind::MacCall(ref mac) => visitor.visit_mac_call(mac),
+ ItemKind::MacroDef(ref ts) => visitor.visit_mac_def(ts, item.id),
+ }
+ walk_list!(visitor, visit_attribute, &item.attrs);
+}
+
+pub fn walk_enum_def<'a, V: Visitor<'a>>(
+ visitor: &mut V,
+ enum_definition: &'a EnumDef,
+ _: &'a Generics,
+ _: NodeId,
+) {
+ walk_list!(visitor, visit_variant, &enum_definition.variants);
+}
+
+pub fn walk_variant<'a, V: Visitor<'a>>(visitor: &mut V, variant: &'a Variant)
+where
+ V: Visitor<'a>,
+{
+ visitor.visit_ident(variant.ident);
+ visitor.visit_vis(&variant.vis);
+ visitor.visit_variant_data(&variant.data);
+ walk_list!(visitor, visit_anon_const, &variant.disr_expr);
+ walk_list!(visitor, visit_attribute, &variant.attrs);
+}
+
+pub fn walk_expr_field<'a, V: Visitor<'a>>(visitor: &mut V, f: &'a ExprField) {
+ visitor.visit_expr(&f.expr);
+ visitor.visit_ident(f.ident);
+ walk_list!(visitor, visit_attribute, f.attrs.iter());
+}
+
+pub fn walk_pat_field<'a, V: Visitor<'a>>(visitor: &mut V, fp: &'a PatField) {
+ visitor.visit_ident(fp.ident);
+ visitor.visit_pat(&fp.pat);
+ walk_list!(visitor, visit_attribute, fp.attrs.iter());
+}
+
+pub fn walk_ty<'a, V: Visitor<'a>>(visitor: &mut V, typ: &'a Ty) {
+ match typ.kind {
+ TyKind::Slice(ref ty) | TyKind::Paren(ref ty) => visitor.visit_ty(ty),
+ TyKind::Ptr(ref mutable_type) => visitor.visit_ty(&mutable_type.ty),
+ TyKind::Rptr(ref opt_lifetime, ref mutable_type) => {
+ walk_list!(visitor, visit_lifetime, opt_lifetime, LifetimeCtxt::Rptr);
+ visitor.visit_ty(&mutable_type.ty)
+ }
+ TyKind::Tup(ref tuple_element_types) => {
+ walk_list!(visitor, visit_ty, tuple_element_types);
+ }
+ TyKind::BareFn(ref function_declaration) => {
+ walk_list!(visitor, visit_generic_param, &function_declaration.generic_params);
+ walk_fn_decl(visitor, &function_declaration.decl);
+ }
+ TyKind::Path(ref maybe_qself, ref path) => {
+ if let Some(ref qself) = *maybe_qself {
+ visitor.visit_ty(&qself.ty);
+ }
+ visitor.visit_path(path, typ.id);
+ }
+ TyKind::Array(ref ty, ref length) => {
+ visitor.visit_ty(ty);
+ visitor.visit_anon_const(length)
+ }
+ TyKind::TraitObject(ref bounds, ..) => {
+ walk_list!(visitor, visit_param_bound, bounds, BoundKind::TraitObject);
+ }
+ TyKind::ImplTrait(_, ref bounds) => {
+ walk_list!(visitor, visit_param_bound, bounds, BoundKind::Impl);
+ }
+ TyKind::Typeof(ref expression) => visitor.visit_anon_const(expression),
+ TyKind::Infer | TyKind::ImplicitSelf | TyKind::Err => {}
+ TyKind::MacCall(ref mac) => visitor.visit_mac_call(mac),
+ TyKind::Never | TyKind::CVarArgs => {}
+ }
+}
+
+pub fn walk_path<'a, V: Visitor<'a>>(visitor: &mut V, path: &'a Path) {
+ for segment in &path.segments {
+ visitor.visit_path_segment(path.span, segment);
+ }
+}
+
+pub fn walk_use_tree<'a, V: Visitor<'a>>(visitor: &mut V, use_tree: &'a UseTree, id: NodeId) {
+ visitor.visit_path(&use_tree.prefix, id);
+ match use_tree.kind {
+ UseTreeKind::Simple(rename, ..) => {
+ // The extra IDs are handled during HIR lowering.
+ if let Some(rename) = rename {
+ visitor.visit_ident(rename);
+ }
+ }
+ UseTreeKind::Glob => {}
+ UseTreeKind::Nested(ref use_trees) => {
+ for &(ref nested_tree, nested_id) in use_trees {
+ visitor.visit_use_tree(nested_tree, nested_id, true);
+ }
+ }
+ }
+}
+
+pub fn walk_path_segment<'a, V: Visitor<'a>>(
+ visitor: &mut V,
+ path_span: Span,
+ segment: &'a PathSegment,
+) {
+ visitor.visit_ident(segment.ident);
+ if let Some(ref args) = segment.args {
+ visitor.visit_generic_args(path_span, args);
+ }
+}
+
+pub fn walk_generic_args<'a, V>(visitor: &mut V, _path_span: Span, generic_args: &'a GenericArgs)
+where
+ V: Visitor<'a>,
+{
+ match *generic_args {
+ GenericArgs::AngleBracketed(ref data) => {
+ for arg in &data.args {
+ match arg {
+ AngleBracketedArg::Arg(a) => visitor.visit_generic_arg(a),
+ AngleBracketedArg::Constraint(c) => visitor.visit_assoc_constraint(c),
+ }
+ }
+ }
+ GenericArgs::Parenthesized(ref data) => {
+ walk_list!(visitor, visit_ty, &data.inputs);
+ walk_fn_ret_ty(visitor, &data.output);
+ }
+ }
+}
+
+pub fn walk_generic_arg<'a, V>(visitor: &mut V, generic_arg: &'a GenericArg)
+where
+ V: Visitor<'a>,
+{
+ match generic_arg {
+ GenericArg::Lifetime(lt) => visitor.visit_lifetime(lt, LifetimeCtxt::GenericArg),
+ GenericArg::Type(ty) => visitor.visit_ty(ty),
+ GenericArg::Const(ct) => visitor.visit_anon_const(ct),
+ }
+}
+
+pub fn walk_assoc_constraint<'a, V: Visitor<'a>>(visitor: &mut V, constraint: &'a AssocConstraint) {
+ visitor.visit_ident(constraint.ident);
+ if let Some(ref gen_args) = constraint.gen_args {
+ visitor.visit_generic_args(gen_args.span(), gen_args);
+ }
+ match constraint.kind {
+ AssocConstraintKind::Equality { ref term } => match term {
+ Term::Ty(ty) => visitor.visit_ty(ty),
+ Term::Const(c) => visitor.visit_anon_const(c),
+ },
+ AssocConstraintKind::Bound { ref bounds } => {
+ walk_list!(visitor, visit_param_bound, bounds, BoundKind::Bound);
+ }
+ }
+}
+
+pub fn walk_pat<'a, V: Visitor<'a>>(visitor: &mut V, pattern: &'a Pat) {
+ match pattern.kind {
+ PatKind::TupleStruct(ref opt_qself, ref path, ref elems) => {
+ if let Some(ref qself) = *opt_qself {
+ visitor.visit_ty(&qself.ty);
+ }
+ visitor.visit_path(path, pattern.id);
+ walk_list!(visitor, visit_pat, elems);
+ }
+ PatKind::Path(ref opt_qself, ref path) => {
+ if let Some(ref qself) = *opt_qself {
+ visitor.visit_ty(&qself.ty);
+ }
+ visitor.visit_path(path, pattern.id)
+ }
+ PatKind::Struct(ref opt_qself, ref path, ref fields, _) => {
+ if let Some(ref qself) = *opt_qself {
+ visitor.visit_ty(&qself.ty);
+ }
+ visitor.visit_path(path, pattern.id);
+ walk_list!(visitor, visit_pat_field, fields);
+ }
+ PatKind::Box(ref subpattern)
+ | PatKind::Ref(ref subpattern, _)
+ | PatKind::Paren(ref subpattern) => visitor.visit_pat(subpattern),
+ PatKind::Ident(_, ident, ref optional_subpattern) => {
+ visitor.visit_ident(ident);
+ walk_list!(visitor, visit_pat, optional_subpattern);
+ }
+ PatKind::Lit(ref expression) => visitor.visit_expr(expression),
+ PatKind::Range(ref lower_bound, ref upper_bound, _) => {
+ walk_list!(visitor, visit_expr, lower_bound);
+ walk_list!(visitor, visit_expr, upper_bound);
+ }
+ PatKind::Wild | PatKind::Rest => {}
+ PatKind::Tuple(ref elems) | PatKind::Slice(ref elems) | PatKind::Or(ref elems) => {
+ walk_list!(visitor, visit_pat, elems);
+ }
+ PatKind::MacCall(ref mac) => visitor.visit_mac_call(mac),
+ }
+}
+
+pub fn walk_foreign_item<'a, V: Visitor<'a>>(visitor: &mut V, item: &'a ForeignItem) {
+ let Item { id, span, ident, ref vis, ref attrs, ref kind, tokens: _ } = *item;
+ visitor.visit_vis(vis);
+ visitor.visit_ident(ident);
+ walk_list!(visitor, visit_attribute, attrs);
+ match kind {
+ ForeignItemKind::Static(ty, _, expr) => {
+ visitor.visit_ty(ty);
+ walk_list!(visitor, visit_expr, expr);
+ }
+ ForeignItemKind::Fn(box Fn { defaultness: _, ref generics, ref sig, ref body }) => {
+ let kind = FnKind::Fn(FnCtxt::Foreign, ident, sig, vis, generics, body.as_deref());
+ visitor.visit_fn(kind, span, id);
+ }
+ ForeignItemKind::TyAlias(box TyAlias { generics, bounds, ty, .. }) => {
+ visitor.visit_generics(generics);
+ walk_list!(visitor, visit_param_bound, bounds, BoundKind::Bound);
+ walk_list!(visitor, visit_ty, ty);
+ }
+ ForeignItemKind::MacCall(mac) => {
+ visitor.visit_mac_call(mac);
+ }
+ }
+}
+
+pub fn walk_param_bound<'a, V: Visitor<'a>>(visitor: &mut V, bound: &'a GenericBound) {
+ match *bound {
+ GenericBound::Trait(ref typ, ref modifier) => visitor.visit_poly_trait_ref(typ, modifier),
+ GenericBound::Outlives(ref lifetime) => {
+ visitor.visit_lifetime(lifetime, LifetimeCtxt::Bound)
+ }
+ }
+}
+
+pub fn walk_generic_param<'a, V: Visitor<'a>>(visitor: &mut V, param: &'a GenericParam) {
+ visitor.visit_ident(param.ident);
+ walk_list!(visitor, visit_attribute, param.attrs.iter());
+ walk_list!(visitor, visit_param_bound, &param.bounds, BoundKind::Bound);
+ match param.kind {
+ GenericParamKind::Lifetime => (),
+ GenericParamKind::Type { ref default } => walk_list!(visitor, visit_ty, default),
+ GenericParamKind::Const { ref ty, ref default, .. } => {
+ visitor.visit_ty(ty);
+ if let Some(default) = default {
+ visitor.visit_anon_const(default);
+ }
+ }
+ }
+}
+
+pub fn walk_generics<'a, V: Visitor<'a>>(visitor: &mut V, generics: &'a Generics) {
+ walk_list!(visitor, visit_generic_param, &generics.params);
+ walk_list!(visitor, visit_where_predicate, &generics.where_clause.predicates);
+}
+
+pub fn walk_closure_binder<'a, V: Visitor<'a>>(visitor: &mut V, binder: &'a ClosureBinder) {
+ match binder {
+ ClosureBinder::NotPresent => {}
+ ClosureBinder::For { generic_params, span: _ } => {
+ walk_list!(visitor, visit_generic_param, generic_params)
+ }
+ }
+}
+
+pub fn walk_where_predicate<'a, V: Visitor<'a>>(visitor: &mut V, predicate: &'a WherePredicate) {
+ match *predicate {
+ WherePredicate::BoundPredicate(WhereBoundPredicate {
+ ref bounded_ty,
+ ref bounds,
+ ref bound_generic_params,
+ ..
+ }) => {
+ visitor.visit_ty(bounded_ty);
+ walk_list!(visitor, visit_param_bound, bounds, BoundKind::Bound);
+ walk_list!(visitor, visit_generic_param, bound_generic_params);
+ }
+ WherePredicate::RegionPredicate(WhereRegionPredicate {
+ ref lifetime, ref bounds, ..
+ }) => {
+ visitor.visit_lifetime(lifetime, LifetimeCtxt::Bound);
+ walk_list!(visitor, visit_param_bound, bounds, BoundKind::Bound);
+ }
+ WherePredicate::EqPredicate(WhereEqPredicate { ref lhs_ty, ref rhs_ty, .. }) => {
+ visitor.visit_ty(lhs_ty);
+ visitor.visit_ty(rhs_ty);
+ }
+ }
+}
+
+pub fn walk_fn_ret_ty<'a, V: Visitor<'a>>(visitor: &mut V, ret_ty: &'a FnRetTy) {
+ if let FnRetTy::Ty(ref output_ty) = *ret_ty {
+ visitor.visit_ty(output_ty)
+ }
+}
+
+pub fn walk_fn_decl<'a, V: Visitor<'a>>(visitor: &mut V, function_declaration: &'a FnDecl) {
+ for param in &function_declaration.inputs {
+ visitor.visit_param(param);
+ }
+ visitor.visit_fn_ret_ty(&function_declaration.output);
+}
+
+pub fn walk_fn<'a, V: Visitor<'a>>(visitor: &mut V, kind: FnKind<'a>, _span: Span) {
+ match kind {
+ FnKind::Fn(_, _, sig, _, generics, body) => {
+ visitor.visit_generics(generics);
+ visitor.visit_fn_header(&sig.header);
+ walk_fn_decl(visitor, &sig.decl);
+ walk_list!(visitor, visit_block, body);
+ }
+ FnKind::Closure(binder, decl, body) => {
+ visitor.visit_closure_binder(binder);
+ walk_fn_decl(visitor, decl);
+ visitor.visit_expr(body);
+ }
+ }
+}
+
+pub fn walk_assoc_item<'a, V: Visitor<'a>>(visitor: &mut V, item: &'a AssocItem, ctxt: AssocCtxt) {
+ let Item { id, span, ident, ref vis, ref attrs, ref kind, tokens: _ } = *item;
+ visitor.visit_vis(vis);
+ visitor.visit_ident(ident);
+ walk_list!(visitor, visit_attribute, attrs);
+ match kind {
+ AssocItemKind::Const(_, ty, expr) => {
+ visitor.visit_ty(ty);
+ walk_list!(visitor, visit_expr, expr);
+ }
+ AssocItemKind::Fn(box Fn { defaultness: _, ref generics, ref sig, ref body }) => {
+ let kind = FnKind::Fn(FnCtxt::Assoc(ctxt), ident, sig, vis, generics, body.as_deref());
+ visitor.visit_fn(kind, span, id);
+ }
+ AssocItemKind::TyAlias(box TyAlias { generics, bounds, ty, .. }) => {
+ visitor.visit_generics(generics);
+ walk_list!(visitor, visit_param_bound, bounds, BoundKind::Bound);
+ walk_list!(visitor, visit_ty, ty);
+ }
+ AssocItemKind::MacCall(mac) => {
+ visitor.visit_mac_call(mac);
+ }
+ }
+}
+
+pub fn walk_struct_def<'a, V: Visitor<'a>>(visitor: &mut V, struct_definition: &'a VariantData) {
+ walk_list!(visitor, visit_field_def, struct_definition.fields());
+}
+
+pub fn walk_field_def<'a, V: Visitor<'a>>(visitor: &mut V, field: &'a FieldDef) {
+ visitor.visit_vis(&field.vis);
+ if let Some(ident) = field.ident {
+ visitor.visit_ident(ident);
+ }
+ visitor.visit_ty(&field.ty);
+ walk_list!(visitor, visit_attribute, &field.attrs);
+}
+
+pub fn walk_block<'a, V: Visitor<'a>>(visitor: &mut V, block: &'a Block) {
+ walk_list!(visitor, visit_stmt, &block.stmts);
+}
+
+pub fn walk_stmt<'a, V: Visitor<'a>>(visitor: &mut V, statement: &'a Stmt) {
+ match statement.kind {
+ StmtKind::Local(ref local) => visitor.visit_local(local),
+ StmtKind::Item(ref item) => visitor.visit_item(item),
+ StmtKind::Expr(ref expr) | StmtKind::Semi(ref expr) => visitor.visit_expr(expr),
+ StmtKind::Empty => {}
+ StmtKind::MacCall(ref mac) => {
+ let MacCallStmt { ref mac, style: _, ref attrs, tokens: _ } = **mac;
+ visitor.visit_mac_call(mac);
+ for attr in attrs.iter() {
+ visitor.visit_attribute(attr);
+ }
+ }
+ }
+}
+
+pub fn walk_mac<'a, V: Visitor<'a>>(visitor: &mut V, mac: &'a MacCall) {
+ visitor.visit_path(&mac.path, DUMMY_NODE_ID);
+}
+
+pub fn walk_anon_const<'a, V: Visitor<'a>>(visitor: &mut V, constant: &'a AnonConst) {
+ visitor.visit_expr(&constant.value);
+}
+
+pub fn walk_inline_asm<'a, V: Visitor<'a>>(visitor: &mut V, asm: &'a InlineAsm) {
+ for (op, _) in &asm.operands {
+ match op {
+ InlineAsmOperand::In { expr, .. }
+ | InlineAsmOperand::Out { expr: Some(expr), .. }
+ | InlineAsmOperand::InOut { expr, .. } => visitor.visit_expr(expr),
+ InlineAsmOperand::Out { expr: None, .. } => {}
+ InlineAsmOperand::SplitInOut { in_expr, out_expr, .. } => {
+ visitor.visit_expr(in_expr);
+ if let Some(out_expr) = out_expr {
+ visitor.visit_expr(out_expr);
+ }
+ }
+ InlineAsmOperand::Const { anon_const, .. } => visitor.visit_anon_const(anon_const),
+ InlineAsmOperand::Sym { sym } => visitor.visit_inline_asm_sym(sym),
+ }
+ }
+}
+
+pub fn walk_inline_asm_sym<'a, V: Visitor<'a>>(visitor: &mut V, sym: &'a InlineAsmSym) {
+ if let Some(ref qself) = sym.qself {
+ visitor.visit_ty(&qself.ty);
+ }
+ visitor.visit_path(&sym.path, sym.id);
+}
+
+pub fn walk_expr<'a, V: Visitor<'a>>(visitor: &mut V, expression: &'a Expr) {
+ walk_list!(visitor, visit_attribute, expression.attrs.iter());
+
+ match expression.kind {
+ ExprKind::Box(ref subexpression) => visitor.visit_expr(subexpression),
+ ExprKind::Array(ref subexpressions) => {
+ walk_list!(visitor, visit_expr, subexpressions);
+ }
+ ExprKind::ConstBlock(ref anon_const) => visitor.visit_anon_const(anon_const),
+ ExprKind::Repeat(ref element, ref count) => {
+ visitor.visit_expr(element);
+ visitor.visit_anon_const(count)
+ }
+ ExprKind::Struct(ref se) => {
+ if let Some(ref qself) = se.qself {
+ visitor.visit_ty(&qself.ty);
+ }
+ visitor.visit_path(&se.path, expression.id);
+ walk_list!(visitor, visit_expr_field, &se.fields);
+ match &se.rest {
+ StructRest::Base(expr) => visitor.visit_expr(expr),
+ StructRest::Rest(_span) => {}
+ StructRest::None => {}
+ }
+ }
+ ExprKind::Tup(ref subexpressions) => {
+ walk_list!(visitor, visit_expr, subexpressions);
+ }
+ ExprKind::Call(ref callee_expression, ref arguments) => {
+ visitor.visit_expr(callee_expression);
+ walk_list!(visitor, visit_expr, arguments);
+ }
+ ExprKind::MethodCall(ref segment, ref arguments, _span) => {
+ visitor.visit_path_segment(expression.span, segment);
+ walk_list!(visitor, visit_expr, arguments);
+ }
+ ExprKind::Binary(_, ref left_expression, ref right_expression) => {
+ visitor.visit_expr(left_expression);
+ visitor.visit_expr(right_expression)
+ }
+ ExprKind::AddrOf(_, _, ref subexpression) | ExprKind::Unary(_, ref subexpression) => {
+ visitor.visit_expr(subexpression)
+ }
+ ExprKind::Cast(ref subexpression, ref typ) | ExprKind::Type(ref subexpression, ref typ) => {
+ visitor.visit_expr(subexpression);
+ visitor.visit_ty(typ)
+ }
+ ExprKind::Let(ref pat, ref expr, _) => {
+ visitor.visit_pat(pat);
+ visitor.visit_expr(expr);
+ }
+ ExprKind::If(ref head_expression, ref if_block, ref optional_else) => {
+ visitor.visit_expr(head_expression);
+ visitor.visit_block(if_block);
+ walk_list!(visitor, visit_expr, optional_else);
+ }
+ ExprKind::While(ref subexpression, ref block, ref opt_label) => {
+ walk_list!(visitor, visit_label, opt_label);
+ visitor.visit_expr(subexpression);
+ visitor.visit_block(block);
+ }
+ ExprKind::ForLoop(ref pattern, ref subexpression, ref block, ref opt_label) => {
+ walk_list!(visitor, visit_label, opt_label);
+ visitor.visit_pat(pattern);
+ visitor.visit_expr(subexpression);
+ visitor.visit_block(block);
+ }
+ ExprKind::Loop(ref block, ref opt_label) => {
+ walk_list!(visitor, visit_label, opt_label);
+ visitor.visit_block(block);
+ }
+ ExprKind::Match(ref subexpression, ref arms) => {
+ visitor.visit_expr(subexpression);
+ walk_list!(visitor, visit_arm, arms);
+ }
+ ExprKind::Closure(ref binder, _, _, _, ref decl, ref body, _decl_span) => {
+ visitor.visit_fn(FnKind::Closure(binder, decl, body), expression.span, expression.id)
+ }
+ ExprKind::Block(ref block, ref opt_label) => {
+ walk_list!(visitor, visit_label, opt_label);
+ visitor.visit_block(block);
+ }
+ ExprKind::Async(_, _, ref body) => {
+ visitor.visit_block(body);
+ }
+ ExprKind::Await(ref expr) => visitor.visit_expr(expr),
+ ExprKind::Assign(ref lhs, ref rhs, _) => {
+ visitor.visit_expr(lhs);
+ visitor.visit_expr(rhs);
+ }
+ ExprKind::AssignOp(_, ref left_expression, ref right_expression) => {
+ visitor.visit_expr(left_expression);
+ visitor.visit_expr(right_expression);
+ }
+ ExprKind::Field(ref subexpression, ident) => {
+ visitor.visit_expr(subexpression);
+ visitor.visit_ident(ident);
+ }
+ ExprKind::Index(ref main_expression, ref index_expression) => {
+ visitor.visit_expr(main_expression);
+ visitor.visit_expr(index_expression)
+ }
+ ExprKind::Range(ref start, ref end, _) => {
+ walk_list!(visitor, visit_expr, start);
+ walk_list!(visitor, visit_expr, end);
+ }
+ ExprKind::Underscore => {}
+ ExprKind::Path(ref maybe_qself, ref path) => {
+ if let Some(ref qself) = *maybe_qself {
+ visitor.visit_ty(&qself.ty);
+ }
+ visitor.visit_path(path, expression.id)
+ }
+ ExprKind::Break(ref opt_label, ref opt_expr) => {
+ walk_list!(visitor, visit_label, opt_label);
+ walk_list!(visitor, visit_expr, opt_expr);
+ }
+ ExprKind::Continue(ref opt_label) => {
+ walk_list!(visitor, visit_label, opt_label);
+ }
+ ExprKind::Ret(ref optional_expression) => {
+ walk_list!(visitor, visit_expr, optional_expression);
+ }
+ ExprKind::Yeet(ref optional_expression) => {
+ walk_list!(visitor, visit_expr, optional_expression);
+ }
+ ExprKind::MacCall(ref mac) => visitor.visit_mac_call(mac),
+ ExprKind::Paren(ref subexpression) => visitor.visit_expr(subexpression),
+ ExprKind::InlineAsm(ref asm) => visitor.visit_inline_asm(asm),
+ ExprKind::Yield(ref optional_expression) => {
+ walk_list!(visitor, visit_expr, optional_expression);
+ }
+ ExprKind::Try(ref subexpression) => visitor.visit_expr(subexpression),
+ ExprKind::TryBlock(ref body) => visitor.visit_block(body),
+ ExprKind::Lit(_) | ExprKind::Err => {}
+ }
+
+ visitor.visit_expr_post(expression)
+}
+
+pub fn walk_param<'a, V: Visitor<'a>>(visitor: &mut V, param: &'a Param) {
+ walk_list!(visitor, visit_attribute, param.attrs.iter());
+ visitor.visit_pat(&param.pat);
+ visitor.visit_ty(&param.ty);
+}
+
+pub fn walk_arm<'a, V: Visitor<'a>>(visitor: &mut V, arm: &'a Arm) {
+ visitor.visit_pat(&arm.pat);
+ walk_list!(visitor, visit_expr, &arm.guard);
+ visitor.visit_expr(&arm.body);
+ walk_list!(visitor, visit_attribute, &arm.attrs);
+}
+
+pub fn walk_vis<'a, V: Visitor<'a>>(visitor: &mut V, vis: &'a Visibility) {
+ if let VisibilityKind::Restricted { ref path, id } = vis.kind {
+ visitor.visit_path(path, id);
+ }
+}
+
+pub fn walk_attribute<'a, V: Visitor<'a>>(visitor: &mut V, attr: &'a Attribute) {
+ match attr.kind {
+ AttrKind::Normal(ref item, ref _tokens) => walk_mac_args(visitor, &item.args),
+ AttrKind::DocComment(..) => {}
+ }
+}
+
+pub fn walk_mac_args<'a, V: Visitor<'a>>(visitor: &mut V, args: &'a MacArgs) {
+ match args {
+ MacArgs::Empty => {}
+ MacArgs::Delimited(_dspan, _delim, _tokens) => {}
+ MacArgs::Eq(_eq_span, MacArgsEq::Ast(expr)) => visitor.visit_expr(expr),
+ MacArgs::Eq(_, MacArgsEq::Hir(lit)) => {
+ unreachable!("in literal form when walking mac args eq: {:?}", lit)
+ }
+ }
+}
diff --git a/compiler/rustc_ast_lowering/Cargo.toml b/compiler/rustc_ast_lowering/Cargo.toml
new file mode 100644
index 000000000..39ba62ef2
--- /dev/null
+++ b/compiler/rustc_ast_lowering/Cargo.toml
@@ -0,0 +1,23 @@
+[package]
+name = "rustc_ast_lowering"
+version = "0.0.0"
+edition = "2021"
+
+[lib]
+doctest = false
+
+[dependencies]
+rustc_arena = { path = "../rustc_arena" }
+tracing = "0.1"
+rustc_ast_pretty = { path = "../rustc_ast_pretty" }
+rustc_hir = { path = "../rustc_hir" }
+rustc_target = { path = "../rustc_target" }
+rustc_data_structures = { path = "../rustc_data_structures" }
+rustc_index = { path = "../rustc_index" }
+rustc_middle = { path = "../rustc_middle" }
+rustc_query_system = { path = "../rustc_query_system" }
+rustc_span = { path = "../rustc_span" }
+rustc_errors = { path = "../rustc_errors" }
+rustc_session = { path = "../rustc_session" }
+rustc_ast = { path = "../rustc_ast" }
+smallvec = { version = "1.8.1", features = ["union", "may_dangle"] }
diff --git a/compiler/rustc_ast_lowering/src/asm.rs b/compiler/rustc_ast_lowering/src/asm.rs
new file mode 100644
index 000000000..4166b4fc2
--- /dev/null
+++ b/compiler/rustc_ast_lowering/src/asm.rs
@@ -0,0 +1,485 @@
+use crate::{ImplTraitContext, ImplTraitPosition, ParamMode, ResolverAstLoweringExt};
+
+use super::LoweringContext;
+
+use rustc_ast::ptr::P;
+use rustc_ast::*;
+use rustc_data_structures::fx::{FxHashMap, FxHashSet};
+use rustc_errors::struct_span_err;
+use rustc_hir as hir;
+use rustc_hir::def::{DefKind, Res};
+use rustc_hir::definitions::DefPathData;
+use rustc_session::parse::feature_err;
+use rustc_span::{sym, Span};
+use rustc_target::asm;
+use std::collections::hash_map::Entry;
+use std::fmt::Write;
+
+impl<'a, 'hir> LoweringContext<'a, 'hir> {
+ pub(crate) fn lower_inline_asm(
+ &mut self,
+ sp: Span,
+ asm: &InlineAsm,
+ ) -> &'hir hir::InlineAsm<'hir> {
+ // Rustdoc needs to support asm! from foreign architectures: don't try
+ // lowering the register constraints in this case.
+ let asm_arch =
+ if self.tcx.sess.opts.actually_rustdoc { None } else { self.tcx.sess.asm_arch };
+ if asm_arch.is_none() && !self.tcx.sess.opts.actually_rustdoc {
+ struct_span_err!(
+ self.tcx.sess,
+ sp,
+ E0472,
+ "inline assembly is unsupported on this target"
+ )
+ .emit();
+ }
+ if let Some(asm_arch) = asm_arch {
+ // Inline assembly is currently only stable for these architectures.
+ let is_stable = matches!(
+ asm_arch,
+ asm::InlineAsmArch::X86
+ | asm::InlineAsmArch::X86_64
+ | asm::InlineAsmArch::Arm
+ | asm::InlineAsmArch::AArch64
+ | asm::InlineAsmArch::RiscV32
+ | asm::InlineAsmArch::RiscV64
+ );
+ if !is_stable && !self.tcx.features().asm_experimental_arch {
+ feature_err(
+ &self.tcx.sess.parse_sess,
+ sym::asm_experimental_arch,
+ sp,
+ "inline assembly is not stable yet on this architecture",
+ )
+ .emit();
+ }
+ }
+ if asm.options.contains(InlineAsmOptions::ATT_SYNTAX)
+ && !matches!(asm_arch, Some(asm::InlineAsmArch::X86 | asm::InlineAsmArch::X86_64))
+ && !self.tcx.sess.opts.actually_rustdoc
+ {
+ self.tcx
+ .sess
+ .struct_span_err(sp, "the `att_syntax` option is only supported on x86")
+ .emit();
+ }
+ if asm.options.contains(InlineAsmOptions::MAY_UNWIND) && !self.tcx.features().asm_unwind {
+ feature_err(
+ &self.tcx.sess.parse_sess,
+ sym::asm_unwind,
+ sp,
+ "the `may_unwind` option is unstable",
+ )
+ .emit();
+ }
+
+ let mut clobber_abis = FxHashMap::default();
+ if let Some(asm_arch) = asm_arch {
+ for (abi_name, abi_span) in &asm.clobber_abis {
+ match asm::InlineAsmClobberAbi::parse(asm_arch, &self.tcx.sess.target, *abi_name) {
+ Ok(abi) => {
+ // If the abi was already in the list, emit an error
+ match clobber_abis.get(&abi) {
+ Some((prev_name, prev_sp)) => {
+ let mut err = self.tcx.sess.struct_span_err(
+ *abi_span,
+ &format!("`{}` ABI specified multiple times", prev_name),
+ );
+ err.span_label(*prev_sp, "previously specified here");
+
+ // Multiple different abi names may actually be the same ABI
+ // If the specified ABIs are not the same name, alert the user that they resolve to the same ABI
+ let source_map = self.tcx.sess.source_map();
+ if source_map.span_to_snippet(*prev_sp)
+ != source_map.span_to_snippet(*abi_span)
+ {
+ err.note("these ABIs are equivalent on the current target");
+ }
+
+ err.emit();
+ }
+ None => {
+ clobber_abis.insert(abi, (abi_name, *abi_span));
+ }
+ }
+ }
+ Err(&[]) => {
+ self.tcx
+ .sess
+ .struct_span_err(
+ *abi_span,
+ "`clobber_abi` is not supported on this target",
+ )
+ .emit();
+ }
+ Err(supported_abis) => {
+ let mut err = self
+ .tcx
+ .sess
+ .struct_span_err(*abi_span, "invalid ABI for `clobber_abi`");
+ let mut abis = format!("`{}`", supported_abis[0]);
+ for m in &supported_abis[1..] {
+ let _ = write!(abis, ", `{}`", m);
+ }
+ err.note(&format!(
+ "the following ABIs are supported on this target: {}",
+ abis
+ ));
+ err.emit();
+ }
+ }
+ }
+ }
+
+ // Lower operands to HIR. We use dummy register classes if an error
+ // occurs during lowering because we still need to be able to produce a
+ // valid HIR.
+ let sess = self.tcx.sess;
+ let mut operands: Vec<_> = asm
+ .operands
+ .iter()
+ .map(|(op, op_sp)| {
+ let lower_reg = |reg| match reg {
+ InlineAsmRegOrRegClass::Reg(s) => {
+ asm::InlineAsmRegOrRegClass::Reg(if let Some(asm_arch) = asm_arch {
+ asm::InlineAsmReg::parse(asm_arch, s).unwrap_or_else(|e| {
+ let msg = format!("invalid register `{}`: {}", s, e);
+ sess.struct_span_err(*op_sp, &msg).emit();
+ asm::InlineAsmReg::Err
+ })
+ } else {
+ asm::InlineAsmReg::Err
+ })
+ }
+ InlineAsmRegOrRegClass::RegClass(s) => {
+ asm::InlineAsmRegOrRegClass::RegClass(if let Some(asm_arch) = asm_arch {
+ asm::InlineAsmRegClass::parse(asm_arch, s).unwrap_or_else(|e| {
+ let msg = format!("invalid register class `{}`: {}", s, e);
+ sess.struct_span_err(*op_sp, &msg).emit();
+ asm::InlineAsmRegClass::Err
+ })
+ } else {
+ asm::InlineAsmRegClass::Err
+ })
+ }
+ };
+
+ let op = match *op {
+ InlineAsmOperand::In { reg, ref expr } => hir::InlineAsmOperand::In {
+ reg: lower_reg(reg),
+ expr: self.lower_expr_mut(expr),
+ },
+ InlineAsmOperand::Out { reg, late, ref expr } => hir::InlineAsmOperand::Out {
+ reg: lower_reg(reg),
+ late,
+ expr: expr.as_ref().map(|expr| self.lower_expr_mut(expr)),
+ },
+ InlineAsmOperand::InOut { reg, late, ref expr } => {
+ hir::InlineAsmOperand::InOut {
+ reg: lower_reg(reg),
+ late,
+ expr: self.lower_expr_mut(expr),
+ }
+ }
+ InlineAsmOperand::SplitInOut { reg, late, ref in_expr, ref out_expr } => {
+ hir::InlineAsmOperand::SplitInOut {
+ reg: lower_reg(reg),
+ late,
+ in_expr: self.lower_expr_mut(in_expr),
+ out_expr: out_expr.as_ref().map(|expr| self.lower_expr_mut(expr)),
+ }
+ }
+ InlineAsmOperand::Const { ref anon_const } => {
+ if !self.tcx.features().asm_const {
+ feature_err(
+ &sess.parse_sess,
+ sym::asm_const,
+ *op_sp,
+ "const operands for inline assembly are unstable",
+ )
+ .emit();
+ }
+ hir::InlineAsmOperand::Const {
+ anon_const: self.lower_anon_const(anon_const),
+ }
+ }
+ InlineAsmOperand::Sym { ref sym } => {
+ if !self.tcx.features().asm_sym {
+ feature_err(
+ &sess.parse_sess,
+ sym::asm_sym,
+ *op_sp,
+ "sym operands for inline assembly are unstable",
+ )
+ .emit();
+ }
+
+ let static_def_id = self
+ .resolver
+ .get_partial_res(sym.id)
+ .filter(|res| res.unresolved_segments() == 0)
+ .and_then(|res| {
+ if let Res::Def(DefKind::Static(_), def_id) = res.base_res() {
+ Some(def_id)
+ } else {
+ None
+ }
+ });
+
+ if let Some(def_id) = static_def_id {
+ let path = self.lower_qpath(
+ sym.id,
+ &sym.qself,
+ &sym.path,
+ ParamMode::Optional,
+ ImplTraitContext::Disallowed(ImplTraitPosition::Path),
+ );
+ hir::InlineAsmOperand::SymStatic { path, def_id }
+ } else {
+ // Replace the InlineAsmSym AST node with an
+ // Expr using the name node id.
+ let expr = Expr {
+ id: sym.id,
+ kind: ExprKind::Path(sym.qself.clone(), sym.path.clone()),
+ span: *op_sp,
+ attrs: AttrVec::new(),
+ tokens: None,
+ };
+
+ // Wrap the expression in an AnonConst.
+ let parent_def_id = self.current_hir_id_owner;
+ let node_id = self.next_node_id();
+ self.create_def(parent_def_id, node_id, DefPathData::AnonConst);
+ let anon_const = AnonConst { id: node_id, value: P(expr) };
+ hir::InlineAsmOperand::SymFn {
+ anon_const: self.lower_anon_const(&anon_const),
+ }
+ }
+ }
+ };
+ (op, self.lower_span(*op_sp))
+ })
+ .collect();
+
+ // Validate template modifiers against the register classes for the operands
+ for p in &asm.template {
+ if let InlineAsmTemplatePiece::Placeholder {
+ operand_idx,
+ modifier: Some(modifier),
+ span: placeholder_span,
+ } = *p
+ {
+ let op_sp = asm.operands[operand_idx].1;
+ match &operands[operand_idx].0 {
+ hir::InlineAsmOperand::In { reg, .. }
+ | hir::InlineAsmOperand::Out { reg, .. }
+ | hir::InlineAsmOperand::InOut { reg, .. }
+ | hir::InlineAsmOperand::SplitInOut { reg, .. } => {
+ let class = reg.reg_class();
+ if class == asm::InlineAsmRegClass::Err {
+ continue;
+ }
+ let valid_modifiers = class.valid_modifiers(asm_arch.unwrap());
+ if !valid_modifiers.contains(&modifier) {
+ let mut err = sess.struct_span_err(
+ placeholder_span,
+ "invalid asm template modifier for this register class",
+ );
+ err.span_label(placeholder_span, "template modifier");
+ err.span_label(op_sp, "argument");
+ if !valid_modifiers.is_empty() {
+ let mut mods = format!("`{}`", valid_modifiers[0]);
+ for m in &valid_modifiers[1..] {
+ let _ = write!(mods, ", `{}`", m);
+ }
+ err.note(&format!(
+ "the `{}` register class supports \
+ the following template modifiers: {}",
+ class.name(),
+ mods
+ ));
+ } else {
+ err.note(&format!(
+ "the `{}` register class does not support template modifiers",
+ class.name()
+ ));
+ }
+ err.emit();
+ }
+ }
+ hir::InlineAsmOperand::Const { .. } => {
+ let mut err = sess.struct_span_err(
+ placeholder_span,
+ "asm template modifiers are not allowed for `const` arguments",
+ );
+ err.span_label(placeholder_span, "template modifier");
+ err.span_label(op_sp, "argument");
+ err.emit();
+ }
+ hir::InlineAsmOperand::SymFn { .. }
+ | hir::InlineAsmOperand::SymStatic { .. } => {
+ let mut err = sess.struct_span_err(
+ placeholder_span,
+ "asm template modifiers are not allowed for `sym` arguments",
+ );
+ err.span_label(placeholder_span, "template modifier");
+ err.span_label(op_sp, "argument");
+ err.emit();
+ }
+ }
+ }
+ }
+
+ let mut used_input_regs = FxHashMap::default();
+ let mut used_output_regs = FxHashMap::default();
+
+ for (idx, &(ref op, op_sp)) in operands.iter().enumerate() {
+ if let Some(reg) = op.reg() {
+ let reg_class = reg.reg_class();
+ if reg_class == asm::InlineAsmRegClass::Err {
+ continue;
+ }
+
+ // Some register classes can only be used as clobbers. This
+ // means that we disallow passing a value in/out of the asm and
+ // require that the operand name an explicit register, not a
+ // register class.
+ if reg_class.is_clobber_only(asm_arch.unwrap()) && !op.is_clobber() {
+ let msg = format!(
+ "register class `{}` can only be used as a clobber, \
+ not as an input or output",
+ reg_class.name()
+ );
+ sess.struct_span_err(op_sp, &msg).emit();
+ continue;
+ }
+
+ // Check for conflicts between explicit register operands.
+ if let asm::InlineAsmRegOrRegClass::Reg(reg) = reg {
+ let (input, output) = match op {
+ hir::InlineAsmOperand::In { .. } => (true, false),
+
+ // Late output do not conflict with inputs, but normal outputs do
+ hir::InlineAsmOperand::Out { late, .. } => (!late, true),
+
+ hir::InlineAsmOperand::InOut { .. }
+ | hir::InlineAsmOperand::SplitInOut { .. } => (true, true),
+
+ hir::InlineAsmOperand::Const { .. }
+ | hir::InlineAsmOperand::SymFn { .. }
+ | hir::InlineAsmOperand::SymStatic { .. } => {
+ unreachable!()
+ }
+ };
+
+ // Flag to output the error only once per operand
+ let mut skip = false;
+ reg.overlapping_regs(|r| {
+ let mut check = |used_regs: &mut FxHashMap<asm::InlineAsmReg, usize>,
+ input| {
+ match used_regs.entry(r) {
+ Entry::Occupied(o) => {
+ if skip {
+ return;
+ }
+ skip = true;
+
+ let idx2 = *o.get();
+ let &(ref op2, op_sp2) = &operands[idx2];
+ let Some(asm::InlineAsmRegOrRegClass::Reg(reg2)) = op2.reg() else {
+ unreachable!();
+ };
+
+ let msg = format!(
+ "register `{}` conflicts with register `{}`",
+ reg.name(),
+ reg2.name()
+ );
+ let mut err = sess.struct_span_err(op_sp, &msg);
+ err.span_label(op_sp, &format!("register `{}`", reg.name()));
+ err.span_label(op_sp2, &format!("register `{}`", reg2.name()));
+
+ match (op, op2) {
+ (
+ hir::InlineAsmOperand::In { .. },
+ hir::InlineAsmOperand::Out { late, .. },
+ )
+ | (
+ hir::InlineAsmOperand::Out { late, .. },
+ hir::InlineAsmOperand::In { .. },
+ ) => {
+ assert!(!*late);
+ let out_op_sp = if input { op_sp2 } else { op_sp };
+ let msg = "use `lateout` instead of \
+ `out` to avoid conflict";
+ err.span_help(out_op_sp, msg);
+ }
+ _ => {}
+ }
+
+ err.emit();
+ }
+ Entry::Vacant(v) => {
+ if r == reg {
+ v.insert(idx);
+ }
+ }
+ }
+ };
+ if input {
+ check(&mut used_input_regs, true);
+ }
+ if output {
+ check(&mut used_output_regs, false);
+ }
+ });
+ }
+ }
+ }
+
+ // If a clobber_abi is specified, add the necessary clobbers to the
+ // operands list.
+ let mut clobbered = FxHashSet::default();
+ for (abi, (_, abi_span)) in clobber_abis {
+ for &clobber in abi.clobbered_regs() {
+ // Don't emit a clobber for a register already clobbered
+ if clobbered.contains(&clobber) {
+ continue;
+ }
+
+ let mut output_used = false;
+ clobber.overlapping_regs(|reg| {
+ if used_output_regs.contains_key(&reg) {
+ output_used = true;
+ }
+ });
+
+ if !output_used {
+ operands.push((
+ hir::InlineAsmOperand::Out {
+ reg: asm::InlineAsmRegOrRegClass::Reg(clobber),
+ late: true,
+ expr: None,
+ },
+ self.lower_span(abi_span),
+ ));
+ clobbered.insert(clobber);
+ }
+ }
+ }
+
+ let operands = self.arena.alloc_from_iter(operands);
+ let template = self.arena.alloc_from_iter(asm.template.iter().cloned());
+ let template_strs = self.arena.alloc_from_iter(
+ asm.template_strs
+ .iter()
+ .map(|(sym, snippet, span)| (*sym, *snippet, self.lower_span(*span))),
+ );
+ let line_spans =
+ self.arena.alloc_from_iter(asm.line_spans.iter().map(|span| self.lower_span(*span)));
+ let hir_asm =
+ hir::InlineAsm { template, template_strs, operands, options: asm.options, line_spans };
+ self.arena.alloc(hir_asm)
+ }
+}
diff --git a/compiler/rustc_ast_lowering/src/block.rs b/compiler/rustc_ast_lowering/src/block.rs
new file mode 100644
index 000000000..7cbfe143b
--- /dev/null
+++ b/compiler/rustc_ast_lowering/src/block.rs
@@ -0,0 +1,122 @@
+use crate::{ImplTraitContext, ImplTraitPosition, LoweringContext};
+use rustc_ast::{Block, BlockCheckMode, Local, LocalKind, Stmt, StmtKind};
+use rustc_hir as hir;
+use rustc_session::parse::feature_err;
+use rustc_span::sym;
+
+use smallvec::SmallVec;
+
+impl<'a, 'hir> LoweringContext<'a, 'hir> {
+ pub(super) fn lower_block(
+ &mut self,
+ b: &Block,
+ targeted_by_break: bool,
+ ) -> &'hir hir::Block<'hir> {
+ self.arena.alloc(self.lower_block_noalloc(b, targeted_by_break))
+ }
+
+ pub(super) fn lower_block_noalloc(
+ &mut self,
+ b: &Block,
+ targeted_by_break: bool,
+ ) -> hir::Block<'hir> {
+ let (stmts, expr) = self.lower_stmts(&b.stmts);
+ let rules = self.lower_block_check_mode(&b.rules);
+ let hir_id = self.lower_node_id(b.id);
+ hir::Block { hir_id, stmts, expr, rules, span: self.lower_span(b.span), targeted_by_break }
+ }
+
+ fn lower_stmts(
+ &mut self,
+ mut ast_stmts: &[Stmt],
+ ) -> (&'hir [hir::Stmt<'hir>], Option<&'hir hir::Expr<'hir>>) {
+ let mut stmts = SmallVec::<[hir::Stmt<'hir>; 8]>::new();
+ let mut expr = None;
+ while let [s, tail @ ..] = ast_stmts {
+ match s.kind {
+ StmtKind::Local(ref local) => {
+ let hir_id = self.lower_node_id(s.id);
+ let local = self.lower_local(local);
+ self.alias_attrs(hir_id, local.hir_id);
+ let kind = hir::StmtKind::Local(local);
+ let span = self.lower_span(s.span);
+ stmts.push(hir::Stmt { hir_id, kind, span });
+ }
+ StmtKind::Item(ref it) => {
+ stmts.extend(self.lower_item_ref(it).into_iter().enumerate().map(
+ |(i, item_id)| {
+ let hir_id = match i {
+ 0 => self.lower_node_id(s.id),
+ _ => self.next_id(),
+ };
+ let kind = hir::StmtKind::Item(item_id);
+ let span = self.lower_span(s.span);
+ hir::Stmt { hir_id, kind, span }
+ },
+ ));
+ }
+ StmtKind::Expr(ref e) => {
+ let e = self.lower_expr(e);
+ if tail.is_empty() {
+ expr = Some(e);
+ } else {
+ let hir_id = self.lower_node_id(s.id);
+ self.alias_attrs(hir_id, e.hir_id);
+ let kind = hir::StmtKind::Expr(e);
+ let span = self.lower_span(s.span);
+ stmts.push(hir::Stmt { hir_id, kind, span });
+ }
+ }
+ StmtKind::Semi(ref e) => {
+ let e = self.lower_expr(e);
+ let hir_id = self.lower_node_id(s.id);
+ self.alias_attrs(hir_id, e.hir_id);
+ let kind = hir::StmtKind::Semi(e);
+ let span = self.lower_span(s.span);
+ stmts.push(hir::Stmt { hir_id, kind, span });
+ }
+ StmtKind::Empty => {}
+ StmtKind::MacCall(..) => panic!("shouldn't exist here"),
+ }
+ ast_stmts = &ast_stmts[1..];
+ }
+ (self.arena.alloc_from_iter(stmts), expr)
+ }
+
+ fn lower_local(&mut self, l: &Local) -> &'hir hir::Local<'hir> {
+ let ty = l
+ .ty
+ .as_ref()
+ .map(|t| self.lower_ty(t, ImplTraitContext::Disallowed(ImplTraitPosition::Variable)));
+ let init = l.kind.init().map(|init| self.lower_expr(init));
+ let hir_id = self.lower_node_id(l.id);
+ let pat = self.lower_pat(&l.pat);
+ let els = if let LocalKind::InitElse(_, els) = &l.kind {
+ if !self.tcx.features().let_else {
+ feature_err(
+ &self.tcx.sess.parse_sess,
+ sym::let_else,
+ l.span,
+ "`let...else` statements are unstable",
+ )
+ .emit();
+ }
+ Some(self.lower_block(els, false))
+ } else {
+ None
+ };
+ let span = self.lower_span(l.span);
+ let source = hir::LocalSource::Normal;
+ self.lower_attrs(hir_id, &l.attrs);
+ self.arena.alloc(hir::Local { hir_id, ty, pat, init, els, span, source })
+ }
+
+ fn lower_block_check_mode(&mut self, b: &BlockCheckMode) -> hir::BlockCheckMode {
+ match *b {
+ BlockCheckMode::Default => hir::BlockCheckMode::DefaultBlock,
+ BlockCheckMode::Unsafe(u) => {
+ hir::BlockCheckMode::UnsafeBlock(self.lower_unsafe_source(u))
+ }
+ }
+ }
+}
diff --git a/compiler/rustc_ast_lowering/src/expr.rs b/compiler/rustc_ast_lowering/src/expr.rs
new file mode 100644
index 000000000..fb6715ff1
--- /dev/null
+++ b/compiler/rustc_ast_lowering/src/expr.rs
@@ -0,0 +1,1914 @@
+use super::ResolverAstLoweringExt;
+use super::{ImplTraitContext, LoweringContext, ParamMode, ParenthesizedGenericArgs};
+use crate::{FnDeclKind, ImplTraitPosition};
+
+use rustc_ast::attr;
+use rustc_ast::ptr::P as AstP;
+use rustc_ast::*;
+use rustc_data_structures::stack::ensure_sufficient_stack;
+use rustc_data_structures::thin_vec::ThinVec;
+use rustc_errors::struct_span_err;
+use rustc_hir as hir;
+use rustc_hir::def::Res;
+use rustc_hir::definitions::DefPathData;
+use rustc_span::source_map::{respan, DesugaringKind, Span, Spanned};
+use rustc_span::symbol::{sym, Ident};
+use rustc_span::DUMMY_SP;
+
+impl<'hir> LoweringContext<'_, 'hir> {
+ fn lower_exprs(&mut self, exprs: &[AstP<Expr>]) -> &'hir [hir::Expr<'hir>] {
+ self.arena.alloc_from_iter(exprs.iter().map(|x| self.lower_expr_mut(x)))
+ }
+
+ pub(super) fn lower_expr(&mut self, e: &Expr) -> &'hir hir::Expr<'hir> {
+ self.arena.alloc(self.lower_expr_mut(e))
+ }
+
+ pub(super) fn lower_expr_mut(&mut self, e: &Expr) -> hir::Expr<'hir> {
+ ensure_sufficient_stack(|| {
+ let kind = match e.kind {
+ ExprKind::Box(ref inner) => hir::ExprKind::Box(self.lower_expr(inner)),
+ ExprKind::Array(ref exprs) => hir::ExprKind::Array(self.lower_exprs(exprs)),
+ ExprKind::ConstBlock(ref anon_const) => {
+ let anon_const = self.lower_anon_const(anon_const);
+ hir::ExprKind::ConstBlock(anon_const)
+ }
+ ExprKind::Repeat(ref expr, ref count) => {
+ let expr = self.lower_expr(expr);
+ let count = self.lower_array_length(count);
+ hir::ExprKind::Repeat(expr, count)
+ }
+ ExprKind::Tup(ref elts) => hir::ExprKind::Tup(self.lower_exprs(elts)),
+ ExprKind::Call(ref f, ref args) => {
+ if e.attrs.get(0).map_or(false, |a| a.has_name(sym::rustc_box)) {
+ if let [inner] = &args[..] && e.attrs.len() == 1 {
+ let kind = hir::ExprKind::Box(self.lower_expr(&inner));
+ let hir_id = self.lower_node_id(e.id);
+ return hir::Expr { hir_id, kind, span: self.lower_span(e.span) };
+ } else {
+ self.tcx.sess
+ .struct_span_err(
+ e.span,
+ "#[rustc_box] requires precisely one argument \
+ and no other attributes are allowed",
+ )
+ .emit();
+ hir::ExprKind::Err
+ }
+ } else if let Some(legacy_args) = self.resolver.legacy_const_generic_args(f) {
+ self.lower_legacy_const_generics((**f).clone(), args.clone(), &legacy_args)
+ } else {
+ let f = self.lower_expr(f);
+ hir::ExprKind::Call(f, self.lower_exprs(args))
+ }
+ }
+ ExprKind::MethodCall(ref seg, ref args, span) => {
+ let hir_seg = self.arena.alloc(self.lower_path_segment(
+ e.span,
+ seg,
+ ParamMode::Optional,
+ ParenthesizedGenericArgs::Err,
+ ImplTraitContext::Disallowed(ImplTraitPosition::Path),
+ ));
+ let args = self.lower_exprs(args);
+ hir::ExprKind::MethodCall(hir_seg, args, self.lower_span(span))
+ }
+ ExprKind::Binary(binop, ref lhs, ref rhs) => {
+ let binop = self.lower_binop(binop);
+ let lhs = self.lower_expr(lhs);
+ let rhs = self.lower_expr(rhs);
+ hir::ExprKind::Binary(binop, lhs, rhs)
+ }
+ ExprKind::Unary(op, ref ohs) => {
+ let op = self.lower_unop(op);
+ let ohs = self.lower_expr(ohs);
+ hir::ExprKind::Unary(op, ohs)
+ }
+ ExprKind::Lit(ref l) => {
+ hir::ExprKind::Lit(respan(self.lower_span(l.span), l.kind.clone()))
+ }
+ ExprKind::Cast(ref expr, ref ty) => {
+ let expr = self.lower_expr(expr);
+ let ty =
+ self.lower_ty(ty, ImplTraitContext::Disallowed(ImplTraitPosition::Type));
+ hir::ExprKind::Cast(expr, ty)
+ }
+ ExprKind::Type(ref expr, ref ty) => {
+ let expr = self.lower_expr(expr);
+ let ty =
+ self.lower_ty(ty, ImplTraitContext::Disallowed(ImplTraitPosition::Type));
+ hir::ExprKind::Type(expr, ty)
+ }
+ ExprKind::AddrOf(k, m, ref ohs) => {
+ let ohs = self.lower_expr(ohs);
+ hir::ExprKind::AddrOf(k, m, ohs)
+ }
+ ExprKind::Let(ref pat, ref scrutinee, span) => {
+ hir::ExprKind::Let(self.arena.alloc(hir::Let {
+ hir_id: self.next_id(),
+ span: self.lower_span(span),
+ pat: self.lower_pat(pat),
+ ty: None,
+ init: self.lower_expr(scrutinee),
+ }))
+ }
+ ExprKind::If(ref cond, ref then, ref else_opt) => {
+ self.lower_expr_if(cond, then, else_opt.as_deref())
+ }
+ ExprKind::While(ref cond, ref body, opt_label) => {
+ self.with_loop_scope(e.id, |this| {
+ let span =
+ this.mark_span_with_reason(DesugaringKind::WhileLoop, e.span, None);
+ this.lower_expr_while_in_loop_scope(span, cond, body, opt_label)
+ })
+ }
+ ExprKind::Loop(ref body, opt_label) => self.with_loop_scope(e.id, |this| {
+ hir::ExprKind::Loop(
+ this.lower_block(body, false),
+ this.lower_label(opt_label),
+ hir::LoopSource::Loop,
+ DUMMY_SP,
+ )
+ }),
+ ExprKind::TryBlock(ref body) => self.lower_expr_try_block(body),
+ ExprKind::Match(ref expr, ref arms) => hir::ExprKind::Match(
+ self.lower_expr(expr),
+ self.arena.alloc_from_iter(arms.iter().map(|x| self.lower_arm(x))),
+ hir::MatchSource::Normal,
+ ),
+ ExprKind::Async(capture_clause, closure_node_id, ref block) => self
+ .make_async_expr(
+ capture_clause,
+ closure_node_id,
+ None,
+ block.span,
+ hir::AsyncGeneratorKind::Block,
+ |this| this.with_new_scopes(|this| this.lower_block_expr(block)),
+ ),
+ ExprKind::Await(ref expr) => {
+ let span = if expr.span.hi() < e.span.hi() {
+ expr.span.shrink_to_hi().with_hi(e.span.hi())
+ } else {
+ // this is a recovered `await expr`
+ e.span
+ };
+ self.lower_expr_await(span, expr)
+ }
+ ExprKind::Closure(
+ ref binder,
+ capture_clause,
+ asyncness,
+ movability,
+ ref decl,
+ ref body,
+ fn_decl_span,
+ ) => {
+ if let Async::Yes { closure_id, .. } = asyncness {
+ self.lower_expr_async_closure(
+ binder,
+ capture_clause,
+ e.id,
+ closure_id,
+ decl,
+ body,
+ fn_decl_span,
+ )
+ } else {
+ self.lower_expr_closure(
+ binder,
+ capture_clause,
+ e.id,
+ movability,
+ decl,
+ body,
+ fn_decl_span,
+ )
+ }
+ }
+ ExprKind::Block(ref blk, opt_label) => {
+ let opt_label = self.lower_label(opt_label);
+ hir::ExprKind::Block(self.lower_block(blk, opt_label.is_some()), opt_label)
+ }
+ ExprKind::Assign(ref el, ref er, span) => {
+ self.lower_expr_assign(el, er, span, e.span)
+ }
+ ExprKind::AssignOp(op, ref el, ref er) => hir::ExprKind::AssignOp(
+ self.lower_binop(op),
+ self.lower_expr(el),
+ self.lower_expr(er),
+ ),
+ ExprKind::Field(ref el, ident) => {
+ hir::ExprKind::Field(self.lower_expr(el), self.lower_ident(ident))
+ }
+ ExprKind::Index(ref el, ref er) => {
+ hir::ExprKind::Index(self.lower_expr(el), self.lower_expr(er))
+ }
+ ExprKind::Range(Some(ref e1), Some(ref e2), RangeLimits::Closed) => {
+ self.lower_expr_range_closed(e.span, e1, e2)
+ }
+ ExprKind::Range(ref e1, ref e2, lims) => {
+ self.lower_expr_range(e.span, e1.as_deref(), e2.as_deref(), lims)
+ }
+ ExprKind::Underscore => {
+ self.tcx
+ .sess.struct_span_err(
+ e.span,
+ "in expressions, `_` can only be used on the left-hand side of an assignment",
+ )
+ .span_label(e.span, "`_` not allowed here")
+ .emit();
+ hir::ExprKind::Err
+ }
+ ExprKind::Path(ref qself, ref path) => {
+ let qpath = self.lower_qpath(
+ e.id,
+ qself,
+ path,
+ ParamMode::Optional,
+ ImplTraitContext::Disallowed(ImplTraitPosition::Path),
+ );
+ hir::ExprKind::Path(qpath)
+ }
+ ExprKind::Break(opt_label, ref opt_expr) => {
+ let opt_expr = opt_expr.as_ref().map(|x| self.lower_expr(x));
+ hir::ExprKind::Break(self.lower_jump_destination(e.id, opt_label), opt_expr)
+ }
+ ExprKind::Continue(opt_label) => {
+ hir::ExprKind::Continue(self.lower_jump_destination(e.id, opt_label))
+ }
+ ExprKind::Ret(ref e) => {
+ let e = e.as_ref().map(|x| self.lower_expr(x));
+ hir::ExprKind::Ret(e)
+ }
+ ExprKind::Yeet(ref sub_expr) => self.lower_expr_yeet(e.span, sub_expr.as_deref()),
+ ExprKind::InlineAsm(ref asm) => {
+ hir::ExprKind::InlineAsm(self.lower_inline_asm(e.span, asm))
+ }
+ ExprKind::Struct(ref se) => {
+ let rest = match &se.rest {
+ StructRest::Base(e) => Some(self.lower_expr(e)),
+ StructRest::Rest(sp) => {
+ self.tcx
+ .sess
+ .struct_span_err(*sp, "base expression required after `..`")
+ .span_label(*sp, "add a base expression here")
+ .emit();
+ Some(&*self.arena.alloc(self.expr_err(*sp)))
+ }
+ StructRest::None => None,
+ };
+ hir::ExprKind::Struct(
+ self.arena.alloc(self.lower_qpath(
+ e.id,
+ &se.qself,
+ &se.path,
+ ParamMode::Optional,
+ ImplTraitContext::Disallowed(ImplTraitPosition::Path),
+ )),
+ self.arena
+ .alloc_from_iter(se.fields.iter().map(|x| self.lower_expr_field(x))),
+ rest,
+ )
+ }
+ ExprKind::Yield(ref opt_expr) => self.lower_expr_yield(e.span, opt_expr.as_deref()),
+ ExprKind::Err => hir::ExprKind::Err,
+ ExprKind::Try(ref sub_expr) => self.lower_expr_try(e.span, sub_expr),
+ ExprKind::Paren(ref ex) => {
+ let mut ex = self.lower_expr_mut(ex);
+ // Include parens in span, but only if it is a super-span.
+ if e.span.contains(ex.span) {
+ ex.span = self.lower_span(e.span);
+ }
+ // Merge attributes into the inner expression.
+ if !e.attrs.is_empty() {
+ let old_attrs =
+ self.attrs.get(&ex.hir_id.local_id).map(|la| *la).unwrap_or(&[]);
+ self.attrs.insert(
+ ex.hir_id.local_id,
+ &*self.arena.alloc_from_iter(
+ e.attrs
+ .iter()
+ .map(|a| self.lower_attr(a))
+ .chain(old_attrs.iter().cloned()),
+ ),
+ );
+ }
+ return ex;
+ }
+
+ // Desugar `ExprForLoop`
+ // from: `[opt_ident]: for <pat> in <head> <body>`
+ ExprKind::ForLoop(ref pat, ref head, ref body, opt_label) => {
+ return self.lower_expr_for(e, pat, head, body, opt_label);
+ }
+ ExprKind::MacCall(_) => panic!("{:?} shouldn't exist here", e.span),
+ };
+
+ let hir_id = self.lower_node_id(e.id);
+ self.lower_attrs(hir_id, &e.attrs);
+ hir::Expr { hir_id, kind, span: self.lower_span(e.span) }
+ })
+ }
+
+ fn lower_unop(&mut self, u: UnOp) -> hir::UnOp {
+ match u {
+ UnOp::Deref => hir::UnOp::Deref,
+ UnOp::Not => hir::UnOp::Not,
+ UnOp::Neg => hir::UnOp::Neg,
+ }
+ }
+
+ fn lower_binop(&mut self, b: BinOp) -> hir::BinOp {
+ Spanned {
+ node: match b.node {
+ BinOpKind::Add => hir::BinOpKind::Add,
+ BinOpKind::Sub => hir::BinOpKind::Sub,
+ BinOpKind::Mul => hir::BinOpKind::Mul,
+ BinOpKind::Div => hir::BinOpKind::Div,
+ BinOpKind::Rem => hir::BinOpKind::Rem,
+ BinOpKind::And => hir::BinOpKind::And,
+ BinOpKind::Or => hir::BinOpKind::Or,
+ BinOpKind::BitXor => hir::BinOpKind::BitXor,
+ BinOpKind::BitAnd => hir::BinOpKind::BitAnd,
+ BinOpKind::BitOr => hir::BinOpKind::BitOr,
+ BinOpKind::Shl => hir::BinOpKind::Shl,
+ BinOpKind::Shr => hir::BinOpKind::Shr,
+ BinOpKind::Eq => hir::BinOpKind::Eq,
+ BinOpKind::Lt => hir::BinOpKind::Lt,
+ BinOpKind::Le => hir::BinOpKind::Le,
+ BinOpKind::Ne => hir::BinOpKind::Ne,
+ BinOpKind::Ge => hir::BinOpKind::Ge,
+ BinOpKind::Gt => hir::BinOpKind::Gt,
+ },
+ span: self.lower_span(b.span),
+ }
+ }
+
+ fn lower_legacy_const_generics(
+ &mut self,
+ mut f: Expr,
+ args: Vec<AstP<Expr>>,
+ legacy_args_idx: &[usize],
+ ) -> hir::ExprKind<'hir> {
+ let ExprKind::Path(None, ref mut path) = f.kind else {
+ unreachable!();
+ };
+
+ // Split the arguments into const generics and normal arguments
+ let mut real_args = vec![];
+ let mut generic_args = vec![];
+ for (idx, arg) in args.into_iter().enumerate() {
+ if legacy_args_idx.contains(&idx) {
+ let parent_def_id = self.current_hir_id_owner;
+ let node_id = self.next_node_id();
+
+ // Add a definition for the in-band const def.
+ self.create_def(parent_def_id, node_id, DefPathData::AnonConst);
+
+ let anon_const = AnonConst { id: node_id, value: arg };
+ generic_args.push(AngleBracketedArg::Arg(GenericArg::Const(anon_const)));
+ } else {
+ real_args.push(arg);
+ }
+ }
+
+ // Add generic args to the last element of the path.
+ let last_segment = path.segments.last_mut().unwrap();
+ assert!(last_segment.args.is_none());
+ last_segment.args = Some(AstP(GenericArgs::AngleBracketed(AngleBracketedArgs {
+ span: DUMMY_SP,
+ args: generic_args,
+ })));
+
+ // Now lower everything as normal.
+ let f = self.lower_expr(&f);
+ hir::ExprKind::Call(f, self.lower_exprs(&real_args))
+ }
+
+ fn lower_expr_if(
+ &mut self,
+ cond: &Expr,
+ then: &Block,
+ else_opt: Option<&Expr>,
+ ) -> hir::ExprKind<'hir> {
+ let lowered_cond = self.lower_expr(cond);
+ let new_cond = self.manage_let_cond(lowered_cond);
+ let then_expr = self.lower_block_expr(then);
+ if let Some(rslt) = else_opt {
+ hir::ExprKind::If(new_cond, self.arena.alloc(then_expr), Some(self.lower_expr(rslt)))
+ } else {
+ hir::ExprKind::If(new_cond, self.arena.alloc(then_expr), None)
+ }
+ }
+
+ // If `cond` kind is `let`, returns `let`. Otherwise, wraps and returns `cond`
+ // in a temporary block.
+ fn manage_let_cond(&mut self, cond: &'hir hir::Expr<'hir>) -> &'hir hir::Expr<'hir> {
+ fn has_let_expr<'hir>(expr: &'hir hir::Expr<'hir>) -> bool {
+ match expr.kind {
+ hir::ExprKind::Binary(_, lhs, rhs) => has_let_expr(lhs) || has_let_expr(rhs),
+ hir::ExprKind::Let(..) => true,
+ _ => false,
+ }
+ }
+ if has_let_expr(cond) {
+ cond
+ } else {
+ let reason = DesugaringKind::CondTemporary;
+ let span_block = self.mark_span_with_reason(reason, cond.span, None);
+ self.expr_drop_temps(span_block, cond, AttrVec::new())
+ }
+ }
+
+ // We desugar: `'label: while $cond $body` into:
+ //
+ // ```
+ // 'label: loop {
+ // if { let _t = $cond; _t } {
+ // $body
+ // }
+ // else {
+ // break;
+ // }
+ // }
+ // ```
+ //
+ // Wrap in a construct equivalent to `{ let _t = $cond; _t }`
+ // to preserve drop semantics since `while $cond { ... }` does not
+ // let temporaries live outside of `cond`.
+ fn lower_expr_while_in_loop_scope(
+ &mut self,
+ span: Span,
+ cond: &Expr,
+ body: &Block,
+ opt_label: Option<Label>,
+ ) -> hir::ExprKind<'hir> {
+ let lowered_cond = self.with_loop_condition_scope(|t| t.lower_expr(cond));
+ let new_cond = self.manage_let_cond(lowered_cond);
+ let then = self.lower_block_expr(body);
+ let expr_break = self.expr_break(span, ThinVec::new());
+ let stmt_break = self.stmt_expr(span, expr_break);
+ let else_blk = self.block_all(span, arena_vec![self; stmt_break], None);
+ let else_expr = self.arena.alloc(self.expr_block(else_blk, ThinVec::new()));
+ let if_kind = hir::ExprKind::If(new_cond, self.arena.alloc(then), Some(else_expr));
+ let if_expr = self.expr(span, if_kind, ThinVec::new());
+ let block = self.block_expr(self.arena.alloc(if_expr));
+ let span = self.lower_span(span.with_hi(cond.span.hi()));
+ let opt_label = self.lower_label(opt_label);
+ hir::ExprKind::Loop(block, opt_label, hir::LoopSource::While, span)
+ }
+
+ /// Desugar `try { <stmts>; <expr> }` into `{ <stmts>; ::std::ops::Try::from_output(<expr>) }`,
+ /// `try { <stmts>; }` into `{ <stmts>; ::std::ops::Try::from_output(()) }`
+ /// and save the block id to use it as a break target for desugaring of the `?` operator.
+ fn lower_expr_try_block(&mut self, body: &Block) -> hir::ExprKind<'hir> {
+ self.with_catch_scope(body.id, |this| {
+ let mut block = this.lower_block_noalloc(body, true);
+
+ // Final expression of the block (if present) or `()` with span at the end of block
+ let (try_span, tail_expr) = if let Some(expr) = block.expr.take() {
+ (
+ this.mark_span_with_reason(
+ DesugaringKind::TryBlock,
+ expr.span,
+ this.allow_try_trait.clone(),
+ ),
+ expr,
+ )
+ } else {
+ let try_span = this.mark_span_with_reason(
+ DesugaringKind::TryBlock,
+ this.tcx.sess.source_map().end_point(body.span),
+ this.allow_try_trait.clone(),
+ );
+
+ (try_span, this.expr_unit(try_span))
+ };
+
+ let ok_wrapped_span =
+ this.mark_span_with_reason(DesugaringKind::TryBlock, tail_expr.span, None);
+
+ // `::std::ops::Try::from_output($tail_expr)`
+ block.expr = Some(this.wrap_in_try_constructor(
+ hir::LangItem::TryTraitFromOutput,
+ try_span,
+ tail_expr,
+ ok_wrapped_span,
+ ));
+
+ hir::ExprKind::Block(this.arena.alloc(block), None)
+ })
+ }
+
+ fn wrap_in_try_constructor(
+ &mut self,
+ lang_item: hir::LangItem,
+ method_span: Span,
+ expr: &'hir hir::Expr<'hir>,
+ overall_span: Span,
+ ) -> &'hir hir::Expr<'hir> {
+ let constructor = self.arena.alloc(self.expr_lang_item_path(
+ method_span,
+ lang_item,
+ ThinVec::new(),
+ None,
+ ));
+ self.expr_call(overall_span, constructor, std::slice::from_ref(expr))
+ }
+
+ fn lower_arm(&mut self, arm: &Arm) -> hir::Arm<'hir> {
+ let pat = self.lower_pat(&arm.pat);
+ let guard = arm.guard.as_ref().map(|cond| {
+ if let ExprKind::Let(ref pat, ref scrutinee, span) = cond.kind {
+ hir::Guard::IfLet(self.arena.alloc(hir::Let {
+ hir_id: self.next_id(),
+ span: self.lower_span(span),
+ pat: self.lower_pat(pat),
+ ty: None,
+ init: self.lower_expr(scrutinee),
+ }))
+ } else {
+ hir::Guard::If(self.lower_expr(cond))
+ }
+ });
+ let hir_id = self.next_id();
+ self.lower_attrs(hir_id, &arm.attrs);
+ hir::Arm {
+ hir_id,
+ pat,
+ guard,
+ body: self.lower_expr(&arm.body),
+ span: self.lower_span(arm.span),
+ }
+ }
+
+ /// Lower an `async` construct to a generator that is then wrapped so it implements `Future`.
+ ///
+ /// This results in:
+ ///
+ /// ```text
+ /// std::future::from_generator(static move? |_task_context| -> <ret_ty> {
+ /// <body>
+ /// })
+ /// ```
+ pub(super) fn make_async_expr(
+ &mut self,
+ capture_clause: CaptureBy,
+ closure_node_id: NodeId,
+ ret_ty: Option<AstP<Ty>>,
+ span: Span,
+ async_gen_kind: hir::AsyncGeneratorKind,
+ body: impl FnOnce(&mut Self) -> hir::Expr<'hir>,
+ ) -> hir::ExprKind<'hir> {
+ let output = match ret_ty {
+ Some(ty) => hir::FnRetTy::Return(
+ self.lower_ty(&ty, ImplTraitContext::Disallowed(ImplTraitPosition::AsyncBlock)),
+ ),
+ None => hir::FnRetTy::DefaultReturn(self.lower_span(span)),
+ };
+
+ // Resume argument type. We let the compiler infer this to simplify the lowering. It is
+ // fully constrained by `future::from_generator`.
+ let input_ty = hir::Ty {
+ hir_id: self.next_id(),
+ kind: hir::TyKind::Infer,
+ span: self.lower_span(span),
+ };
+
+ // The closure/generator `FnDecl` takes a single (resume) argument of type `input_ty`.
+ let fn_decl = self.arena.alloc(hir::FnDecl {
+ inputs: arena_vec![self; input_ty],
+ output,
+ c_variadic: false,
+ implicit_self: hir::ImplicitSelfKind::None,
+ });
+
+ // Lower the argument pattern/ident. The ident is used again in the `.await` lowering.
+ let (pat, task_context_hid) = self.pat_ident_binding_mode(
+ span,
+ Ident::with_dummy_span(sym::_task_context),
+ hir::BindingAnnotation::Mutable,
+ );
+ let param = hir::Param {
+ hir_id: self.next_id(),
+ pat,
+ ty_span: self.lower_span(span),
+ span: self.lower_span(span),
+ };
+ let params = arena_vec![self; param];
+
+ let body = self.lower_body(move |this| {
+ this.generator_kind = Some(hir::GeneratorKind::Async(async_gen_kind));
+
+ let old_ctx = this.task_context;
+ this.task_context = Some(task_context_hid);
+ let res = body(this);
+ this.task_context = old_ctx;
+ (params, res)
+ });
+
+ // `static |_task_context| -> <ret_ty> { body }`:
+ let generator_kind = {
+ let c = self.arena.alloc(hir::Closure {
+ binder: hir::ClosureBinder::Default,
+ capture_clause,
+ bound_generic_params: &[],
+ fn_decl,
+ body,
+ fn_decl_span: self.lower_span(span),
+ movability: Some(hir::Movability::Static),
+ });
+
+ hir::ExprKind::Closure(c)
+ };
+ let generator = hir::Expr {
+ hir_id: self.lower_node_id(closure_node_id),
+ kind: generator_kind,
+ span: self.lower_span(span),
+ };
+
+ // `future::from_generator`:
+ let unstable_span =
+ self.mark_span_with_reason(DesugaringKind::Async, span, self.allow_gen_future.clone());
+ let gen_future = self.expr_lang_item_path(
+ unstable_span,
+ hir::LangItem::FromGenerator,
+ ThinVec::new(),
+ None,
+ );
+
+ // `future::from_generator(generator)`:
+ hir::ExprKind::Call(self.arena.alloc(gen_future), arena_vec![self; generator])
+ }
+
+ /// Desugar `<expr>.await` into:
+ /// ```ignore (pseudo-rust)
+ /// match ::std::future::IntoFuture::into_future(<expr>) {
+ /// mut __awaitee => loop {
+ /// match unsafe { ::std::future::Future::poll(
+ /// <::std::pin::Pin>::new_unchecked(&mut __awaitee),
+ /// ::std::future::get_context(task_context),
+ /// ) } {
+ /// ::std::task::Poll::Ready(result) => break result,
+ /// ::std::task::Poll::Pending => {}
+ /// }
+ /// task_context = yield ();
+ /// }
+ /// }
+ /// ```
+ fn lower_expr_await(&mut self, dot_await_span: Span, expr: &Expr) -> hir::ExprKind<'hir> {
+ let full_span = expr.span.to(dot_await_span);
+ match self.generator_kind {
+ Some(hir::GeneratorKind::Async(_)) => {}
+ Some(hir::GeneratorKind::Gen) | None => {
+ let mut err = struct_span_err!(
+ self.tcx.sess,
+ dot_await_span,
+ E0728,
+ "`await` is only allowed inside `async` functions and blocks"
+ );
+ err.span_label(dot_await_span, "only allowed inside `async` functions and blocks");
+ if let Some(item_sp) = self.current_item {
+ err.span_label(item_sp, "this is not `async`");
+ }
+ err.emit();
+ }
+ }
+ let span = self.mark_span_with_reason(DesugaringKind::Await, dot_await_span, None);
+ let gen_future_span = self.mark_span_with_reason(
+ DesugaringKind::Await,
+ full_span,
+ self.allow_gen_future.clone(),
+ );
+ let expr = self.lower_expr_mut(expr);
+ let expr_hir_id = expr.hir_id;
+
+ // Note that the name of this binding must not be changed to something else because
+ // debuggers and debugger extensions expect it to be called `__awaitee`. They use
+ // this name to identify what is being awaited by a suspended async functions.
+ let awaitee_ident = Ident::with_dummy_span(sym::__awaitee);
+ let (awaitee_pat, awaitee_pat_hid) =
+ self.pat_ident_binding_mode(span, awaitee_ident, hir::BindingAnnotation::Mutable);
+
+ let task_context_ident = Ident::with_dummy_span(sym::_task_context);
+
+ // unsafe {
+ // ::std::future::Future::poll(
+ // ::std::pin::Pin::new_unchecked(&mut __awaitee),
+ // ::std::future::get_context(task_context),
+ // )
+ // }
+ let poll_expr = {
+ let awaitee = self.expr_ident(span, awaitee_ident, awaitee_pat_hid);
+ let ref_mut_awaitee = self.expr_mut_addr_of(span, awaitee);
+ let task_context = if let Some(task_context_hid) = self.task_context {
+ self.expr_ident_mut(span, task_context_ident, task_context_hid)
+ } else {
+ // Use of `await` outside of an async context, we cannot use `task_context` here.
+ self.expr_err(span)
+ };
+ let new_unchecked = self.expr_call_lang_item_fn_mut(
+ span,
+ hir::LangItem::PinNewUnchecked,
+ arena_vec![self; ref_mut_awaitee],
+ Some(expr_hir_id),
+ );
+ let get_context = self.expr_call_lang_item_fn_mut(
+ gen_future_span,
+ hir::LangItem::GetContext,
+ arena_vec![self; task_context],
+ Some(expr_hir_id),
+ );
+ let call = self.expr_call_lang_item_fn(
+ span,
+ hir::LangItem::FuturePoll,
+ arena_vec![self; new_unchecked, get_context],
+ Some(expr_hir_id),
+ );
+ self.arena.alloc(self.expr_unsafe(call))
+ };
+
+ // `::std::task::Poll::Ready(result) => break result`
+ let loop_node_id = self.next_node_id();
+ let loop_hir_id = self.lower_node_id(loop_node_id);
+ let ready_arm = {
+ let x_ident = Ident::with_dummy_span(sym::result);
+ let (x_pat, x_pat_hid) = self.pat_ident(gen_future_span, x_ident);
+ let x_expr = self.expr_ident(gen_future_span, x_ident, x_pat_hid);
+ let ready_field = self.single_pat_field(gen_future_span, x_pat);
+ let ready_pat = self.pat_lang_item_variant(
+ span,
+ hir::LangItem::PollReady,
+ ready_field,
+ Some(expr_hir_id),
+ );
+ let break_x = self.with_loop_scope(loop_node_id, move |this| {
+ let expr_break =
+ hir::ExprKind::Break(this.lower_loop_destination(None), Some(x_expr));
+ this.arena.alloc(this.expr(gen_future_span, expr_break, ThinVec::new()))
+ });
+ self.arm(ready_pat, break_x)
+ };
+
+ // `::std::task::Poll::Pending => {}`
+ let pending_arm = {
+ let pending_pat = self.pat_lang_item_variant(
+ span,
+ hir::LangItem::PollPending,
+ &[],
+ Some(expr_hir_id),
+ );
+ let empty_block = self.expr_block_empty(span);
+ self.arm(pending_pat, empty_block)
+ };
+
+ let inner_match_stmt = {
+ let match_expr = self.expr_match(
+ span,
+ poll_expr,
+ arena_vec![self; ready_arm, pending_arm],
+ hir::MatchSource::AwaitDesugar,
+ );
+ self.stmt_expr(span, match_expr)
+ };
+
+ // task_context = yield ();
+ let yield_stmt = {
+ let unit = self.expr_unit(span);
+ let yield_expr = self.expr(
+ span,
+ hir::ExprKind::Yield(unit, hir::YieldSource::Await { expr: Some(expr_hir_id) }),
+ ThinVec::new(),
+ );
+ let yield_expr = self.arena.alloc(yield_expr);
+
+ if let Some(task_context_hid) = self.task_context {
+ let lhs = self.expr_ident(span, task_context_ident, task_context_hid);
+ let assign = self.expr(
+ span,
+ hir::ExprKind::Assign(lhs, yield_expr, self.lower_span(span)),
+ AttrVec::new(),
+ );
+ self.stmt_expr(span, assign)
+ } else {
+ // Use of `await` outside of an async context. Return `yield_expr` so that we can
+ // proceed with type checking.
+ self.stmt(span, hir::StmtKind::Semi(yield_expr))
+ }
+ };
+
+ let loop_block = self.block_all(span, arena_vec![self; inner_match_stmt, yield_stmt], None);
+
+ // loop { .. }
+ let loop_expr = self.arena.alloc(hir::Expr {
+ hir_id: loop_hir_id,
+ kind: hir::ExprKind::Loop(
+ loop_block,
+ None,
+ hir::LoopSource::Loop,
+ self.lower_span(span),
+ ),
+ span: self.lower_span(span),
+ });
+
+ // mut __awaitee => loop { ... }
+ let awaitee_arm = self.arm(awaitee_pat, loop_expr);
+
+ // `match ::std::future::IntoFuture::into_future(<expr>) { ... }`
+ let into_future_span = self.mark_span_with_reason(
+ DesugaringKind::Await,
+ dot_await_span,
+ self.allow_into_future.clone(),
+ );
+ let into_future_expr = self.expr_call_lang_item_fn(
+ into_future_span,
+ hir::LangItem::IntoFutureIntoFuture,
+ arena_vec![self; expr],
+ Some(expr_hir_id),
+ );
+
+ // match <into_future_expr> {
+ // mut __awaitee => loop { .. }
+ // }
+ hir::ExprKind::Match(
+ into_future_expr,
+ arena_vec![self; awaitee_arm],
+ hir::MatchSource::AwaitDesugar,
+ )
+ }
+
+ fn lower_expr_closure(
+ &mut self,
+ binder: &ClosureBinder,
+ capture_clause: CaptureBy,
+ closure_id: NodeId,
+ movability: Movability,
+ decl: &FnDecl,
+ body: &Expr,
+ fn_decl_span: Span,
+ ) -> hir::ExprKind<'hir> {
+ let (binder_clause, generic_params) = self.lower_closure_binder(binder);
+
+ let (body_id, generator_option) = self.with_new_scopes(move |this| {
+ let prev = this.current_item;
+ this.current_item = Some(fn_decl_span);
+ let mut generator_kind = None;
+ let body_id = this.lower_fn_body(decl, |this| {
+ let e = this.lower_expr_mut(body);
+ generator_kind = this.generator_kind;
+ e
+ });
+ let generator_option =
+ this.generator_movability_for_fn(&decl, fn_decl_span, generator_kind, movability);
+ this.current_item = prev;
+ (body_id, generator_option)
+ });
+
+ let bound_generic_params = self.lower_lifetime_binder(closure_id, generic_params);
+ // Lower outside new scope to preserve `is_in_loop_condition`.
+ let fn_decl = self.lower_fn_decl(decl, None, FnDeclKind::Closure, None);
+
+ let c = self.arena.alloc(hir::Closure {
+ binder: binder_clause,
+ capture_clause,
+ bound_generic_params,
+ fn_decl,
+ body: body_id,
+ fn_decl_span: self.lower_span(fn_decl_span),
+ movability: generator_option,
+ });
+
+ hir::ExprKind::Closure(c)
+ }
+
+ fn generator_movability_for_fn(
+ &mut self,
+ decl: &FnDecl,
+ fn_decl_span: Span,
+ generator_kind: Option<hir::GeneratorKind>,
+ movability: Movability,
+ ) -> Option<hir::Movability> {
+ match generator_kind {
+ Some(hir::GeneratorKind::Gen) => {
+ if decl.inputs.len() > 1 {
+ struct_span_err!(
+ self.tcx.sess,
+ fn_decl_span,
+ E0628,
+ "too many parameters for a generator (expected 0 or 1 parameters)"
+ )
+ .emit();
+ }
+ Some(movability)
+ }
+ Some(hir::GeneratorKind::Async(_)) => {
+ panic!("non-`async` closure body turned `async` during lowering");
+ }
+ None => {
+ if movability == Movability::Static {
+ struct_span_err!(
+ self.tcx.sess,
+ fn_decl_span,
+ E0697,
+ "closures cannot be static"
+ )
+ .emit();
+ }
+ None
+ }
+ }
+ }
+
+ fn lower_closure_binder<'c>(
+ &mut self,
+ binder: &'c ClosureBinder,
+ ) -> (hir::ClosureBinder, &'c [GenericParam]) {
+ let (binder, params) = match binder {
+ ClosureBinder::NotPresent => (hir::ClosureBinder::Default, &[][..]),
+ &ClosureBinder::For { span, ref generic_params } => {
+ let span = self.lower_span(span);
+ (hir::ClosureBinder::For { span }, &**generic_params)
+ }
+ };
+
+ (binder, params)
+ }
+
+ fn lower_expr_async_closure(
+ &mut self,
+ binder: &ClosureBinder,
+ capture_clause: CaptureBy,
+ closure_id: NodeId,
+ inner_closure_id: NodeId,
+ decl: &FnDecl,
+ body: &Expr,
+ fn_decl_span: Span,
+ ) -> hir::ExprKind<'hir> {
+ if let &ClosureBinder::For { span, .. } = binder {
+ self.tcx.sess.span_err(
+ span,
+ "`for<...>` binders on `async` closures are not currently supported",
+ );
+ }
+
+ let (binder_clause, generic_params) = self.lower_closure_binder(binder);
+
+ let outer_decl =
+ FnDecl { inputs: decl.inputs.clone(), output: FnRetTy::Default(fn_decl_span) };
+
+ let body = self.with_new_scopes(|this| {
+ // FIXME(cramertj): allow `async` non-`move` closures with arguments.
+ if capture_clause == CaptureBy::Ref && !decl.inputs.is_empty() {
+ struct_span_err!(
+ this.tcx.sess,
+ fn_decl_span,
+ E0708,
+ "`async` non-`move` closures with parameters are not currently supported",
+ )
+ .help(
+ "consider using `let` statements to manually capture \
+ variables by reference before entering an `async move` closure",
+ )
+ .emit();
+ }
+
+ // Transform `async |x: u8| -> X { ... }` into
+ // `|x: u8| future_from_generator(|| -> X { ... })`.
+ let body_id = this.lower_fn_body(&outer_decl, |this| {
+ let async_ret_ty =
+ if let FnRetTy::Ty(ty) = &decl.output { Some(ty.clone()) } else { None };
+ let async_body = this.make_async_expr(
+ capture_clause,
+ inner_closure_id,
+ async_ret_ty,
+ body.span,
+ hir::AsyncGeneratorKind::Closure,
+ |this| this.with_new_scopes(|this| this.lower_expr_mut(body)),
+ );
+ this.expr(fn_decl_span, async_body, ThinVec::new())
+ });
+ body_id
+ });
+
+ let bound_generic_params = self.lower_lifetime_binder(closure_id, generic_params);
+
+ // We need to lower the declaration outside the new scope, because we
+ // have to conserve the state of being inside a loop condition for the
+ // closure argument types.
+ let fn_decl = self.lower_fn_decl(&outer_decl, None, FnDeclKind::Closure, None);
+
+ let c = self.arena.alloc(hir::Closure {
+ binder: binder_clause,
+ capture_clause,
+ bound_generic_params,
+ fn_decl,
+ body,
+ fn_decl_span: self.lower_span(fn_decl_span),
+ movability: None,
+ });
+ hir::ExprKind::Closure(c)
+ }
+
+ /// Destructure the LHS of complex assignments.
+ /// For instance, lower `(a, b) = t` to `{ let (lhs1, lhs2) = t; a = lhs1; b = lhs2; }`.
+ fn lower_expr_assign(
+ &mut self,
+ lhs: &Expr,
+ rhs: &Expr,
+ eq_sign_span: Span,
+ whole_span: Span,
+ ) -> hir::ExprKind<'hir> {
+ // Return early in case of an ordinary assignment.
+ fn is_ordinary(lower_ctx: &mut LoweringContext<'_, '_>, lhs: &Expr) -> bool {
+ match &lhs.kind {
+ ExprKind::Array(..)
+ | ExprKind::Struct(..)
+ | ExprKind::Tup(..)
+ | ExprKind::Underscore => false,
+ // Check for tuple struct constructor.
+ ExprKind::Call(callee, ..) => lower_ctx.extract_tuple_struct_path(callee).is_none(),
+ ExprKind::Paren(e) => {
+ match e.kind {
+ // We special-case `(..)` for consistency with patterns.
+ ExprKind::Range(None, None, RangeLimits::HalfOpen) => false,
+ _ => is_ordinary(lower_ctx, e),
+ }
+ }
+ _ => true,
+ }
+ }
+ if is_ordinary(self, lhs) {
+ return hir::ExprKind::Assign(
+ self.lower_expr(lhs),
+ self.lower_expr(rhs),
+ self.lower_span(eq_sign_span),
+ );
+ }
+
+ let mut assignments = vec![];
+
+ // The LHS becomes a pattern: `(lhs1, lhs2)`.
+ let pat = self.destructure_assign(lhs, eq_sign_span, &mut assignments);
+ let rhs = self.lower_expr(rhs);
+
+ // Introduce a `let` for destructuring: `let (lhs1, lhs2) = t`.
+ let destructure_let = self.stmt_let_pat(
+ None,
+ whole_span,
+ Some(rhs),
+ pat,
+ hir::LocalSource::AssignDesugar(self.lower_span(eq_sign_span)),
+ );
+
+ // `a = lhs1; b = lhs2;`.
+ let stmts = self
+ .arena
+ .alloc_from_iter(std::iter::once(destructure_let).chain(assignments.into_iter()));
+
+ // Wrap everything in a block.
+ hir::ExprKind::Block(&self.block_all(whole_span, stmts, None), None)
+ }
+
+ /// If the given expression is a path to a tuple struct, returns that path.
+ /// It is not a complete check, but just tries to reject most paths early
+ /// if they are not tuple structs.
+ /// Type checking will take care of the full validation later.
+ fn extract_tuple_struct_path<'a>(
+ &mut self,
+ expr: &'a Expr,
+ ) -> Option<(&'a Option<QSelf>, &'a Path)> {
+ if let ExprKind::Path(qself, path) = &expr.kind {
+ // Does the path resolve to something disallowed in a tuple struct/variant pattern?
+ if let Some(partial_res) = self.resolver.get_partial_res(expr.id) {
+ if partial_res.unresolved_segments() == 0
+ && !partial_res.base_res().expected_in_tuple_struct_pat()
+ {
+ return None;
+ }
+ }
+ return Some((qself, path));
+ }
+ None
+ }
+
+ /// If the given expression is a path to a unit struct, returns that path.
+ /// It is not a complete check, but just tries to reject most paths early
+ /// if they are not unit structs.
+ /// Type checking will take care of the full validation later.
+ fn extract_unit_struct_path<'a>(
+ &mut self,
+ expr: &'a Expr,
+ ) -> Option<(&'a Option<QSelf>, &'a Path)> {
+ if let ExprKind::Path(qself, path) = &expr.kind {
+ // Does the path resolve to something disallowed in a unit struct/variant pattern?
+ if let Some(partial_res) = self.resolver.get_partial_res(expr.id) {
+ if partial_res.unresolved_segments() == 0
+ && !partial_res.base_res().expected_in_unit_struct_pat()
+ {
+ return None;
+ }
+ }
+ return Some((qself, path));
+ }
+ None
+ }
+
+ /// Convert the LHS of a destructuring assignment to a pattern.
+ /// Each sub-assignment is recorded in `assignments`.
+ fn destructure_assign(
+ &mut self,
+ lhs: &Expr,
+ eq_sign_span: Span,
+ assignments: &mut Vec<hir::Stmt<'hir>>,
+ ) -> &'hir hir::Pat<'hir> {
+ self.arena.alloc(self.destructure_assign_mut(lhs, eq_sign_span, assignments))
+ }
+
+ fn destructure_assign_mut(
+ &mut self,
+ lhs: &Expr,
+ eq_sign_span: Span,
+ assignments: &mut Vec<hir::Stmt<'hir>>,
+ ) -> hir::Pat<'hir> {
+ match &lhs.kind {
+ // Underscore pattern.
+ ExprKind::Underscore => {
+ return self.pat_without_dbm(lhs.span, hir::PatKind::Wild);
+ }
+ // Slice patterns.
+ ExprKind::Array(elements) => {
+ let (pats, rest) =
+ self.destructure_sequence(elements, "slice", eq_sign_span, assignments);
+ let slice_pat = if let Some((i, span)) = rest {
+ let (before, after) = pats.split_at(i);
+ hir::PatKind::Slice(
+ before,
+ Some(self.arena.alloc(self.pat_without_dbm(span, hir::PatKind::Wild))),
+ after,
+ )
+ } else {
+ hir::PatKind::Slice(pats, None, &[])
+ };
+ return self.pat_without_dbm(lhs.span, slice_pat);
+ }
+ // Tuple structs.
+ ExprKind::Call(callee, args) => {
+ if let Some((qself, path)) = self.extract_tuple_struct_path(callee) {
+ let (pats, rest) = self.destructure_sequence(
+ args,
+ "tuple struct or variant",
+ eq_sign_span,
+ assignments,
+ );
+ let qpath = self.lower_qpath(
+ callee.id,
+ qself,
+ path,
+ ParamMode::Optional,
+ ImplTraitContext::Disallowed(ImplTraitPosition::Path),
+ );
+ // Destructure like a tuple struct.
+ let tuple_struct_pat =
+ hir::PatKind::TupleStruct(qpath, pats, rest.map(|r| r.0));
+ return self.pat_without_dbm(lhs.span, tuple_struct_pat);
+ }
+ }
+ // Unit structs and enum variants.
+ ExprKind::Path(..) => {
+ if let Some((qself, path)) = self.extract_unit_struct_path(lhs) {
+ let qpath = self.lower_qpath(
+ lhs.id,
+ qself,
+ path,
+ ParamMode::Optional,
+ ImplTraitContext::Disallowed(ImplTraitPosition::Path),
+ );
+ // Destructure like a unit struct.
+ let unit_struct_pat = hir::PatKind::Path(qpath);
+ return self.pat_without_dbm(lhs.span, unit_struct_pat);
+ }
+ }
+ // Structs.
+ ExprKind::Struct(se) => {
+ let field_pats = self.arena.alloc_from_iter(se.fields.iter().map(|f| {
+ let pat = self.destructure_assign(&f.expr, eq_sign_span, assignments);
+ hir::PatField {
+ hir_id: self.next_id(),
+ ident: self.lower_ident(f.ident),
+ pat,
+ is_shorthand: f.is_shorthand,
+ span: self.lower_span(f.span),
+ }
+ }));
+ let qpath = self.lower_qpath(
+ lhs.id,
+ &se.qself,
+ &se.path,
+ ParamMode::Optional,
+ ImplTraitContext::Disallowed(ImplTraitPosition::Path),
+ );
+ let fields_omitted = match &se.rest {
+ StructRest::Base(e) => {
+ self.tcx
+ .sess
+ .struct_span_err(
+ e.span,
+ "functional record updates are not allowed in destructuring \
+ assignments",
+ )
+ .span_suggestion(
+ e.span,
+ "consider removing the trailing pattern",
+ "",
+ rustc_errors::Applicability::MachineApplicable,
+ )
+ .emit();
+ true
+ }
+ StructRest::Rest(_) => true,
+ StructRest::None => false,
+ };
+ let struct_pat = hir::PatKind::Struct(qpath, field_pats, fields_omitted);
+ return self.pat_without_dbm(lhs.span, struct_pat);
+ }
+ // Tuples.
+ ExprKind::Tup(elements) => {
+ let (pats, rest) =
+ self.destructure_sequence(elements, "tuple", eq_sign_span, assignments);
+ let tuple_pat = hir::PatKind::Tuple(pats, rest.map(|r| r.0));
+ return self.pat_without_dbm(lhs.span, tuple_pat);
+ }
+ ExprKind::Paren(e) => {
+ // We special-case `(..)` for consistency with patterns.
+ if let ExprKind::Range(None, None, RangeLimits::HalfOpen) = e.kind {
+ let tuple_pat = hir::PatKind::Tuple(&[], Some(0));
+ return self.pat_without_dbm(lhs.span, tuple_pat);
+ } else {
+ return self.destructure_assign_mut(e, eq_sign_span, assignments);
+ }
+ }
+ _ => {}
+ }
+ // Treat all other cases as normal lvalue.
+ let ident = Ident::new(sym::lhs, self.lower_span(lhs.span));
+ let (pat, binding) = self.pat_ident_mut(lhs.span, ident);
+ let ident = self.expr_ident(lhs.span, ident, binding);
+ let assign =
+ hir::ExprKind::Assign(self.lower_expr(lhs), ident, self.lower_span(eq_sign_span));
+ let expr = self.expr(lhs.span, assign, ThinVec::new());
+ assignments.push(self.stmt_expr(lhs.span, expr));
+ pat
+ }
+
+ /// Destructure a sequence of expressions occurring on the LHS of an assignment.
+ /// Such a sequence occurs in a tuple (struct)/slice.
+ /// Return a sequence of corresponding patterns, and the index and the span of `..` if it
+ /// exists.
+ /// Each sub-assignment is recorded in `assignments`.
+ fn destructure_sequence(
+ &mut self,
+ elements: &[AstP<Expr>],
+ ctx: &str,
+ eq_sign_span: Span,
+ assignments: &mut Vec<hir::Stmt<'hir>>,
+ ) -> (&'hir [hir::Pat<'hir>], Option<(usize, Span)>) {
+ let mut rest = None;
+ let elements =
+ self.arena.alloc_from_iter(elements.iter().enumerate().filter_map(|(i, e)| {
+ // Check for `..` pattern.
+ if let ExprKind::Range(None, None, RangeLimits::HalfOpen) = e.kind {
+ if let Some((_, prev_span)) = rest {
+ self.ban_extra_rest_pat(e.span, prev_span, ctx);
+ } else {
+ rest = Some((i, e.span));
+ }
+ None
+ } else {
+ Some(self.destructure_assign_mut(e, eq_sign_span, assignments))
+ }
+ }));
+ (elements, rest)
+ }
+
+ /// Desugar `<start>..=<end>` into `std::ops::RangeInclusive::new(<start>, <end>)`.
+ fn lower_expr_range_closed(&mut self, span: Span, e1: &Expr, e2: &Expr) -> hir::ExprKind<'hir> {
+ let e1 = self.lower_expr_mut(e1);
+ let e2 = self.lower_expr_mut(e2);
+ let fn_path =
+ hir::QPath::LangItem(hir::LangItem::RangeInclusiveNew, self.lower_span(span), None);
+ let fn_expr =
+ self.arena.alloc(self.expr(span, hir::ExprKind::Path(fn_path), ThinVec::new()));
+ hir::ExprKind::Call(fn_expr, arena_vec![self; e1, e2])
+ }
+
+ fn lower_expr_range(
+ &mut self,
+ span: Span,
+ e1: Option<&Expr>,
+ e2: Option<&Expr>,
+ lims: RangeLimits,
+ ) -> hir::ExprKind<'hir> {
+ use rustc_ast::RangeLimits::*;
+
+ let lang_item = match (e1, e2, lims) {
+ (None, None, HalfOpen) => hir::LangItem::RangeFull,
+ (Some(..), None, HalfOpen) => hir::LangItem::RangeFrom,
+ (None, Some(..), HalfOpen) => hir::LangItem::RangeTo,
+ (Some(..), Some(..), HalfOpen) => hir::LangItem::Range,
+ (None, Some(..), Closed) => hir::LangItem::RangeToInclusive,
+ (Some(..), Some(..), Closed) => unreachable!(),
+ (_, None, Closed) => self.diagnostic().span_fatal(span, "inclusive range with no end"),
+ };
+
+ let fields = self.arena.alloc_from_iter(
+ e1.iter().map(|e| (sym::start, e)).chain(e2.iter().map(|e| (sym::end, e))).map(
+ |(s, e)| {
+ let expr = self.lower_expr(&e);
+ let ident = Ident::new(s, self.lower_span(e.span));
+ self.expr_field(ident, expr, e.span)
+ },
+ ),
+ );
+
+ hir::ExprKind::Struct(
+ self.arena.alloc(hir::QPath::LangItem(lang_item, self.lower_span(span), None)),
+ fields,
+ None,
+ )
+ }
+
+ fn lower_label(&self, opt_label: Option<Label>) -> Option<Label> {
+ let label = opt_label?;
+ Some(Label { ident: self.lower_ident(label.ident) })
+ }
+
+ fn lower_loop_destination(&mut self, destination: Option<(NodeId, Label)>) -> hir::Destination {
+ let target_id = match destination {
+ Some((id, _)) => {
+ if let Some(loop_id) = self.resolver.get_label_res(id) {
+ Ok(self.lower_node_id(loop_id))
+ } else {
+ Err(hir::LoopIdError::UnresolvedLabel)
+ }
+ }
+ None => self
+ .loop_scope
+ .map(|id| Ok(self.lower_node_id(id)))
+ .unwrap_or(Err(hir::LoopIdError::OutsideLoopScope)),
+ };
+ let label = self.lower_label(destination.map(|(_, label)| label));
+ hir::Destination { label, target_id }
+ }
+
+ fn lower_jump_destination(&mut self, id: NodeId, opt_label: Option<Label>) -> hir::Destination {
+ if self.is_in_loop_condition && opt_label.is_none() {
+ hir::Destination {
+ label: None,
+ target_id: Err(hir::LoopIdError::UnlabeledCfInWhileCondition),
+ }
+ } else {
+ self.lower_loop_destination(opt_label.map(|label| (id, label)))
+ }
+ }
+
+ fn with_catch_scope<T>(&mut self, catch_id: NodeId, f: impl FnOnce(&mut Self) -> T) -> T {
+ let old_scope = self.catch_scope.replace(catch_id);
+ let result = f(self);
+ self.catch_scope = old_scope;
+ result
+ }
+
+ fn with_loop_scope<T>(&mut self, loop_id: NodeId, f: impl FnOnce(&mut Self) -> T) -> T {
+ // We're no longer in the base loop's condition; we're in another loop.
+ let was_in_loop_condition = self.is_in_loop_condition;
+ self.is_in_loop_condition = false;
+
+ let old_scope = self.loop_scope.replace(loop_id);
+ let result = f(self);
+ self.loop_scope = old_scope;
+
+ self.is_in_loop_condition = was_in_loop_condition;
+
+ result
+ }
+
+ fn with_loop_condition_scope<T>(&mut self, f: impl FnOnce(&mut Self) -> T) -> T {
+ let was_in_loop_condition = self.is_in_loop_condition;
+ self.is_in_loop_condition = true;
+
+ let result = f(self);
+
+ self.is_in_loop_condition = was_in_loop_condition;
+
+ result
+ }
+
+ fn lower_expr_field(&mut self, f: &ExprField) -> hir::ExprField<'hir> {
+ hir::ExprField {
+ hir_id: self.next_id(),
+ ident: self.lower_ident(f.ident),
+ expr: self.lower_expr(&f.expr),
+ span: self.lower_span(f.span),
+ is_shorthand: f.is_shorthand,
+ }
+ }
+
+ fn lower_expr_yield(&mut self, span: Span, opt_expr: Option<&Expr>) -> hir::ExprKind<'hir> {
+ match self.generator_kind {
+ Some(hir::GeneratorKind::Gen) => {}
+ Some(hir::GeneratorKind::Async(_)) => {
+ struct_span_err!(
+ self.tcx.sess,
+ span,
+ E0727,
+ "`async` generators are not yet supported"
+ )
+ .emit();
+ }
+ None => self.generator_kind = Some(hir::GeneratorKind::Gen),
+ }
+
+ let expr =
+ opt_expr.as_ref().map(|x| self.lower_expr(x)).unwrap_or_else(|| self.expr_unit(span));
+
+ hir::ExprKind::Yield(expr, hir::YieldSource::Yield)
+ }
+
+ /// Desugar `ExprForLoop` from: `[opt_ident]: for <pat> in <head> <body>` into:
+ /// ```ignore (pseudo-rust)
+ /// {
+ /// let result = match IntoIterator::into_iter(<head>) {
+ /// mut iter => {
+ /// [opt_ident]: loop {
+ /// match Iterator::next(&mut iter) {
+ /// None => break,
+ /// Some(<pat>) => <body>,
+ /// };
+ /// }
+ /// }
+ /// };
+ /// result
+ /// }
+ /// ```
+ fn lower_expr_for(
+ &mut self,
+ e: &Expr,
+ pat: &Pat,
+ head: &Expr,
+ body: &Block,
+ opt_label: Option<Label>,
+ ) -> hir::Expr<'hir> {
+ let head = self.lower_expr_mut(head);
+ let pat = self.lower_pat(pat);
+ let for_span =
+ self.mark_span_with_reason(DesugaringKind::ForLoop, self.lower_span(e.span), None);
+ let head_span = self.mark_span_with_reason(DesugaringKind::ForLoop, head.span, None);
+ let pat_span = self.mark_span_with_reason(DesugaringKind::ForLoop, pat.span, None);
+
+ // `None => break`
+ let none_arm = {
+ let break_expr =
+ self.with_loop_scope(e.id, |this| this.expr_break_alloc(for_span, ThinVec::new()));
+ let pat = self.pat_none(for_span);
+ self.arm(pat, break_expr)
+ };
+
+ // Some(<pat>) => <body>,
+ let some_arm = {
+ let some_pat = self.pat_some(pat_span, pat);
+ let body_block = self.with_loop_scope(e.id, |this| this.lower_block(body, false));
+ let body_expr = self.arena.alloc(self.expr_block(body_block, ThinVec::new()));
+ self.arm(some_pat, body_expr)
+ };
+
+ // `mut iter`
+ let iter = Ident::with_dummy_span(sym::iter);
+ let (iter_pat, iter_pat_nid) =
+ self.pat_ident_binding_mode(head_span, iter, hir::BindingAnnotation::Mutable);
+
+ // `match Iterator::next(&mut iter) { ... }`
+ let match_expr = {
+ let iter = self.expr_ident(head_span, iter, iter_pat_nid);
+ let ref_mut_iter = self.expr_mut_addr_of(head_span, iter);
+ let next_expr = self.expr_call_lang_item_fn(
+ head_span,
+ hir::LangItem::IteratorNext,
+ arena_vec![self; ref_mut_iter],
+ None,
+ );
+ let arms = arena_vec![self; none_arm, some_arm];
+
+ self.expr_match(head_span, next_expr, arms, hir::MatchSource::ForLoopDesugar)
+ };
+ let match_stmt = self.stmt_expr(for_span, match_expr);
+
+ let loop_block = self.block_all(for_span, arena_vec![self; match_stmt], None);
+
+ // `[opt_ident]: loop { ... }`
+ let kind = hir::ExprKind::Loop(
+ loop_block,
+ self.lower_label(opt_label),
+ hir::LoopSource::ForLoop,
+ self.lower_span(for_span.with_hi(head.span.hi())),
+ );
+ let loop_expr =
+ self.arena.alloc(hir::Expr { hir_id: self.lower_node_id(e.id), kind, span: for_span });
+
+ // `mut iter => { ... }`
+ let iter_arm = self.arm(iter_pat, loop_expr);
+
+ // `match ::std::iter::IntoIterator::into_iter(<head>) { ... }`
+ let into_iter_expr = {
+ self.expr_call_lang_item_fn(
+ head_span,
+ hir::LangItem::IntoIterIntoIter,
+ arena_vec![self; head],
+ None,
+ )
+ };
+
+ let match_expr = self.arena.alloc(self.expr_match(
+ for_span,
+ into_iter_expr,
+ arena_vec![self; iter_arm],
+ hir::MatchSource::ForLoopDesugar,
+ ));
+
+ let attrs: Vec<_> = e.attrs.iter().map(|a| self.lower_attr(a)).collect();
+
+ // This is effectively `{ let _result = ...; _result }`.
+ // The construct was introduced in #21984 and is necessary to make sure that
+ // temporaries in the `head` expression are dropped and do not leak to the
+ // surrounding scope of the `match` since the `match` is not a terminating scope.
+ //
+ // Also, add the attributes to the outer returned expr node.
+ self.expr_drop_temps_mut(for_span, match_expr, attrs.into())
+ }
+
+ /// Desugar `ExprKind::Try` from: `<expr>?` into:
+ /// ```ignore (pseudo-rust)
+ /// match Try::branch(<expr>) {
+ /// ControlFlow::Continue(val) => #[allow(unreachable_code)] val,,
+ /// ControlFlow::Break(residual) =>
+ /// #[allow(unreachable_code)]
+ /// // If there is an enclosing `try {...}`:
+ /// break 'catch_target Try::from_residual(residual),
+ /// // Otherwise:
+ /// return Try::from_residual(residual),
+ /// }
+ /// ```
+ fn lower_expr_try(&mut self, span: Span, sub_expr: &Expr) -> hir::ExprKind<'hir> {
+ let unstable_span = self.mark_span_with_reason(
+ DesugaringKind::QuestionMark,
+ span,
+ self.allow_try_trait.clone(),
+ );
+ let try_span = self.tcx.sess.source_map().end_point(span);
+ let try_span = self.mark_span_with_reason(
+ DesugaringKind::QuestionMark,
+ try_span,
+ self.allow_try_trait.clone(),
+ );
+
+ // `Try::branch(<expr>)`
+ let scrutinee = {
+ // expand <expr>
+ let sub_expr = self.lower_expr_mut(sub_expr);
+
+ self.expr_call_lang_item_fn(
+ unstable_span,
+ hir::LangItem::TryTraitBranch,
+ arena_vec![self; sub_expr],
+ None,
+ )
+ };
+
+ // `#[allow(unreachable_code)]`
+ let attr = {
+ // `allow(unreachable_code)`
+ let allow = {
+ let allow_ident = Ident::new(sym::allow, self.lower_span(span));
+ let uc_ident = Ident::new(sym::unreachable_code, self.lower_span(span));
+ let uc_nested = attr::mk_nested_word_item(uc_ident);
+ attr::mk_list_item(allow_ident, vec![uc_nested])
+ };
+ attr::mk_attr_outer(allow)
+ };
+ let attrs = vec![attr];
+
+ // `ControlFlow::Continue(val) => #[allow(unreachable_code)] val,`
+ let continue_arm = {
+ let val_ident = Ident::with_dummy_span(sym::val);
+ let (val_pat, val_pat_nid) = self.pat_ident(span, val_ident);
+ let val_expr = self.arena.alloc(self.expr_ident_with_attrs(
+ span,
+ val_ident,
+ val_pat_nid,
+ ThinVec::from(attrs.clone()),
+ ));
+ let continue_pat = self.pat_cf_continue(unstable_span, val_pat);
+ self.arm(continue_pat, val_expr)
+ };
+
+ // `ControlFlow::Break(residual) =>
+ // #[allow(unreachable_code)]
+ // return Try::from_residual(residual),`
+ let break_arm = {
+ let residual_ident = Ident::with_dummy_span(sym::residual);
+ let (residual_local, residual_local_nid) = self.pat_ident(try_span, residual_ident);
+ let residual_expr = self.expr_ident_mut(try_span, residual_ident, residual_local_nid);
+ let from_residual_expr = self.wrap_in_try_constructor(
+ hir::LangItem::TryTraitFromResidual,
+ try_span,
+ self.arena.alloc(residual_expr),
+ unstable_span,
+ );
+ let thin_attrs = ThinVec::from(attrs);
+ let ret_expr = if let Some(catch_node) = self.catch_scope {
+ let target_id = Ok(self.lower_node_id(catch_node));
+ self.arena.alloc(self.expr(
+ try_span,
+ hir::ExprKind::Break(
+ hir::Destination { label: None, target_id },
+ Some(from_residual_expr),
+ ),
+ thin_attrs,
+ ))
+ } else {
+ self.arena.alloc(self.expr(
+ try_span,
+ hir::ExprKind::Ret(Some(from_residual_expr)),
+ thin_attrs,
+ ))
+ };
+
+ let break_pat = self.pat_cf_break(try_span, residual_local);
+ self.arm(break_pat, ret_expr)
+ };
+
+ hir::ExprKind::Match(
+ scrutinee,
+ arena_vec![self; break_arm, continue_arm],
+ hir::MatchSource::TryDesugar,
+ )
+ }
+
+ /// Desugar `ExprKind::Yeet` from: `do yeet <expr>` into:
+ /// ```rust
+ /// // If there is an enclosing `try {...}`:
+ /// break 'catch_target FromResidual::from_residual(Yeet(residual)),
+ /// // Otherwise:
+ /// return FromResidual::from_residual(Yeet(residual)),
+ /// ```
+ /// But to simplify this, there's a `from_yeet` lang item function which
+ /// handles the combined `FromResidual::from_residual(Yeet(residual))`.
+ fn lower_expr_yeet(&mut self, span: Span, sub_expr: Option<&Expr>) -> hir::ExprKind<'hir> {
+ // The expression (if present) or `()` otherwise.
+ let (yeeted_span, yeeted_expr) = if let Some(sub_expr) = sub_expr {
+ (sub_expr.span, self.lower_expr(sub_expr))
+ } else {
+ (self.mark_span_with_reason(DesugaringKind::YeetExpr, span, None), self.expr_unit(span))
+ };
+
+ let unstable_span = self.mark_span_with_reason(
+ DesugaringKind::YeetExpr,
+ span,
+ self.allow_try_trait.clone(),
+ );
+
+ let from_yeet_expr = self.wrap_in_try_constructor(
+ hir::LangItem::TryTraitFromYeet,
+ unstable_span,
+ yeeted_expr,
+ yeeted_span,
+ );
+
+ if let Some(catch_node) = self.catch_scope {
+ let target_id = Ok(self.lower_node_id(catch_node));
+ hir::ExprKind::Break(hir::Destination { label: None, target_id }, Some(from_yeet_expr))
+ } else {
+ hir::ExprKind::Ret(Some(from_yeet_expr))
+ }
+ }
+
+ // =========================================================================
+ // Helper methods for building HIR.
+ // =========================================================================
+
+ /// Wrap the given `expr` in a terminating scope using `hir::ExprKind::DropTemps`.
+ ///
+ /// In terms of drop order, it has the same effect as wrapping `expr` in
+ /// `{ let _t = $expr; _t }` but should provide better compile-time performance.
+ ///
+ /// The drop order can be important in e.g. `if expr { .. }`.
+ pub(super) fn expr_drop_temps(
+ &mut self,
+ span: Span,
+ expr: &'hir hir::Expr<'hir>,
+ attrs: AttrVec,
+ ) -> &'hir hir::Expr<'hir> {
+ self.arena.alloc(self.expr_drop_temps_mut(span, expr, attrs))
+ }
+
+ pub(super) fn expr_drop_temps_mut(
+ &mut self,
+ span: Span,
+ expr: &'hir hir::Expr<'hir>,
+ attrs: AttrVec,
+ ) -> hir::Expr<'hir> {
+ self.expr(span, hir::ExprKind::DropTemps(expr), attrs)
+ }
+
+ fn expr_match(
+ &mut self,
+ span: Span,
+ arg: &'hir hir::Expr<'hir>,
+ arms: &'hir [hir::Arm<'hir>],
+ source: hir::MatchSource,
+ ) -> hir::Expr<'hir> {
+ self.expr(span, hir::ExprKind::Match(arg, arms, source), ThinVec::new())
+ }
+
+ fn expr_break(&mut self, span: Span, attrs: AttrVec) -> hir::Expr<'hir> {
+ let expr_break = hir::ExprKind::Break(self.lower_loop_destination(None), None);
+ self.expr(span, expr_break, attrs)
+ }
+
+ fn expr_break_alloc(&mut self, span: Span, attrs: AttrVec) -> &'hir hir::Expr<'hir> {
+ let expr_break = self.expr_break(span, attrs);
+ self.arena.alloc(expr_break)
+ }
+
+ fn expr_mut_addr_of(&mut self, span: Span, e: &'hir hir::Expr<'hir>) -> hir::Expr<'hir> {
+ self.expr(
+ span,
+ hir::ExprKind::AddrOf(hir::BorrowKind::Ref, hir::Mutability::Mut, e),
+ ThinVec::new(),
+ )
+ }
+
+ fn expr_unit(&mut self, sp: Span) -> &'hir hir::Expr<'hir> {
+ self.arena.alloc(self.expr(sp, hir::ExprKind::Tup(&[]), ThinVec::new()))
+ }
+
+ fn expr_call_mut(
+ &mut self,
+ span: Span,
+ e: &'hir hir::Expr<'hir>,
+ args: &'hir [hir::Expr<'hir>],
+ ) -> hir::Expr<'hir> {
+ self.expr(span, hir::ExprKind::Call(e, args), ThinVec::new())
+ }
+
+ fn expr_call(
+ &mut self,
+ span: Span,
+ e: &'hir hir::Expr<'hir>,
+ args: &'hir [hir::Expr<'hir>],
+ ) -> &'hir hir::Expr<'hir> {
+ self.arena.alloc(self.expr_call_mut(span, e, args))
+ }
+
+ fn expr_call_lang_item_fn_mut(
+ &mut self,
+ span: Span,
+ lang_item: hir::LangItem,
+ args: &'hir [hir::Expr<'hir>],
+ hir_id: Option<hir::HirId>,
+ ) -> hir::Expr<'hir> {
+ let path =
+ self.arena.alloc(self.expr_lang_item_path(span, lang_item, ThinVec::new(), hir_id));
+ self.expr_call_mut(span, path, args)
+ }
+
+ fn expr_call_lang_item_fn(
+ &mut self,
+ span: Span,
+ lang_item: hir::LangItem,
+ args: &'hir [hir::Expr<'hir>],
+ hir_id: Option<hir::HirId>,
+ ) -> &'hir hir::Expr<'hir> {
+ self.arena.alloc(self.expr_call_lang_item_fn_mut(span, lang_item, args, hir_id))
+ }
+
+ fn expr_lang_item_path(
+ &mut self,
+ span: Span,
+ lang_item: hir::LangItem,
+ attrs: AttrVec,
+ hir_id: Option<hir::HirId>,
+ ) -> hir::Expr<'hir> {
+ self.expr(
+ span,
+ hir::ExprKind::Path(hir::QPath::LangItem(lang_item, self.lower_span(span), hir_id)),
+ attrs,
+ )
+ }
+
+ pub(super) fn expr_ident(
+ &mut self,
+ sp: Span,
+ ident: Ident,
+ binding: hir::HirId,
+ ) -> &'hir hir::Expr<'hir> {
+ self.arena.alloc(self.expr_ident_mut(sp, ident, binding))
+ }
+
+ pub(super) fn expr_ident_mut(
+ &mut self,
+ sp: Span,
+ ident: Ident,
+ binding: hir::HirId,
+ ) -> hir::Expr<'hir> {
+ self.expr_ident_with_attrs(sp, ident, binding, ThinVec::new())
+ }
+
+ fn expr_ident_with_attrs(
+ &mut self,
+ span: Span,
+ ident: Ident,
+ binding: hir::HirId,
+ attrs: AttrVec,
+ ) -> hir::Expr<'hir> {
+ let expr_path = hir::ExprKind::Path(hir::QPath::Resolved(
+ None,
+ self.arena.alloc(hir::Path {
+ span: self.lower_span(span),
+ res: Res::Local(binding),
+ segments: arena_vec![self; hir::PathSegment::from_ident(ident)],
+ }),
+ ));
+
+ self.expr(span, expr_path, attrs)
+ }
+
+ fn expr_unsafe(&mut self, expr: &'hir hir::Expr<'hir>) -> hir::Expr<'hir> {
+ let hir_id = self.next_id();
+ let span = expr.span;
+ self.expr(
+ span,
+ hir::ExprKind::Block(
+ self.arena.alloc(hir::Block {
+ stmts: &[],
+ expr: Some(expr),
+ hir_id,
+ rules: hir::BlockCheckMode::UnsafeBlock(hir::UnsafeSource::CompilerGenerated),
+ span: self.lower_span(span),
+ targeted_by_break: false,
+ }),
+ None,
+ ),
+ ThinVec::new(),
+ )
+ }
+
+ fn expr_block_empty(&mut self, span: Span) -> &'hir hir::Expr<'hir> {
+ let blk = self.block_all(span, &[], None);
+ let expr = self.expr_block(blk, ThinVec::new());
+ self.arena.alloc(expr)
+ }
+
+ pub(super) fn expr_block(
+ &mut self,
+ b: &'hir hir::Block<'hir>,
+ attrs: AttrVec,
+ ) -> hir::Expr<'hir> {
+ self.expr(b.span, hir::ExprKind::Block(b, None), attrs)
+ }
+
+ pub(super) fn expr(
+ &mut self,
+ span: Span,
+ kind: hir::ExprKind<'hir>,
+ attrs: AttrVec,
+ ) -> hir::Expr<'hir> {
+ let hir_id = self.next_id();
+ self.lower_attrs(hir_id, &attrs);
+ hir::Expr { hir_id, kind, span: self.lower_span(span) }
+ }
+
+ fn expr_field(
+ &mut self,
+ ident: Ident,
+ expr: &'hir hir::Expr<'hir>,
+ span: Span,
+ ) -> hir::ExprField<'hir> {
+ hir::ExprField {
+ hir_id: self.next_id(),
+ ident,
+ span: self.lower_span(span),
+ expr,
+ is_shorthand: false,
+ }
+ }
+
+ fn arm(&mut self, pat: &'hir hir::Pat<'hir>, expr: &'hir hir::Expr<'hir>) -> hir::Arm<'hir> {
+ hir::Arm {
+ hir_id: self.next_id(),
+ pat,
+ guard: None,
+ span: self.lower_span(expr.span),
+ body: expr,
+ }
+ }
+}
diff --git a/compiler/rustc_ast_lowering/src/index.rs b/compiler/rustc_ast_lowering/src/index.rs
new file mode 100644
index 000000000..d5af74d47
--- /dev/null
+++ b/compiler/rustc_ast_lowering/src/index.rs
@@ -0,0 +1,346 @@
+use rustc_data_structures::fx::FxHashMap;
+use rustc_data_structures::sorted_map::SortedMap;
+use rustc_hir as hir;
+use rustc_hir::def_id::LocalDefId;
+use rustc_hir::definitions;
+use rustc_hir::intravisit::{self, Visitor};
+use rustc_hir::*;
+use rustc_index::vec::{Idx, IndexVec};
+use rustc_middle::span_bug;
+use rustc_session::Session;
+use rustc_span::source_map::SourceMap;
+use rustc_span::{Span, DUMMY_SP};
+
+use tracing::debug;
+
+/// A visitor that walks over the HIR and collects `Node`s into a HIR map.
+pub(super) struct NodeCollector<'a, 'hir> {
+ /// Source map
+ source_map: &'a SourceMap,
+ bodies: &'a SortedMap<ItemLocalId, &'hir Body<'hir>>,
+
+ /// Outputs
+ nodes: IndexVec<ItemLocalId, Option<ParentedNode<'hir>>>,
+ parenting: FxHashMap<LocalDefId, ItemLocalId>,
+
+ /// The parent of this node
+ parent_node: hir::ItemLocalId,
+
+ owner: LocalDefId,
+
+ definitions: &'a definitions::Definitions,
+}
+
+#[tracing::instrument(level = "debug", skip(sess, definitions, bodies))]
+pub(super) fn index_hir<'hir>(
+ sess: &Session,
+ definitions: &definitions::Definitions,
+ item: hir::OwnerNode<'hir>,
+ bodies: &SortedMap<ItemLocalId, &'hir Body<'hir>>,
+) -> (IndexVec<ItemLocalId, Option<ParentedNode<'hir>>>, FxHashMap<LocalDefId, ItemLocalId>) {
+ let mut nodes = IndexVec::new();
+ // This node's parent should never be accessed: the owner's parent is computed by the
+ // hir_owner_parent query. Make it invalid (= ItemLocalId::MAX) to force an ICE whenever it is
+ // used.
+ nodes.push(Some(ParentedNode { parent: ItemLocalId::INVALID, node: item.into() }));
+ let mut collector = NodeCollector {
+ source_map: sess.source_map(),
+ definitions,
+ owner: item.def_id(),
+ parent_node: ItemLocalId::new(0),
+ nodes,
+ bodies,
+ parenting: FxHashMap::default(),
+ };
+
+ match item {
+ OwnerNode::Crate(citem) => {
+ collector.visit_mod(&citem, citem.spans.inner_span, hir::CRATE_HIR_ID)
+ }
+ OwnerNode::Item(item) => collector.visit_item(item),
+ OwnerNode::TraitItem(item) => collector.visit_trait_item(item),
+ OwnerNode::ImplItem(item) => collector.visit_impl_item(item),
+ OwnerNode::ForeignItem(item) => collector.visit_foreign_item(item),
+ };
+
+ (collector.nodes, collector.parenting)
+}
+
+impl<'a, 'hir> NodeCollector<'a, 'hir> {
+ #[tracing::instrument(level = "debug", skip(self))]
+ fn insert(&mut self, span: Span, hir_id: HirId, node: Node<'hir>) {
+ debug_assert_eq!(self.owner, hir_id.owner);
+ debug_assert_ne!(hir_id.local_id.as_u32(), 0);
+
+ // Make sure that the DepNode of some node coincides with the HirId
+ // owner of that node.
+ if cfg!(debug_assertions) {
+ if hir_id.owner != self.owner {
+ span_bug!(
+ span,
+ "inconsistent DepNode at `{:?}` for `{:?}`: \
+ current_dep_node_owner={} ({:?}), hir_id.owner={} ({:?})",
+ self.source_map.span_to_diagnostic_string(span),
+ node,
+ self.definitions.def_path(self.owner).to_string_no_crate_verbose(),
+ self.owner,
+ self.definitions.def_path(hir_id.owner).to_string_no_crate_verbose(),
+ hir_id.owner,
+ )
+ }
+ }
+
+ self.nodes.insert(hir_id.local_id, ParentedNode { parent: self.parent_node, node: node });
+ }
+
+ fn with_parent<F: FnOnce(&mut Self)>(&mut self, parent_node_id: HirId, f: F) {
+ debug_assert_eq!(parent_node_id.owner, self.owner);
+ let parent_node = self.parent_node;
+ self.parent_node = parent_node_id.local_id;
+ f(self);
+ self.parent_node = parent_node;
+ }
+
+ fn insert_nested(&mut self, item: LocalDefId) {
+ self.parenting.insert(item, self.parent_node);
+ }
+}
+
+impl<'a, 'hir> Visitor<'hir> for NodeCollector<'a, 'hir> {
+ /// Because we want to track parent items and so forth, enable
+ /// deep walking so that we walk nested items in the context of
+ /// their outer items.
+
+ fn visit_nested_item(&mut self, item: ItemId) {
+ debug!("visit_nested_item: {:?}", item);
+ self.insert_nested(item.def_id);
+ }
+
+ fn visit_nested_trait_item(&mut self, item_id: TraitItemId) {
+ self.insert_nested(item_id.def_id);
+ }
+
+ fn visit_nested_impl_item(&mut self, item_id: ImplItemId) {
+ self.insert_nested(item_id.def_id);
+ }
+
+ fn visit_nested_foreign_item(&mut self, foreign_id: ForeignItemId) {
+ self.insert_nested(foreign_id.def_id);
+ }
+
+ fn visit_nested_body(&mut self, id: BodyId) {
+ debug_assert_eq!(id.hir_id.owner, self.owner);
+ let body = self.bodies[&id.hir_id.local_id];
+ self.visit_body(body);
+ }
+
+ fn visit_param(&mut self, param: &'hir Param<'hir>) {
+ let node = Node::Param(param);
+ self.insert(param.pat.span, param.hir_id, node);
+ self.with_parent(param.hir_id, |this| {
+ intravisit::walk_param(this, param);
+ });
+ }
+
+ #[tracing::instrument(level = "debug", skip(self))]
+ fn visit_item(&mut self, i: &'hir Item<'hir>) {
+ debug_assert_eq!(i.def_id, self.owner);
+ self.with_parent(i.hir_id(), |this| {
+ if let ItemKind::Struct(ref struct_def, _) = i.kind {
+ // If this is a tuple or unit-like struct, register the constructor.
+ if let Some(ctor_hir_id) = struct_def.ctor_hir_id() {
+ this.insert(i.span, ctor_hir_id, Node::Ctor(struct_def));
+ }
+ }
+ intravisit::walk_item(this, i);
+ });
+ }
+
+ #[tracing::instrument(level = "debug", skip(self))]
+ fn visit_foreign_item(&mut self, fi: &'hir ForeignItem<'hir>) {
+ debug_assert_eq!(fi.def_id, self.owner);
+ self.with_parent(fi.hir_id(), |this| {
+ intravisit::walk_foreign_item(this, fi);
+ });
+ }
+
+ fn visit_generic_param(&mut self, param: &'hir GenericParam<'hir>) {
+ self.insert(param.span, param.hir_id, Node::GenericParam(param));
+ intravisit::walk_generic_param(self, param);
+ }
+
+ fn visit_const_param_default(&mut self, param: HirId, ct: &'hir AnonConst) {
+ self.with_parent(param, |this| {
+ intravisit::walk_const_param_default(this, ct);
+ })
+ }
+
+ #[tracing::instrument(level = "debug", skip(self))]
+ fn visit_trait_item(&mut self, ti: &'hir TraitItem<'hir>) {
+ debug_assert_eq!(ti.def_id, self.owner);
+ self.with_parent(ti.hir_id(), |this| {
+ intravisit::walk_trait_item(this, ti);
+ });
+ }
+
+ #[tracing::instrument(level = "debug", skip(self))]
+ fn visit_impl_item(&mut self, ii: &'hir ImplItem<'hir>) {
+ debug_assert_eq!(ii.def_id, self.owner);
+ self.with_parent(ii.hir_id(), |this| {
+ intravisit::walk_impl_item(this, ii);
+ });
+ }
+
+ fn visit_pat(&mut self, pat: &'hir Pat<'hir>) {
+ self.insert(pat.span, pat.hir_id, Node::Pat(pat));
+
+ self.with_parent(pat.hir_id, |this| {
+ intravisit::walk_pat(this, pat);
+ });
+ }
+
+ fn visit_arm(&mut self, arm: &'hir Arm<'hir>) {
+ let node = Node::Arm(arm);
+
+ self.insert(arm.span, arm.hir_id, node);
+
+ self.with_parent(arm.hir_id, |this| {
+ intravisit::walk_arm(this, arm);
+ });
+ }
+
+ fn visit_anon_const(&mut self, constant: &'hir AnonConst) {
+ self.insert(DUMMY_SP, constant.hir_id, Node::AnonConst(constant));
+
+ self.with_parent(constant.hir_id, |this| {
+ intravisit::walk_anon_const(this, constant);
+ });
+ }
+
+ fn visit_expr(&mut self, expr: &'hir Expr<'hir>) {
+ self.insert(expr.span, expr.hir_id, Node::Expr(expr));
+
+ self.with_parent(expr.hir_id, |this| {
+ intravisit::walk_expr(this, expr);
+ });
+ }
+
+ fn visit_stmt(&mut self, stmt: &'hir Stmt<'hir>) {
+ self.insert(stmt.span, stmt.hir_id, Node::Stmt(stmt));
+
+ self.with_parent(stmt.hir_id, |this| {
+ intravisit::walk_stmt(this, stmt);
+ });
+ }
+
+ fn visit_path_segment(&mut self, path_span: Span, path_segment: &'hir PathSegment<'hir>) {
+ if let Some(hir_id) = path_segment.hir_id {
+ self.insert(path_span, hir_id, Node::PathSegment(path_segment));
+ }
+ intravisit::walk_path_segment(self, path_span, path_segment);
+ }
+
+ fn visit_ty(&mut self, ty: &'hir Ty<'hir>) {
+ self.insert(ty.span, ty.hir_id, Node::Ty(ty));
+
+ self.with_parent(ty.hir_id, |this| {
+ intravisit::walk_ty(this, ty);
+ });
+ }
+
+ fn visit_infer(&mut self, inf: &'hir InferArg) {
+ self.insert(inf.span, inf.hir_id, Node::Infer(inf));
+
+ self.with_parent(inf.hir_id, |this| {
+ intravisit::walk_inf(this, inf);
+ });
+ }
+
+ fn visit_trait_ref(&mut self, tr: &'hir TraitRef<'hir>) {
+ self.insert(tr.path.span, tr.hir_ref_id, Node::TraitRef(tr));
+
+ self.with_parent(tr.hir_ref_id, |this| {
+ intravisit::walk_trait_ref(this, tr);
+ });
+ }
+
+ fn visit_fn(
+ &mut self,
+ fk: intravisit::FnKind<'hir>,
+ fd: &'hir FnDecl<'hir>,
+ b: BodyId,
+ s: Span,
+ id: HirId,
+ ) {
+ assert_eq!(self.owner, id.owner);
+ assert_eq!(self.parent_node, id.local_id);
+ intravisit::walk_fn(self, fk, fd, b, s, id);
+ }
+
+ fn visit_block(&mut self, block: &'hir Block<'hir>) {
+ self.insert(block.span, block.hir_id, Node::Block(block));
+ self.with_parent(block.hir_id, |this| {
+ intravisit::walk_block(this, block);
+ });
+ }
+
+ fn visit_local(&mut self, l: &'hir Local<'hir>) {
+ self.insert(l.span, l.hir_id, Node::Local(l));
+ self.with_parent(l.hir_id, |this| {
+ intravisit::walk_local(this, l);
+ })
+ }
+
+ fn visit_lifetime(&mut self, lifetime: &'hir Lifetime) {
+ self.insert(lifetime.span, lifetime.hir_id, Node::Lifetime(lifetime));
+ }
+
+ fn visit_variant(&mut self, v: &'hir Variant<'hir>, g: &'hir Generics<'hir>, item_id: HirId) {
+ self.insert(v.span, v.id, Node::Variant(v));
+ self.with_parent(v.id, |this| {
+ // Register the constructor of this variant.
+ if let Some(ctor_hir_id) = v.data.ctor_hir_id() {
+ this.insert(v.span, ctor_hir_id, Node::Ctor(&v.data));
+ }
+ intravisit::walk_variant(this, v, g, item_id);
+ });
+ }
+
+ fn visit_field_def(&mut self, field: &'hir FieldDef<'hir>) {
+ self.insert(field.span, field.hir_id, Node::Field(field));
+ self.with_parent(field.hir_id, |this| {
+ intravisit::walk_field_def(this, field);
+ });
+ }
+
+ fn visit_assoc_type_binding(&mut self, type_binding: &'hir TypeBinding<'hir>) {
+ self.insert(type_binding.span, type_binding.hir_id, Node::TypeBinding(type_binding));
+ self.with_parent(type_binding.hir_id, |this| {
+ intravisit::walk_assoc_type_binding(this, type_binding)
+ })
+ }
+
+ fn visit_trait_item_ref(&mut self, ii: &'hir TraitItemRef) {
+ // Do not visit the duplicate information in TraitItemRef. We want to
+ // map the actual nodes, not the duplicate ones in the *Ref.
+ let TraitItemRef { id, ident: _, kind: _, span: _ } = *ii;
+
+ self.visit_nested_trait_item(id);
+ }
+
+ fn visit_impl_item_ref(&mut self, ii: &'hir ImplItemRef) {
+ // Do not visit the duplicate information in ImplItemRef. We want to
+ // map the actual nodes, not the duplicate ones in the *Ref.
+ let ImplItemRef { id, ident: _, kind: _, span: _, trait_item_def_id: _ } = *ii;
+
+ self.visit_nested_impl_item(id);
+ }
+
+ fn visit_foreign_item_ref(&mut self, fi: &'hir ForeignItemRef) {
+ // Do not visit the duplicate information in ForeignItemRef. We want to
+ // map the actual nodes, not the duplicate ones in the *Ref.
+ let ForeignItemRef { id, ident: _, span: _ } = *fi;
+
+ self.visit_nested_foreign_item(id);
+ }
+}
diff --git a/compiler/rustc_ast_lowering/src/item.rs b/compiler/rustc_ast_lowering/src/item.rs
new file mode 100644
index 000000000..ee4c0036f
--- /dev/null
+++ b/compiler/rustc_ast_lowering/src/item.rs
@@ -0,0 +1,1513 @@
+use super::ResolverAstLoweringExt;
+use super::{AstOwner, ImplTraitContext, ImplTraitPosition};
+use super::{FnDeclKind, LoweringContext, ParamMode};
+
+use rustc_ast::ptr::P;
+use rustc_ast::visit::AssocCtxt;
+use rustc_ast::*;
+use rustc_data_structures::fx::FxHashMap;
+use rustc_data_structures::sorted_map::SortedMap;
+use rustc_errors::struct_span_err;
+use rustc_hir as hir;
+use rustc_hir::def::{DefKind, Res};
+use rustc_hir::def_id::{LocalDefId, CRATE_DEF_ID};
+use rustc_hir::PredicateOrigin;
+use rustc_index::vec::{Idx, IndexVec};
+use rustc_middle::ty::{DefIdTree, ResolverAstLowering, TyCtxt};
+use rustc_span::source_map::DesugaringKind;
+use rustc_span::symbol::{kw, sym, Ident};
+use rustc_span::Span;
+use rustc_target::spec::abi;
+use smallvec::{smallvec, SmallVec};
+
+use std::iter;
+
+pub(super) struct ItemLowerer<'a, 'hir> {
+ pub(super) tcx: TyCtxt<'hir>,
+ pub(super) resolver: &'a mut ResolverAstLowering,
+ pub(super) ast_index: &'a IndexVec<LocalDefId, AstOwner<'a>>,
+ pub(super) owners: &'a mut IndexVec<LocalDefId, hir::MaybeOwner<&'hir hir::OwnerInfo<'hir>>>,
+}
+
+/// When we have a ty alias we *may* have two where clauses. To give the best diagnostics, we set the span
+/// to the where clause that is preferred, if it exists. Otherwise, it sets the span to the other where
+/// clause if it exists.
+fn add_ty_alias_where_clause(
+ generics: &mut ast::Generics,
+ mut where_clauses: (TyAliasWhereClause, TyAliasWhereClause),
+ prefer_first: bool,
+) {
+ if !prefer_first {
+ where_clauses = (where_clauses.1, where_clauses.0);
+ }
+ if where_clauses.0.0 || !where_clauses.1.0 {
+ generics.where_clause.has_where_token = where_clauses.0.0;
+ generics.where_clause.span = where_clauses.0.1;
+ } else {
+ generics.where_clause.has_where_token = where_clauses.1.0;
+ generics.where_clause.span = where_clauses.1.1;
+ }
+}
+
+impl<'a, 'hir> ItemLowerer<'a, 'hir> {
+ fn with_lctx(
+ &mut self,
+ owner: NodeId,
+ f: impl FnOnce(&mut LoweringContext<'_, 'hir>) -> hir::OwnerNode<'hir>,
+ ) {
+ let mut lctx = LoweringContext {
+ // Pseudo-globals.
+ tcx: self.tcx,
+ resolver: self.resolver,
+ arena: self.tcx.hir_arena,
+
+ // HirId handling.
+ bodies: Vec::new(),
+ attrs: SortedMap::default(),
+ children: FxHashMap::default(),
+ current_hir_id_owner: CRATE_DEF_ID,
+ item_local_id_counter: hir::ItemLocalId::new(0),
+ node_id_to_local_id: Default::default(),
+ local_id_to_def_id: SortedMap::new(),
+ trait_map: Default::default(),
+
+ // Lowering state.
+ catch_scope: None,
+ loop_scope: None,
+ is_in_loop_condition: false,
+ is_in_trait_impl: false,
+ is_in_dyn_type: false,
+ generator_kind: None,
+ task_context: None,
+ current_item: None,
+ impl_trait_defs: Vec::new(),
+ impl_trait_bounds: Vec::new(),
+ allow_try_trait: Some([sym::try_trait_v2, sym::yeet_desugar_details][..].into()),
+ allow_gen_future: Some([sym::gen_future][..].into()),
+ allow_into_future: Some([sym::into_future][..].into()),
+ };
+ lctx.with_hir_id_owner(owner, |lctx| f(lctx));
+
+ for (def_id, info) in lctx.children {
+ self.owners.ensure_contains_elem(def_id, || hir::MaybeOwner::Phantom);
+ debug_assert!(matches!(self.owners[def_id], hir::MaybeOwner::Phantom));
+ self.owners[def_id] = info;
+ }
+ }
+
+ pub(super) fn lower_node(
+ &mut self,
+ def_id: LocalDefId,
+ ) -> hir::MaybeOwner<&'hir hir::OwnerInfo<'hir>> {
+ self.owners.ensure_contains_elem(def_id, || hir::MaybeOwner::Phantom);
+ if let hir::MaybeOwner::Phantom = self.owners[def_id] {
+ let node = self.ast_index[def_id];
+ match node {
+ AstOwner::NonOwner => {}
+ AstOwner::Crate(c) => self.lower_crate(c),
+ AstOwner::Item(item) => self.lower_item(item),
+ AstOwner::AssocItem(item, ctxt) => self.lower_assoc_item(item, ctxt),
+ AstOwner::ForeignItem(item) => self.lower_foreign_item(item),
+ }
+ }
+
+ self.owners[def_id]
+ }
+
+ #[instrument(level = "debug", skip(self, c))]
+ fn lower_crate(&mut self, c: &Crate) {
+ debug_assert_eq!(self.resolver.node_id_to_def_id[&CRATE_NODE_ID], CRATE_DEF_ID);
+ self.with_lctx(CRATE_NODE_ID, |lctx| {
+ let module = lctx.lower_mod(&c.items, &c.spans);
+ lctx.lower_attrs(hir::CRATE_HIR_ID, &c.attrs);
+ hir::OwnerNode::Crate(lctx.arena.alloc(module))
+ })
+ }
+
+ #[instrument(level = "debug", skip(self))]
+ fn lower_item(&mut self, item: &Item) {
+ self.with_lctx(item.id, |lctx| hir::OwnerNode::Item(lctx.lower_item(item)))
+ }
+
+ fn lower_assoc_item(&mut self, item: &AssocItem, ctxt: AssocCtxt) {
+ let def_id = self.resolver.node_id_to_def_id[&item.id];
+
+ let parent_id = self.tcx.local_parent(def_id);
+ let parent_hir = self.lower_node(parent_id).unwrap();
+ self.with_lctx(item.id, |lctx| {
+ // Evaluate with the lifetimes in `params` in-scope.
+ // This is used to track which lifetimes have already been defined,
+ // and which need to be replicated when lowering an async fn.
+ match parent_hir.node().expect_item().kind {
+ hir::ItemKind::Impl(hir::Impl { ref of_trait, .. }) => {
+ lctx.is_in_trait_impl = of_trait.is_some();
+ }
+ _ => {}
+ };
+
+ match ctxt {
+ AssocCtxt::Trait => hir::OwnerNode::TraitItem(lctx.lower_trait_item(item)),
+ AssocCtxt::Impl => hir::OwnerNode::ImplItem(lctx.lower_impl_item(item)),
+ }
+ })
+ }
+
+ fn lower_foreign_item(&mut self, item: &ForeignItem) {
+ self.with_lctx(item.id, |lctx| hir::OwnerNode::ForeignItem(lctx.lower_foreign_item(item)))
+ }
+}
+
+impl<'hir> LoweringContext<'_, 'hir> {
+ pub(super) fn lower_mod(&mut self, items: &[P<Item>], spans: &ModSpans) -> hir::Mod<'hir> {
+ hir::Mod {
+ spans: hir::ModSpans {
+ inner_span: self.lower_span(spans.inner_span),
+ inject_use_span: self.lower_span(spans.inject_use_span),
+ },
+ item_ids: self.arena.alloc_from_iter(items.iter().flat_map(|x| self.lower_item_ref(x))),
+ }
+ }
+
+ pub(super) fn lower_item_ref(&mut self, i: &Item) -> SmallVec<[hir::ItemId; 1]> {
+ let mut node_ids = smallvec![hir::ItemId { def_id: self.local_def_id(i.id) }];
+ if let ItemKind::Use(ref use_tree) = &i.kind {
+ self.lower_item_id_use_tree(use_tree, i.id, &mut node_ids);
+ }
+ node_ids
+ }
+
+ fn lower_item_id_use_tree(
+ &mut self,
+ tree: &UseTree,
+ base_id: NodeId,
+ vec: &mut SmallVec<[hir::ItemId; 1]>,
+ ) {
+ match tree.kind {
+ UseTreeKind::Nested(ref nested_vec) => {
+ for &(ref nested, id) in nested_vec {
+ vec.push(hir::ItemId { def_id: self.local_def_id(id) });
+ self.lower_item_id_use_tree(nested, id, vec);
+ }
+ }
+ UseTreeKind::Glob => {}
+ UseTreeKind::Simple(_, id1, id2) => {
+ for (_, &id) in
+ iter::zip(self.expect_full_res_from_use(base_id).skip(1), &[id1, id2])
+ {
+ vec.push(hir::ItemId { def_id: self.local_def_id(id) });
+ }
+ }
+ }
+ }
+
+ fn lower_item(&mut self, i: &Item) -> &'hir hir::Item<'hir> {
+ let mut ident = i.ident;
+ let vis_span = self.lower_span(i.vis.span);
+ let hir_id = self.lower_node_id(i.id);
+ let attrs = self.lower_attrs(hir_id, &i.attrs);
+ let kind = self.lower_item_kind(i.span, i.id, hir_id, &mut ident, attrs, vis_span, &i.kind);
+ let item = hir::Item {
+ def_id: hir_id.expect_owner(),
+ ident: self.lower_ident(ident),
+ kind,
+ vis_span,
+ span: self.lower_span(i.span),
+ };
+ self.arena.alloc(item)
+ }
+
+ fn lower_item_kind(
+ &mut self,
+ span: Span,
+ id: NodeId,
+ hir_id: hir::HirId,
+ ident: &mut Ident,
+ attrs: Option<&'hir [Attribute]>,
+ vis_span: Span,
+ i: &ItemKind,
+ ) -> hir::ItemKind<'hir> {
+ match *i {
+ ItemKind::ExternCrate(orig_name) => hir::ItemKind::ExternCrate(orig_name),
+ ItemKind::Use(ref use_tree) => {
+ // Start with an empty prefix.
+ let prefix = Path { segments: vec![], span: use_tree.span, tokens: None };
+
+ self.lower_use_tree(use_tree, &prefix, id, vis_span, ident, attrs)
+ }
+ ItemKind::Static(ref t, m, ref e) => {
+ let (ty, body_id) = self.lower_const_item(t, span, e.as_deref());
+ hir::ItemKind::Static(ty, m, body_id)
+ }
+ ItemKind::Const(_, ref t, ref e) => {
+ let (ty, body_id) = self.lower_const_item(t, span, e.as_deref());
+ hir::ItemKind::Const(ty, body_id)
+ }
+ ItemKind::Fn(box Fn {
+ sig: FnSig { ref decl, header, span: fn_sig_span },
+ ref generics,
+ ref body,
+ ..
+ }) => {
+ self.with_new_scopes(|this| {
+ this.current_item = Some(ident.span);
+
+ // Note: we don't need to change the return type from `T` to
+ // `impl Future<Output = T>` here because lower_body
+ // only cares about the input argument patterns in the function
+ // declaration (decl), not the return types.
+ let asyncness = header.asyncness;
+ let body_id =
+ this.lower_maybe_async_body(span, &decl, asyncness, body.as_deref());
+
+ let itctx = ImplTraitContext::Universal;
+ let (generics, decl) = this.lower_generics(generics, id, itctx, |this| {
+ let ret_id = asyncness.opt_return_id();
+ this.lower_fn_decl(&decl, Some(id), FnDeclKind::Fn, ret_id)
+ });
+ let sig = hir::FnSig {
+ decl,
+ header: this.lower_fn_header(header),
+ span: this.lower_span(fn_sig_span),
+ };
+ hir::ItemKind::Fn(sig, generics, body_id)
+ })
+ }
+ ItemKind::Mod(_, ref mod_kind) => match mod_kind {
+ ModKind::Loaded(items, _, spans) => {
+ hir::ItemKind::Mod(self.lower_mod(items, spans))
+ }
+ ModKind::Unloaded => panic!("`mod` items should have been loaded by now"),
+ },
+ ItemKind::ForeignMod(ref fm) => hir::ItemKind::ForeignMod {
+ abi: fm.abi.map_or(abi::Abi::FALLBACK, |abi| self.lower_abi(abi)),
+ items: self
+ .arena
+ .alloc_from_iter(fm.items.iter().map(|x| self.lower_foreign_item_ref(x))),
+ },
+ ItemKind::GlobalAsm(ref asm) => {
+ hir::ItemKind::GlobalAsm(self.lower_inline_asm(span, asm))
+ }
+ ItemKind::TyAlias(box TyAlias {
+ ref generics,
+ where_clauses,
+ ty: Some(ref ty),
+ ..
+ }) => {
+ // We lower
+ //
+ // type Foo = impl Trait
+ //
+ // to
+ //
+ // type Foo = Foo1
+ // opaque type Foo1: Trait
+ let mut generics = generics.clone();
+ add_ty_alias_where_clause(&mut generics, where_clauses, true);
+ let (generics, ty) = self.lower_generics(
+ &generics,
+ id,
+ ImplTraitContext::Disallowed(ImplTraitPosition::Generic),
+ |this| this.lower_ty(ty, ImplTraitContext::TypeAliasesOpaqueTy),
+ );
+ hir::ItemKind::TyAlias(ty, generics)
+ }
+ ItemKind::TyAlias(box TyAlias {
+ ref generics, ref where_clauses, ty: None, ..
+ }) => {
+ let mut generics = generics.clone();
+ add_ty_alias_where_clause(&mut generics, *where_clauses, true);
+ let (generics, ty) = self.lower_generics(
+ &generics,
+ id,
+ ImplTraitContext::Disallowed(ImplTraitPosition::Generic),
+ |this| this.arena.alloc(this.ty(span, hir::TyKind::Err)),
+ );
+ hir::ItemKind::TyAlias(ty, generics)
+ }
+ ItemKind::Enum(ref enum_definition, ref generics) => {
+ let (generics, variants) = self.lower_generics(
+ generics,
+ id,
+ ImplTraitContext::Disallowed(ImplTraitPosition::Generic),
+ |this| {
+ this.arena.alloc_from_iter(
+ enum_definition.variants.iter().map(|x| this.lower_variant(x)),
+ )
+ },
+ );
+ hir::ItemKind::Enum(hir::EnumDef { variants }, generics)
+ }
+ ItemKind::Struct(ref struct_def, ref generics) => {
+ let (generics, struct_def) = self.lower_generics(
+ generics,
+ id,
+ ImplTraitContext::Disallowed(ImplTraitPosition::Generic),
+ |this| this.lower_variant_data(hir_id, struct_def),
+ );
+ hir::ItemKind::Struct(struct_def, generics)
+ }
+ ItemKind::Union(ref vdata, ref generics) => {
+ let (generics, vdata) = self.lower_generics(
+ generics,
+ id,
+ ImplTraitContext::Disallowed(ImplTraitPosition::Generic),
+ |this| this.lower_variant_data(hir_id, vdata),
+ );
+ hir::ItemKind::Union(vdata, generics)
+ }
+ ItemKind::Impl(box Impl {
+ unsafety,
+ polarity,
+ defaultness,
+ constness,
+ generics: ref ast_generics,
+ of_trait: ref trait_ref,
+ self_ty: ref ty,
+ items: ref impl_items,
+ }) => {
+ // Lower the "impl header" first. This ordering is important
+ // for in-band lifetimes! Consider `'a` here:
+ //
+ // impl Foo<'a> for u32 {
+ // fn method(&'a self) { .. }
+ // }
+ //
+ // Because we start by lowering the `Foo<'a> for u32`
+ // part, we will add `'a` to the list of generics on
+ // the impl. When we then encounter it later in the
+ // method, it will not be considered an in-band
+ // lifetime to be added, but rather a reference to a
+ // parent lifetime.
+ let itctx = ImplTraitContext::Universal;
+ let (generics, (trait_ref, lowered_ty)) =
+ self.lower_generics(ast_generics, id, itctx, |this| {
+ let trait_ref = trait_ref.as_ref().map(|trait_ref| {
+ this.lower_trait_ref(
+ trait_ref,
+ ImplTraitContext::Disallowed(ImplTraitPosition::Trait),
+ )
+ });
+
+ let lowered_ty = this
+ .lower_ty(ty, ImplTraitContext::Disallowed(ImplTraitPosition::Type));
+
+ (trait_ref, lowered_ty)
+ });
+
+ let new_impl_items = self
+ .arena
+ .alloc_from_iter(impl_items.iter().map(|item| self.lower_impl_item_ref(item)));
+
+ // `defaultness.has_value()` is never called for an `impl`, always `true` in order
+ // to not cause an assertion failure inside the `lower_defaultness` function.
+ let has_val = true;
+ let (defaultness, defaultness_span) = self.lower_defaultness(defaultness, has_val);
+ let polarity = match polarity {
+ ImplPolarity::Positive => ImplPolarity::Positive,
+ ImplPolarity::Negative(s) => ImplPolarity::Negative(self.lower_span(s)),
+ };
+ hir::ItemKind::Impl(self.arena.alloc(hir::Impl {
+ unsafety: self.lower_unsafety(unsafety),
+ polarity,
+ defaultness,
+ defaultness_span,
+ constness: self.lower_constness(constness),
+ generics,
+ of_trait: trait_ref,
+ self_ty: lowered_ty,
+ items: new_impl_items,
+ }))
+ }
+ ItemKind::Trait(box Trait {
+ is_auto,
+ unsafety,
+ ref generics,
+ ref bounds,
+ ref items,
+ }) => {
+ let (generics, (unsafety, items, bounds)) = self.lower_generics(
+ generics,
+ id,
+ ImplTraitContext::Disallowed(ImplTraitPosition::Generic),
+ |this| {
+ let bounds = this.lower_param_bounds(
+ bounds,
+ ImplTraitContext::Disallowed(ImplTraitPosition::Bound),
+ );
+ let items = this.arena.alloc_from_iter(
+ items.iter().map(|item| this.lower_trait_item_ref(item)),
+ );
+ let unsafety = this.lower_unsafety(unsafety);
+ (unsafety, items, bounds)
+ },
+ );
+ hir::ItemKind::Trait(is_auto, unsafety, generics, bounds, items)
+ }
+ ItemKind::TraitAlias(ref generics, ref bounds) => {
+ let (generics, bounds) = self.lower_generics(
+ generics,
+ id,
+ ImplTraitContext::Disallowed(ImplTraitPosition::Generic),
+ |this| {
+ this.lower_param_bounds(
+ bounds,
+ ImplTraitContext::Disallowed(ImplTraitPosition::Bound),
+ )
+ },
+ );
+ hir::ItemKind::TraitAlias(generics, bounds)
+ }
+ ItemKind::MacroDef(MacroDef { ref body, macro_rules }) => {
+ let body = P(self.lower_mac_args(body));
+ let macro_kind = self.resolver.decl_macro_kind(self.local_def_id(id));
+ hir::ItemKind::Macro(ast::MacroDef { body, macro_rules }, macro_kind)
+ }
+ ItemKind::MacCall(..) => {
+ panic!("`TyMac` should have been expanded by now")
+ }
+ }
+ }
+
+ fn lower_const_item(
+ &mut self,
+ ty: &Ty,
+ span: Span,
+ body: Option<&Expr>,
+ ) -> (&'hir hir::Ty<'hir>, hir::BodyId) {
+ let ty = self.lower_ty(ty, ImplTraitContext::Disallowed(ImplTraitPosition::Type));
+ (ty, self.lower_const_body(span, body))
+ }
+
+ #[instrument(level = "debug", skip(self))]
+ fn lower_use_tree(
+ &mut self,
+ tree: &UseTree,
+ prefix: &Path,
+ id: NodeId,
+ vis_span: Span,
+ ident: &mut Ident,
+ attrs: Option<&'hir [Attribute]>,
+ ) -> hir::ItemKind<'hir> {
+ let path = &tree.prefix;
+ let segments = prefix.segments.iter().chain(path.segments.iter()).cloned().collect();
+
+ match tree.kind {
+ UseTreeKind::Simple(rename, id1, id2) => {
+ *ident = tree.ident();
+
+ // First, apply the prefix to the path.
+ let mut path = Path { segments, span: path.span, tokens: None };
+
+ // Correctly resolve `self` imports.
+ if path.segments.len() > 1
+ && path.segments.last().unwrap().ident.name == kw::SelfLower
+ {
+ let _ = path.segments.pop();
+ if rename.is_none() {
+ *ident = path.segments.last().unwrap().ident;
+ }
+ }
+
+ let mut resolutions = self.expect_full_res_from_use(id).fuse();
+ // We want to return *something* from this function, so hold onto the first item
+ // for later.
+ let ret_res = self.lower_res(resolutions.next().unwrap_or(Res::Err));
+
+ // Here, we are looping over namespaces, if they exist for the definition
+ // being imported. We only handle type and value namespaces because we
+ // won't be dealing with macros in the rest of the compiler.
+ // Essentially a single `use` which imports two names is desugared into
+ // two imports.
+ for new_node_id in [id1, id2] {
+ let new_id = self.local_def_id(new_node_id);
+ let Some(res) = resolutions.next() else {
+ // Associate an HirId to both ids even if there is no resolution.
+ let _old = self.children.insert(
+ new_id,
+ hir::MaybeOwner::NonOwner(hir::HirId::make_owner(new_id)),
+ );
+ debug_assert!(_old.is_none());
+ continue;
+ };
+ let ident = *ident;
+ let mut path = path.clone();
+ for seg in &mut path.segments {
+ seg.id = self.next_node_id();
+ }
+ let span = path.span;
+
+ self.with_hir_id_owner(new_node_id, |this| {
+ let res = this.lower_res(res);
+ let path = this.lower_path_extra(res, &path, ParamMode::Explicit);
+ let kind = hir::ItemKind::Use(path, hir::UseKind::Single);
+ if let Some(attrs) = attrs {
+ this.attrs.insert(hir::ItemLocalId::new(0), attrs);
+ }
+
+ let item = hir::Item {
+ def_id: new_id,
+ ident: this.lower_ident(ident),
+ kind,
+ vis_span,
+ span: this.lower_span(span),
+ };
+ hir::OwnerNode::Item(this.arena.alloc(item))
+ });
+ }
+
+ let path = self.lower_path_extra(ret_res, &path, ParamMode::Explicit);
+ hir::ItemKind::Use(path, hir::UseKind::Single)
+ }
+ UseTreeKind::Glob => {
+ let path = self.lower_path(
+ id,
+ &Path { segments, span: path.span, tokens: None },
+ ParamMode::Explicit,
+ );
+ hir::ItemKind::Use(path, hir::UseKind::Glob)
+ }
+ UseTreeKind::Nested(ref trees) => {
+ // Nested imports are desugared into simple imports.
+ // So, if we start with
+ //
+ // ```
+ // pub(x) use foo::{a, b};
+ // ```
+ //
+ // we will create three items:
+ //
+ // ```
+ // pub(x) use foo::a;
+ // pub(x) use foo::b;
+ // pub(x) use foo::{}; // <-- this is called the `ListStem`
+ // ```
+ //
+ // The first two are produced by recursively invoking
+ // `lower_use_tree` (and indeed there may be things
+ // like `use foo::{a::{b, c}}` and so forth). They
+ // wind up being directly added to
+ // `self.items`. However, the structure of this
+ // function also requires us to return one item, and
+ // for that we return the `{}` import (called the
+ // `ListStem`).
+
+ let prefix = Path { segments, span: prefix.span.to(path.span), tokens: None };
+
+ // Add all the nested `PathListItem`s to the HIR.
+ for &(ref use_tree, id) in trees {
+ let new_hir_id = self.local_def_id(id);
+
+ let mut prefix = prefix.clone();
+
+ // Give the segments new node-ids since they are being cloned.
+ for seg in &mut prefix.segments {
+ seg.id = self.next_node_id();
+ }
+
+ // Each `use` import is an item and thus are owners of the
+ // names in the path. Up to this point the nested import is
+ // the current owner, since we want each desugared import to
+ // own its own names, we have to adjust the owner before
+ // lowering the rest of the import.
+ self.with_hir_id_owner(id, |this| {
+ let mut ident = *ident;
+
+ let kind =
+ this.lower_use_tree(use_tree, &prefix, id, vis_span, &mut ident, attrs);
+ if let Some(attrs) = attrs {
+ this.attrs.insert(hir::ItemLocalId::new(0), attrs);
+ }
+
+ let item = hir::Item {
+ def_id: new_hir_id,
+ ident: this.lower_ident(ident),
+ kind,
+ vis_span,
+ span: this.lower_span(use_tree.span),
+ };
+ hir::OwnerNode::Item(this.arena.alloc(item))
+ });
+ }
+
+ let res = self.expect_full_res_from_use(id).next().unwrap_or(Res::Err);
+ let res = self.lower_res(res);
+ let path = self.lower_path_extra(res, &prefix, ParamMode::Explicit);
+ hir::ItemKind::Use(path, hir::UseKind::ListStem)
+ }
+ }
+ }
+
+ fn lower_foreign_item(&mut self, i: &ForeignItem) -> &'hir hir::ForeignItem<'hir> {
+ let hir_id = self.lower_node_id(i.id);
+ let def_id = hir_id.expect_owner();
+ self.lower_attrs(hir_id, &i.attrs);
+ let item = hir::ForeignItem {
+ def_id,
+ ident: self.lower_ident(i.ident),
+ kind: match i.kind {
+ ForeignItemKind::Fn(box Fn { ref sig, ref generics, .. }) => {
+ let fdec = &sig.decl;
+ let itctx = ImplTraitContext::Universal;
+ let (generics, (fn_dec, fn_args)) =
+ self.lower_generics(generics, i.id, itctx, |this| {
+ (
+ // Disallow `impl Trait` in foreign items.
+ this.lower_fn_decl(fdec, None, FnDeclKind::ExternFn, None),
+ this.lower_fn_params_to_names(fdec),
+ )
+ });
+
+ hir::ForeignItemKind::Fn(fn_dec, fn_args, generics)
+ }
+ ForeignItemKind::Static(ref t, m, _) => {
+ let ty =
+ self.lower_ty(t, ImplTraitContext::Disallowed(ImplTraitPosition::Type));
+ hir::ForeignItemKind::Static(ty, m)
+ }
+ ForeignItemKind::TyAlias(..) => hir::ForeignItemKind::Type,
+ ForeignItemKind::MacCall(_) => panic!("macro shouldn't exist here"),
+ },
+ vis_span: self.lower_span(i.vis.span),
+ span: self.lower_span(i.span),
+ };
+ self.arena.alloc(item)
+ }
+
+ fn lower_foreign_item_ref(&mut self, i: &ForeignItem) -> hir::ForeignItemRef {
+ hir::ForeignItemRef {
+ id: hir::ForeignItemId { def_id: self.local_def_id(i.id) },
+ ident: self.lower_ident(i.ident),
+ span: self.lower_span(i.span),
+ }
+ }
+
+ fn lower_variant(&mut self, v: &Variant) -> hir::Variant<'hir> {
+ let id = self.lower_node_id(v.id);
+ self.lower_attrs(id, &v.attrs);
+ hir::Variant {
+ id,
+ data: self.lower_variant_data(id, &v.data),
+ disr_expr: v.disr_expr.as_ref().map(|e| self.lower_anon_const(e)),
+ ident: self.lower_ident(v.ident),
+ span: self.lower_span(v.span),
+ }
+ }
+
+ fn lower_variant_data(
+ &mut self,
+ parent_id: hir::HirId,
+ vdata: &VariantData,
+ ) -> hir::VariantData<'hir> {
+ match *vdata {
+ VariantData::Struct(ref fields, recovered) => hir::VariantData::Struct(
+ self.arena
+ .alloc_from_iter(fields.iter().enumerate().map(|f| self.lower_field_def(f))),
+ recovered,
+ ),
+ VariantData::Tuple(ref fields, id) => {
+ let ctor_id = self.lower_node_id(id);
+ self.alias_attrs(ctor_id, parent_id);
+ hir::VariantData::Tuple(
+ self.arena.alloc_from_iter(
+ fields.iter().enumerate().map(|f| self.lower_field_def(f)),
+ ),
+ ctor_id,
+ )
+ }
+ VariantData::Unit(id) => {
+ let ctor_id = self.lower_node_id(id);
+ self.alias_attrs(ctor_id, parent_id);
+ hir::VariantData::Unit(ctor_id)
+ }
+ }
+ }
+
+ fn lower_field_def(&mut self, (index, f): (usize, &FieldDef)) -> hir::FieldDef<'hir> {
+ let ty = if let TyKind::Path(ref qself, ref path) = f.ty.kind {
+ let t = self.lower_path_ty(
+ &f.ty,
+ qself,
+ path,
+ ParamMode::ExplicitNamed, // no `'_` in declarations (Issue #61124)
+ ImplTraitContext::Disallowed(ImplTraitPosition::Path),
+ );
+ self.arena.alloc(t)
+ } else {
+ self.lower_ty(&f.ty, ImplTraitContext::Disallowed(ImplTraitPosition::Type))
+ };
+ let hir_id = self.lower_node_id(f.id);
+ self.lower_attrs(hir_id, &f.attrs);
+ hir::FieldDef {
+ span: self.lower_span(f.span),
+ hir_id,
+ ident: match f.ident {
+ Some(ident) => self.lower_ident(ident),
+ // FIXME(jseyfried): positional field hygiene.
+ None => Ident::new(sym::integer(index), self.lower_span(f.span)),
+ },
+ vis_span: self.lower_span(f.vis.span),
+ ty,
+ }
+ }
+
+ fn lower_trait_item(&mut self, i: &AssocItem) -> &'hir hir::TraitItem<'hir> {
+ let hir_id = self.lower_node_id(i.id);
+ let trait_item_def_id = hir_id.expect_owner();
+
+ let (generics, kind, has_default) = match i.kind {
+ AssocItemKind::Const(_, ref ty, ref default) => {
+ let ty = self.lower_ty(ty, ImplTraitContext::Disallowed(ImplTraitPosition::Type));
+ let body = default.as_ref().map(|x| self.lower_const_body(i.span, Some(x)));
+ (hir::Generics::empty(), hir::TraitItemKind::Const(ty, body), body.is_some())
+ }
+ AssocItemKind::Fn(box Fn { ref sig, ref generics, body: None, .. }) => {
+ let names = self.lower_fn_params_to_names(&sig.decl);
+ let (generics, sig) =
+ self.lower_method_sig(generics, sig, i.id, FnDeclKind::Trait, None);
+ (generics, hir::TraitItemKind::Fn(sig, hir::TraitFn::Required(names)), false)
+ }
+ AssocItemKind::Fn(box Fn { ref sig, ref generics, body: Some(ref body), .. }) => {
+ let asyncness = sig.header.asyncness;
+ let body_id =
+ self.lower_maybe_async_body(i.span, &sig.decl, asyncness, Some(&body));
+ let (generics, sig) = self.lower_method_sig(
+ generics,
+ sig,
+ i.id,
+ FnDeclKind::Trait,
+ asyncness.opt_return_id(),
+ );
+ (generics, hir::TraitItemKind::Fn(sig, hir::TraitFn::Provided(body_id)), true)
+ }
+ AssocItemKind::TyAlias(box TyAlias {
+ ref generics,
+ where_clauses,
+ ref bounds,
+ ref ty,
+ ..
+ }) => {
+ let mut generics = generics.clone();
+ add_ty_alias_where_clause(&mut generics, where_clauses, false);
+ let (generics, kind) = self.lower_generics(
+ &generics,
+ i.id,
+ ImplTraitContext::Disallowed(ImplTraitPosition::Generic),
+ |this| {
+ let ty = ty.as_ref().map(|x| {
+ this.lower_ty(x, ImplTraitContext::Disallowed(ImplTraitPosition::Type))
+ });
+ hir::TraitItemKind::Type(
+ this.lower_param_bounds(
+ bounds,
+ ImplTraitContext::Disallowed(ImplTraitPosition::Generic),
+ ),
+ ty,
+ )
+ },
+ );
+ (generics, kind, ty.is_some())
+ }
+ AssocItemKind::MacCall(..) => panic!("macro item shouldn't exist at this point"),
+ };
+
+ self.lower_attrs(hir_id, &i.attrs);
+ let item = hir::TraitItem {
+ def_id: trait_item_def_id,
+ ident: self.lower_ident(i.ident),
+ generics,
+ kind,
+ span: self.lower_span(i.span),
+ defaultness: hir::Defaultness::Default { has_value: has_default },
+ };
+ self.arena.alloc(item)
+ }
+
+ fn lower_trait_item_ref(&mut self, i: &AssocItem) -> hir::TraitItemRef {
+ let kind = match &i.kind {
+ AssocItemKind::Const(..) => hir::AssocItemKind::Const,
+ AssocItemKind::TyAlias(..) => hir::AssocItemKind::Type,
+ AssocItemKind::Fn(box Fn { sig, .. }) => {
+ hir::AssocItemKind::Fn { has_self: sig.decl.has_self() }
+ }
+ AssocItemKind::MacCall(..) => unimplemented!(),
+ };
+ let id = hir::TraitItemId { def_id: self.local_def_id(i.id) };
+ hir::TraitItemRef {
+ id,
+ ident: self.lower_ident(i.ident),
+ span: self.lower_span(i.span),
+ kind,
+ }
+ }
+
+ /// Construct `ExprKind::Err` for the given `span`.
+ pub(crate) fn expr_err(&mut self, span: Span) -> hir::Expr<'hir> {
+ self.expr(span, hir::ExprKind::Err, AttrVec::new())
+ }
+
+ fn lower_impl_item(&mut self, i: &AssocItem) -> &'hir hir::ImplItem<'hir> {
+ // Since `default impl` is not yet implemented, this is always true in impls.
+ let has_value = true;
+ let (defaultness, _) = self.lower_defaultness(i.kind.defaultness(), has_value);
+
+ let (generics, kind) = match &i.kind {
+ AssocItemKind::Const(_, ty, expr) => {
+ let ty = self.lower_ty(ty, ImplTraitContext::Disallowed(ImplTraitPosition::Type));
+ (
+ hir::Generics::empty(),
+ hir::ImplItemKind::Const(ty, self.lower_const_body(i.span, expr.as_deref())),
+ )
+ }
+ AssocItemKind::Fn(box Fn { sig, generics, body, .. }) => {
+ self.current_item = Some(i.span);
+ let asyncness = sig.header.asyncness;
+ let body_id =
+ self.lower_maybe_async_body(i.span, &sig.decl, asyncness, body.as_deref());
+ let (generics, sig) = self.lower_method_sig(
+ generics,
+ sig,
+ i.id,
+ if self.is_in_trait_impl { FnDeclKind::Impl } else { FnDeclKind::Inherent },
+ asyncness.opt_return_id(),
+ );
+
+ (generics, hir::ImplItemKind::Fn(sig, body_id))
+ }
+ AssocItemKind::TyAlias(box TyAlias { generics, where_clauses, ty, .. }) => {
+ let mut generics = generics.clone();
+ add_ty_alias_where_clause(&mut generics, *where_clauses, false);
+ self.lower_generics(
+ &generics,
+ i.id,
+ ImplTraitContext::Disallowed(ImplTraitPosition::Generic),
+ |this| match ty {
+ None => {
+ let ty = this.arena.alloc(this.ty(i.span, hir::TyKind::Err));
+ hir::ImplItemKind::TyAlias(ty)
+ }
+ Some(ty) => {
+ let ty = this.lower_ty(ty, ImplTraitContext::TypeAliasesOpaqueTy);
+ hir::ImplItemKind::TyAlias(ty)
+ }
+ },
+ )
+ }
+ AssocItemKind::MacCall(..) => panic!("`TyMac` should have been expanded by now"),
+ };
+
+ let hir_id = self.lower_node_id(i.id);
+ self.lower_attrs(hir_id, &i.attrs);
+ let item = hir::ImplItem {
+ def_id: hir_id.expect_owner(),
+ ident: self.lower_ident(i.ident),
+ generics,
+ kind,
+ vis_span: self.lower_span(i.vis.span),
+ span: self.lower_span(i.span),
+ defaultness,
+ };
+ self.arena.alloc(item)
+ }
+
+ fn lower_impl_item_ref(&mut self, i: &AssocItem) -> hir::ImplItemRef {
+ hir::ImplItemRef {
+ id: hir::ImplItemId { def_id: self.local_def_id(i.id) },
+ ident: self.lower_ident(i.ident),
+ span: self.lower_span(i.span),
+ kind: match &i.kind {
+ AssocItemKind::Const(..) => hir::AssocItemKind::Const,
+ AssocItemKind::TyAlias(..) => hir::AssocItemKind::Type,
+ AssocItemKind::Fn(box Fn { sig, .. }) => {
+ hir::AssocItemKind::Fn { has_self: sig.decl.has_self() }
+ }
+ AssocItemKind::MacCall(..) => unimplemented!(),
+ },
+ trait_item_def_id: self.resolver.get_partial_res(i.id).map(|r| r.base_res().def_id()),
+ }
+ }
+
+ fn lower_defaultness(
+ &self,
+ d: Defaultness,
+ has_value: bool,
+ ) -> (hir::Defaultness, Option<Span>) {
+ match d {
+ Defaultness::Default(sp) => {
+ (hir::Defaultness::Default { has_value }, Some(self.lower_span(sp)))
+ }
+ Defaultness::Final => {
+ assert!(has_value);
+ (hir::Defaultness::Final, None)
+ }
+ }
+ }
+
+ fn record_body(
+ &mut self,
+ params: &'hir [hir::Param<'hir>],
+ value: hir::Expr<'hir>,
+ ) -> hir::BodyId {
+ let body = hir::Body { generator_kind: self.generator_kind, params, value };
+ let id = body.id();
+ debug_assert_eq!(id.hir_id.owner, self.current_hir_id_owner);
+ self.bodies.push((id.hir_id.local_id, self.arena.alloc(body)));
+ id
+ }
+
+ pub(super) fn lower_body(
+ &mut self,
+ f: impl FnOnce(&mut Self) -> (&'hir [hir::Param<'hir>], hir::Expr<'hir>),
+ ) -> hir::BodyId {
+ let prev_gen_kind = self.generator_kind.take();
+ let task_context = self.task_context.take();
+ let (parameters, result) = f(self);
+ let body_id = self.record_body(parameters, result);
+ self.task_context = task_context;
+ self.generator_kind = prev_gen_kind;
+ body_id
+ }
+
+ fn lower_param(&mut self, param: &Param) -> hir::Param<'hir> {
+ let hir_id = self.lower_node_id(param.id);
+ self.lower_attrs(hir_id, &param.attrs);
+ hir::Param {
+ hir_id,
+ pat: self.lower_pat(&param.pat),
+ ty_span: self.lower_span(param.ty.span),
+ span: self.lower_span(param.span),
+ }
+ }
+
+ pub(super) fn lower_fn_body(
+ &mut self,
+ decl: &FnDecl,
+ body: impl FnOnce(&mut Self) -> hir::Expr<'hir>,
+ ) -> hir::BodyId {
+ self.lower_body(|this| {
+ (
+ this.arena.alloc_from_iter(decl.inputs.iter().map(|x| this.lower_param(x))),
+ body(this),
+ )
+ })
+ }
+
+ fn lower_fn_body_block(
+ &mut self,
+ span: Span,
+ decl: &FnDecl,
+ body: Option<&Block>,
+ ) -> hir::BodyId {
+ self.lower_fn_body(decl, |this| this.lower_block_expr_opt(span, body))
+ }
+
+ fn lower_block_expr_opt(&mut self, span: Span, block: Option<&Block>) -> hir::Expr<'hir> {
+ match block {
+ Some(block) => self.lower_block_expr(block),
+ None => self.expr_err(span),
+ }
+ }
+
+ pub(super) fn lower_const_body(&mut self, span: Span, expr: Option<&Expr>) -> hir::BodyId {
+ self.lower_body(|this| {
+ (
+ &[],
+ match expr {
+ Some(expr) => this.lower_expr_mut(expr),
+ None => this.expr_err(span),
+ },
+ )
+ })
+ }
+
+ fn lower_maybe_async_body(
+ &mut self,
+ span: Span,
+ decl: &FnDecl,
+ asyncness: Async,
+ body: Option<&Block>,
+ ) -> hir::BodyId {
+ let closure_id = match asyncness {
+ Async::Yes { closure_id, .. } => closure_id,
+ Async::No => return self.lower_fn_body_block(span, decl, body),
+ };
+
+ self.lower_body(|this| {
+ let mut parameters: Vec<hir::Param<'_>> = Vec::new();
+ let mut statements: Vec<hir::Stmt<'_>> = Vec::new();
+
+ // Async function parameters are lowered into the closure body so that they are
+ // captured and so that the drop order matches the equivalent non-async functions.
+ //
+ // from:
+ //
+ // async fn foo(<pattern>: <ty>, <pattern>: <ty>, <pattern>: <ty>) {
+ // <body>
+ // }
+ //
+ // into:
+ //
+ // fn foo(__arg0: <ty>, __arg1: <ty>, __arg2: <ty>) {
+ // async move {
+ // let __arg2 = __arg2;
+ // let <pattern> = __arg2;
+ // let __arg1 = __arg1;
+ // let <pattern> = __arg1;
+ // let __arg0 = __arg0;
+ // let <pattern> = __arg0;
+ // drop-temps { <body> } // see comments later in fn for details
+ // }
+ // }
+ //
+ // If `<pattern>` is a simple ident, then it is lowered to a single
+ // `let <pattern> = <pattern>;` statement as an optimization.
+ //
+ // Note that the body is embedded in `drop-temps`; an
+ // equivalent desugaring would be `return { <body>
+ // };`. The key point is that we wish to drop all the
+ // let-bound variables and temporaries created in the body
+ // (and its tail expression!) before we drop the
+ // parameters (c.f. rust-lang/rust#64512).
+ for (index, parameter) in decl.inputs.iter().enumerate() {
+ let parameter = this.lower_param(parameter);
+ let span = parameter.pat.span;
+
+ // Check if this is a binding pattern, if so, we can optimize and avoid adding a
+ // `let <pat> = __argN;` statement. In this case, we do not rename the parameter.
+ let (ident, is_simple_parameter) = match parameter.pat.kind {
+ hir::PatKind::Binding(
+ hir::BindingAnnotation::Unannotated | hir::BindingAnnotation::Mutable,
+ _,
+ ident,
+ _,
+ ) => (ident, true),
+ // For `ref mut` or wildcard arguments, we can't reuse the binding, but
+ // we can keep the same name for the parameter.
+ // This lets rustdoc render it correctly in documentation.
+ hir::PatKind::Binding(_, _, ident, _) => (ident, false),
+ hir::PatKind::Wild => {
+ (Ident::with_dummy_span(rustc_span::symbol::kw::Underscore), false)
+ }
+ _ => {
+ // Replace the ident for bindings that aren't simple.
+ let name = format!("__arg{}", index);
+ let ident = Ident::from_str(&name);
+
+ (ident, false)
+ }
+ };
+
+ let desugared_span = this.mark_span_with_reason(DesugaringKind::Async, span, None);
+
+ // Construct a parameter representing `__argN: <ty>` to replace the parameter of the
+ // async function.
+ //
+ // If this is the simple case, this parameter will end up being the same as the
+ // original parameter, but with a different pattern id.
+ let stmt_attrs = this.attrs.get(&parameter.hir_id.local_id).copied();
+ let (new_parameter_pat, new_parameter_id) = this.pat_ident(desugared_span, ident);
+ let new_parameter = hir::Param {
+ hir_id: parameter.hir_id,
+ pat: new_parameter_pat,
+ ty_span: this.lower_span(parameter.ty_span),
+ span: this.lower_span(parameter.span),
+ };
+
+ if is_simple_parameter {
+ // If this is the simple case, then we only insert one statement that is
+ // `let <pat> = <pat>;`. We re-use the original argument's pattern so that
+ // `HirId`s are densely assigned.
+ let expr = this.expr_ident(desugared_span, ident, new_parameter_id);
+ let stmt = this.stmt_let_pat(
+ stmt_attrs,
+ desugared_span,
+ Some(expr),
+ parameter.pat,
+ hir::LocalSource::AsyncFn,
+ );
+ statements.push(stmt);
+ } else {
+ // If this is not the simple case, then we construct two statements:
+ //
+ // ```
+ // let __argN = __argN;
+ // let <pat> = __argN;
+ // ```
+ //
+ // The first statement moves the parameter into the closure and thus ensures
+ // that the drop order is correct.
+ //
+ // The second statement creates the bindings that the user wrote.
+
+ // Construct the `let mut __argN = __argN;` statement. It must be a mut binding
+ // because the user may have specified a `ref mut` binding in the next
+ // statement.
+ let (move_pat, move_id) = this.pat_ident_binding_mode(
+ desugared_span,
+ ident,
+ hir::BindingAnnotation::Mutable,
+ );
+ let move_expr = this.expr_ident(desugared_span, ident, new_parameter_id);
+ let move_stmt = this.stmt_let_pat(
+ None,
+ desugared_span,
+ Some(move_expr),
+ move_pat,
+ hir::LocalSource::AsyncFn,
+ );
+
+ // Construct the `let <pat> = __argN;` statement. We re-use the original
+ // parameter's pattern so that `HirId`s are densely assigned.
+ let pattern_expr = this.expr_ident(desugared_span, ident, move_id);
+ let pattern_stmt = this.stmt_let_pat(
+ stmt_attrs,
+ desugared_span,
+ Some(pattern_expr),
+ parameter.pat,
+ hir::LocalSource::AsyncFn,
+ );
+
+ statements.push(move_stmt);
+ statements.push(pattern_stmt);
+ };
+
+ parameters.push(new_parameter);
+ }
+
+ let body_span = body.map_or(span, |b| b.span);
+ let async_expr = this.make_async_expr(
+ CaptureBy::Value,
+ closure_id,
+ None,
+ body_span,
+ hir::AsyncGeneratorKind::Fn,
+ |this| {
+ // Create a block from the user's function body:
+ let user_body = this.lower_block_expr_opt(body_span, body);
+
+ // Transform into `drop-temps { <user-body> }`, an expression:
+ let desugared_span =
+ this.mark_span_with_reason(DesugaringKind::Async, user_body.span, None);
+ let user_body = this.expr_drop_temps(
+ desugared_span,
+ this.arena.alloc(user_body),
+ AttrVec::new(),
+ );
+
+ // As noted above, create the final block like
+ //
+ // ```
+ // {
+ // let $param_pattern = $raw_param;
+ // ...
+ // drop-temps { <user-body> }
+ // }
+ // ```
+ let body = this.block_all(
+ desugared_span,
+ this.arena.alloc_from_iter(statements),
+ Some(user_body),
+ );
+
+ this.expr_block(body, AttrVec::new())
+ },
+ );
+
+ (
+ this.arena.alloc_from_iter(parameters),
+ this.expr(body_span, async_expr, AttrVec::new()),
+ )
+ })
+ }
+
+ fn lower_method_sig(
+ &mut self,
+ generics: &Generics,
+ sig: &FnSig,
+ id: NodeId,
+ kind: FnDeclKind,
+ is_async: Option<NodeId>,
+ ) -> (&'hir hir::Generics<'hir>, hir::FnSig<'hir>) {
+ let header = self.lower_fn_header(sig.header);
+ let itctx = ImplTraitContext::Universal;
+ let (generics, decl) = self.lower_generics(generics, id, itctx, |this| {
+ this.lower_fn_decl(&sig.decl, Some(id), kind, is_async)
+ });
+ (generics, hir::FnSig { header, decl, span: self.lower_span(sig.span) })
+ }
+
+ fn lower_fn_header(&mut self, h: FnHeader) -> hir::FnHeader {
+ hir::FnHeader {
+ unsafety: self.lower_unsafety(h.unsafety),
+ asyncness: self.lower_asyncness(h.asyncness),
+ constness: self.lower_constness(h.constness),
+ abi: self.lower_extern(h.ext),
+ }
+ }
+
+ pub(super) fn lower_abi(&mut self, abi: StrLit) -> abi::Abi {
+ abi::lookup(abi.symbol_unescaped.as_str()).unwrap_or_else(|| {
+ self.error_on_invalid_abi(abi);
+ abi::Abi::Rust
+ })
+ }
+
+ pub(super) fn lower_extern(&mut self, ext: Extern) -> abi::Abi {
+ match ext {
+ Extern::None => abi::Abi::Rust,
+ Extern::Implicit(_) => abi::Abi::FALLBACK,
+ Extern::Explicit(abi, _) => self.lower_abi(abi),
+ }
+ }
+
+ fn error_on_invalid_abi(&self, abi: StrLit) {
+ struct_span_err!(self.tcx.sess, abi.span, E0703, "invalid ABI: found `{}`", abi.symbol)
+ .span_label(abi.span, "invalid ABI")
+ .help(&format!("valid ABIs: {}", abi::all_names().join(", ")))
+ .emit();
+ }
+
+ fn lower_asyncness(&mut self, a: Async) -> hir::IsAsync {
+ match a {
+ Async::Yes { .. } => hir::IsAsync::Async,
+ Async::No => hir::IsAsync::NotAsync,
+ }
+ }
+
+ fn lower_constness(&mut self, c: Const) -> hir::Constness {
+ match c {
+ Const::Yes(_) => hir::Constness::Const,
+ Const::No => hir::Constness::NotConst,
+ }
+ }
+
+ pub(super) fn lower_unsafety(&mut self, u: Unsafe) -> hir::Unsafety {
+ match u {
+ Unsafe::Yes(_) => hir::Unsafety::Unsafe,
+ Unsafe::No => hir::Unsafety::Normal,
+ }
+ }
+
+ /// Return the pair of the lowered `generics` as `hir::Generics` and the evaluation of `f` with
+ /// the carried impl trait definitions and bounds.
+ #[instrument(level = "debug", skip(self, f))]
+ fn lower_generics<T>(
+ &mut self,
+ generics: &Generics,
+ parent_node_id: NodeId,
+ itctx: ImplTraitContext,
+ f: impl FnOnce(&mut Self) -> T,
+ ) -> (&'hir hir::Generics<'hir>, T) {
+ debug_assert!(self.impl_trait_defs.is_empty());
+ debug_assert!(self.impl_trait_bounds.is_empty());
+
+ // Error if `?Trait` bounds in where clauses don't refer directly to type parameters.
+ // Note: we used to clone these bounds directly onto the type parameter (and avoid lowering
+ // these into hir when we lower thee where clauses), but this makes it quite difficult to
+ // keep track of the Span info. Now, `add_implicitly_sized` in `AstConv` checks both param bounds and
+ // where clauses for `?Sized`.
+ for pred in &generics.where_clause.predicates {
+ let WherePredicate::BoundPredicate(ref bound_pred) = *pred else {
+ continue;
+ };
+ let compute_is_param = || {
+ // Check if the where clause type is a plain type parameter.
+ match self
+ .resolver
+ .get_partial_res(bound_pred.bounded_ty.id)
+ .map(|d| (d.base_res(), d.unresolved_segments()))
+ {
+ Some((Res::Def(DefKind::TyParam, def_id), 0))
+ if bound_pred.bound_generic_params.is_empty() =>
+ {
+ generics
+ .params
+ .iter()
+ .any(|p| def_id == self.local_def_id(p.id).to_def_id())
+ }
+ // Either the `bounded_ty` is not a plain type parameter, or
+ // it's not found in the generic type parameters list.
+ _ => false,
+ }
+ };
+ // We only need to compute this once per `WherePredicate`, but don't
+ // need to compute this at all unless there is a Maybe bound.
+ let mut is_param: Option<bool> = None;
+ for bound in &bound_pred.bounds {
+ if !matches!(*bound, GenericBound::Trait(_, TraitBoundModifier::Maybe)) {
+ continue;
+ }
+ let is_param = *is_param.get_or_insert_with(compute_is_param);
+ if !is_param {
+ self.diagnostic().span_err(
+ bound.span(),
+ "`?Trait` bounds are only permitted at the \
+ point where a type parameter is declared",
+ );
+ }
+ }
+ }
+
+ let mut predicates: SmallVec<[hir::WherePredicate<'hir>; 4]> = SmallVec::new();
+ predicates.extend(generics.params.iter().filter_map(|param| {
+ self.lower_generic_bound_predicate(
+ param.ident,
+ param.id,
+ &param.kind,
+ &param.bounds,
+ itctx,
+ PredicateOrigin::GenericParam,
+ )
+ }));
+ predicates.extend(
+ generics
+ .where_clause
+ .predicates
+ .iter()
+ .map(|predicate| self.lower_where_predicate(predicate)),
+ );
+
+ let mut params: SmallVec<[hir::GenericParam<'hir>; 4]> =
+ self.lower_generic_params_mut(&generics.params).collect();
+
+ // Introduce extra lifetimes if late resolution tells us to.
+ let extra_lifetimes = self.resolver.take_extra_lifetime_params(parent_node_id);
+ params.extend(extra_lifetimes.into_iter().filter_map(|(ident, node_id, res)| {
+ self.lifetime_res_to_generic_param(ident, node_id, res)
+ }));
+
+ let has_where_clause_predicates = !generics.where_clause.predicates.is_empty();
+ let where_clause_span = self.lower_span(generics.where_clause.span);
+ let span = self.lower_span(generics.span);
+ let res = f(self);
+
+ let impl_trait_defs = std::mem::take(&mut self.impl_trait_defs);
+ params.extend(impl_trait_defs.into_iter());
+
+ let impl_trait_bounds = std::mem::take(&mut self.impl_trait_bounds);
+ predicates.extend(impl_trait_bounds.into_iter());
+
+ let lowered_generics = self.arena.alloc(hir::Generics {
+ params: self.arena.alloc_from_iter(params),
+ predicates: self.arena.alloc_from_iter(predicates),
+ has_where_clause_predicates,
+ where_clause_span,
+ span,
+ });
+
+ (lowered_generics, res)
+ }
+
+ pub(super) fn lower_generic_bound_predicate(
+ &mut self,
+ ident: Ident,
+ id: NodeId,
+ kind: &GenericParamKind,
+ bounds: &[GenericBound],
+ itctx: ImplTraitContext,
+ origin: PredicateOrigin,
+ ) -> Option<hir::WherePredicate<'hir>> {
+ // Do not create a clause if we do not have anything inside it.
+ if bounds.is_empty() {
+ return None;
+ }
+
+ let bounds = self.lower_param_bounds(bounds, itctx);
+
+ let ident = self.lower_ident(ident);
+ let param_span = ident.span;
+ let span = bounds
+ .iter()
+ .fold(Some(param_span.shrink_to_hi()), |span: Option<Span>, bound| {
+ let bound_span = bound.span();
+ // We include bounds that come from a `#[derive(_)]` but point at the user's code,
+ // as we use this method to get a span appropriate for suggestions.
+ if !bound_span.can_be_used_for_suggestions() {
+ None
+ } else if let Some(span) = span {
+ Some(span.to(bound_span))
+ } else {
+ Some(bound_span)
+ }
+ })
+ .unwrap_or(param_span.shrink_to_hi());
+ match kind {
+ GenericParamKind::Const { .. } => None,
+ GenericParamKind::Type { .. } => {
+ let def_id = self.local_def_id(id).to_def_id();
+ let ty_path = self.arena.alloc(hir::Path {
+ span: param_span,
+ res: Res::Def(DefKind::TyParam, def_id),
+ segments: self.arena.alloc_from_iter([hir::PathSegment::from_ident(ident)]),
+ });
+ let ty_id = self.next_id();
+ let bounded_ty =
+ self.ty_path(ty_id, param_span, hir::QPath::Resolved(None, ty_path));
+ Some(hir::WherePredicate::BoundPredicate(hir::WhereBoundPredicate {
+ bounded_ty: self.arena.alloc(bounded_ty),
+ bounds,
+ span,
+ bound_generic_params: &[],
+ origin,
+ }))
+ }
+ GenericParamKind::Lifetime => {
+ let ident_span = self.lower_span(ident.span);
+ let ident = self.lower_ident(ident);
+ let lt_id = self.next_node_id();
+ let lifetime = self.new_named_lifetime(id, lt_id, ident_span, ident);
+ Some(hir::WherePredicate::RegionPredicate(hir::WhereRegionPredicate {
+ lifetime,
+ span,
+ bounds,
+ in_where_clause: false,
+ }))
+ }
+ }
+ }
+
+ fn lower_where_predicate(&mut self, pred: &WherePredicate) -> hir::WherePredicate<'hir> {
+ match *pred {
+ WherePredicate::BoundPredicate(WhereBoundPredicate {
+ ref bound_generic_params,
+ ref bounded_ty,
+ ref bounds,
+ span,
+ }) => hir::WherePredicate::BoundPredicate(hir::WhereBoundPredicate {
+ bound_generic_params: self.lower_generic_params(bound_generic_params),
+ bounded_ty: self
+ .lower_ty(bounded_ty, ImplTraitContext::Disallowed(ImplTraitPosition::Type)),
+ bounds: self.arena.alloc_from_iter(bounds.iter().map(|bound| {
+ self.lower_param_bound(
+ bound,
+ ImplTraitContext::Disallowed(ImplTraitPosition::Bound),
+ )
+ })),
+ span: self.lower_span(span),
+ origin: PredicateOrigin::WhereClause,
+ }),
+ WherePredicate::RegionPredicate(WhereRegionPredicate {
+ ref lifetime,
+ ref bounds,
+ span,
+ }) => hir::WherePredicate::RegionPredicate(hir::WhereRegionPredicate {
+ span: self.lower_span(span),
+ lifetime: self.lower_lifetime(lifetime),
+ bounds: self.lower_param_bounds(
+ bounds,
+ ImplTraitContext::Disallowed(ImplTraitPosition::Bound),
+ ),
+ in_where_clause: true,
+ }),
+ WherePredicate::EqPredicate(WhereEqPredicate { id, ref lhs_ty, ref rhs_ty, span }) => {
+ hir::WherePredicate::EqPredicate(hir::WhereEqPredicate {
+ hir_id: self.lower_node_id(id),
+ lhs_ty: self
+ .lower_ty(lhs_ty, ImplTraitContext::Disallowed(ImplTraitPosition::Type)),
+ rhs_ty: self
+ .lower_ty(rhs_ty, ImplTraitContext::Disallowed(ImplTraitPosition::Type)),
+ span: self.lower_span(span),
+ })
+ }
+ }
+ }
+}
diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs
new file mode 100644
index 000000000..224dc3c23
--- /dev/null
+++ b/compiler/rustc_ast_lowering/src/lib.rs
@@ -0,0 +1,2501 @@
+//! Lowers the AST to the HIR.
+//!
+//! Since the AST and HIR are fairly similar, this is mostly a simple procedure,
+//! much like a fold. Where lowering involves a bit more work things get more
+//! interesting and there are some invariants you should know about. These mostly
+//! concern spans and IDs.
+//!
+//! Spans are assigned to AST nodes during parsing and then are modified during
+//! expansion to indicate the origin of a node and the process it went through
+//! being expanded. IDs are assigned to AST nodes just before lowering.
+//!
+//! For the simpler lowering steps, IDs and spans should be preserved. Unlike
+//! expansion we do not preserve the process of lowering in the spans, so spans
+//! should not be modified here. When creating a new node (as opposed to
+//! "folding" an existing one), create a new ID using `next_id()`.
+//!
+//! You must ensure that IDs are unique. That means that you should only use the
+//! ID from an AST node in a single HIR node (you can assume that AST node-IDs
+//! are unique). Every new node must have a unique ID. Avoid cloning HIR nodes.
+//! If you do, you must then set the new node's ID to a fresh one.
+//!
+//! Spans are used for error messages and for tools to map semantics back to
+//! source code. It is therefore not as important with spans as IDs to be strict
+//! about use (you can't break the compiler by screwing up a span). Obviously, a
+//! HIR node can only have a single span. But multiple nodes can have the same
+//! span and spans don't need to be kept in order, etc. Where code is preserved
+//! by lowering, it should have the same span as in the AST. Where HIR nodes are
+//! new it is probably best to give a span for the whole AST node being lowered.
+//! All nodes should have real spans; don't use dummy spans. Tools are likely to
+//! get confused if the spans from leaf AST nodes occur in multiple places
+//! in the HIR, especially for multiple identifiers.
+
+#![feature(box_patterns)]
+#![feature(let_chains)]
+#![feature(let_else)]
+#![feature(never_type)]
+#![recursion_limit = "256"]
+#![allow(rustc::potential_query_instability)]
+
+#[macro_use]
+extern crate tracing;
+
+use rustc_ast::visit;
+use rustc_ast::{self as ast, *};
+use rustc_ast_pretty::pprust;
+use rustc_data_structures::captures::Captures;
+use rustc_data_structures::fingerprint::Fingerprint;
+use rustc_data_structures::fx::FxHashMap;
+use rustc_data_structures::sorted_map::SortedMap;
+use rustc_data_structures::stable_hasher::{HashStable, StableHasher};
+use rustc_data_structures::sync::Lrc;
+use rustc_errors::{struct_span_err, Applicability, Handler};
+use rustc_hir as hir;
+use rustc_hir::def::{DefKind, LifetimeRes, Namespace, PartialRes, PerNS, Res};
+use rustc_hir::def_id::{LocalDefId, CRATE_DEF_ID};
+use rustc_hir::definitions::DefPathData;
+use rustc_hir::{ConstArg, GenericArg, ItemLocalId, ParamName, TraitCandidate};
+use rustc_index::vec::{Idx, IndexVec};
+use rustc_middle::span_bug;
+use rustc_middle::ty::{ResolverAstLowering, TyCtxt};
+use rustc_session::parse::feature_err;
+use rustc_span::hygiene::MacroKind;
+use rustc_span::source_map::DesugaringKind;
+use rustc_span::symbol::{kw, sym, Ident, Symbol};
+use rustc_span::{Span, DUMMY_SP};
+
+use smallvec::SmallVec;
+use std::collections::hash_map::Entry;
+
+macro_rules! arena_vec {
+ ($this:expr; $($x:expr),*) => (
+ $this.arena.alloc_from_iter([$($x),*])
+ );
+}
+
+mod asm;
+mod block;
+mod expr;
+mod index;
+mod item;
+mod lifetime_collector;
+mod pat;
+mod path;
+
+struct LoweringContext<'a, 'hir> {
+ tcx: TyCtxt<'hir>,
+ resolver: &'a mut ResolverAstLowering,
+
+ /// Used to allocate HIR nodes.
+ arena: &'hir hir::Arena<'hir>,
+
+ /// Bodies inside the owner being lowered.
+ bodies: Vec<(hir::ItemLocalId, &'hir hir::Body<'hir>)>,
+ /// Attributes inside the owner being lowered.
+ attrs: SortedMap<hir::ItemLocalId, &'hir [Attribute]>,
+ /// Collect items that were created by lowering the current owner.
+ children: FxHashMap<LocalDefId, hir::MaybeOwner<&'hir hir::OwnerInfo<'hir>>>,
+
+ generator_kind: Option<hir::GeneratorKind>,
+
+ /// When inside an `async` context, this is the `HirId` of the
+ /// `task_context` local bound to the resume argument of the generator.
+ task_context: Option<hir::HirId>,
+
+ /// Used to get the current `fn`'s def span to point to when using `await`
+ /// outside of an `async fn`.
+ current_item: Option<Span>,
+
+ catch_scope: Option<NodeId>,
+ loop_scope: Option<NodeId>,
+ is_in_loop_condition: bool,
+ is_in_trait_impl: bool,
+ is_in_dyn_type: bool,
+
+ current_hir_id_owner: LocalDefId,
+ item_local_id_counter: hir::ItemLocalId,
+ local_id_to_def_id: SortedMap<ItemLocalId, LocalDefId>,
+ trait_map: FxHashMap<ItemLocalId, Box<[TraitCandidate]>>,
+
+ impl_trait_defs: Vec<hir::GenericParam<'hir>>,
+ impl_trait_bounds: Vec<hir::WherePredicate<'hir>>,
+
+ /// NodeIds that are lowered inside the current HIR owner.
+ node_id_to_local_id: FxHashMap<NodeId, hir::ItemLocalId>,
+
+ allow_try_trait: Option<Lrc<[Symbol]>>,
+ allow_gen_future: Option<Lrc<[Symbol]>>,
+ allow_into_future: Option<Lrc<[Symbol]>>,
+}
+
+trait ResolverAstLoweringExt {
+ fn legacy_const_generic_args(&self, expr: &Expr) -> Option<Vec<usize>>;
+ fn get_partial_res(&self, id: NodeId) -> Option<PartialRes>;
+ fn get_import_res(&self, id: NodeId) -> PerNS<Option<Res<NodeId>>>;
+ fn get_label_res(&self, id: NodeId) -> Option<NodeId>;
+ fn get_lifetime_res(&self, id: NodeId) -> Option<LifetimeRes>;
+ fn take_extra_lifetime_params(&mut self, id: NodeId) -> Vec<(Ident, NodeId, LifetimeRes)>;
+ fn decl_macro_kind(&self, def_id: LocalDefId) -> MacroKind;
+ /// Record the map from `from` local def id to `to` local def id, on `generics_def_id_map`
+ /// field.
+ fn record_def_id_remap(&mut self, from: LocalDefId, to: LocalDefId);
+ /// Get the previously recorded `to` local def id given the `from` local def id, obtained using
+ /// `generics_def_id_map` field.
+ fn get_remapped_def_id(&self, local_def_id: LocalDefId) -> LocalDefId;
+}
+
+impl ResolverAstLoweringExt for ResolverAstLowering {
+ fn legacy_const_generic_args(&self, expr: &Expr) -> Option<Vec<usize>> {
+ if let ExprKind::Path(None, path) = &expr.kind {
+ // Don't perform legacy const generics rewriting if the path already
+ // has generic arguments.
+ if path.segments.last().unwrap().args.is_some() {
+ return None;
+ }
+
+ let partial_res = self.partial_res_map.get(&expr.id)?;
+ if partial_res.unresolved_segments() != 0 {
+ return None;
+ }
+
+ if let Res::Def(DefKind::Fn, def_id) = partial_res.base_res() {
+ // We only support cross-crate argument rewriting. Uses
+ // within the same crate should be updated to use the new
+ // const generics style.
+ if def_id.is_local() {
+ return None;
+ }
+
+ if let Some(v) = self.legacy_const_generic_args.get(&def_id) {
+ return v.clone();
+ }
+ }
+ }
+
+ None
+ }
+
+ /// Obtains resolution for a `NodeId` with a single resolution.
+ fn get_partial_res(&self, id: NodeId) -> Option<PartialRes> {
+ self.partial_res_map.get(&id).copied()
+ }
+
+ /// Obtains per-namespace resolutions for `use` statement with the given `NodeId`.
+ fn get_import_res(&self, id: NodeId) -> PerNS<Option<Res<NodeId>>> {
+ self.import_res_map.get(&id).copied().unwrap_or_default()
+ }
+
+ /// Obtains resolution for a label with the given `NodeId`.
+ fn get_label_res(&self, id: NodeId) -> Option<NodeId> {
+ self.label_res_map.get(&id).copied()
+ }
+
+ /// Obtains resolution for a lifetime with the given `NodeId`.
+ fn get_lifetime_res(&self, id: NodeId) -> Option<LifetimeRes> {
+ self.lifetimes_res_map.get(&id).copied()
+ }
+
+ /// Obtain the list of lifetimes parameters to add to an item.
+ ///
+ /// Extra lifetime parameters should only be added in places that can appear
+ /// as a `binder` in `LifetimeRes`.
+ ///
+ /// The extra lifetimes that appear from the parenthesized `Fn`-trait desugaring
+ /// should appear at the enclosing `PolyTraitRef`.
+ fn take_extra_lifetime_params(&mut self, id: NodeId) -> Vec<(Ident, NodeId, LifetimeRes)> {
+ self.extra_lifetime_params_map.remove(&id).unwrap_or_default()
+ }
+
+ fn decl_macro_kind(&self, def_id: LocalDefId) -> MacroKind {
+ self.builtin_macro_kinds.get(&def_id).copied().unwrap_or(MacroKind::Bang)
+ }
+
+ /// Push a remapping into the top-most map.
+ /// Panics if no map has been pushed.
+ /// Remapping is used when creating lowering `-> impl Trait` return
+ /// types to create the resulting opaque type.
+ #[tracing::instrument(level = "debug", skip(self))]
+ fn record_def_id_remap(&mut self, from: LocalDefId, to: LocalDefId) {
+ self.generics_def_id_map.last_mut().expect("no map pushed").insert(from, to);
+ }
+
+ fn get_remapped_def_id(&self, mut local_def_id: LocalDefId) -> LocalDefId {
+ // `generics_def_id_map` is a stack of mappings. As we go deeper in impl traits nesting we
+ // push new mappings so we need to try first the latest mappings, hence `iter().rev()`.
+ //
+ // Consider:
+ //
+ // `fn test<'a, 'b>() -> impl Trait<&'a u8, Ty = impl Sized + 'b> {}`
+ //
+ // We would end with a generics_def_id_map like:
+ //
+ // `[[fn#'b -> impl_trait#'b], [fn#'b -> impl_sized#'b]]`
+ //
+ // for the opaque type generated on `impl Sized + 'b`, We want the result to be:
+ // impl_sized#'b, so iterating forward is the wrong thing to do.
+ for map in self.generics_def_id_map.iter().rev() {
+ if let Some(r) = map.get(&local_def_id) {
+ debug!("def_id_remapper: remapping from `{local_def_id:?}` to `{r:?}`");
+ local_def_id = *r;
+ } else {
+ debug!("def_id_remapper: no remapping for `{local_def_id:?}` found in map");
+ }
+ }
+
+ local_def_id
+ }
+}
+
+/// Context of `impl Trait` in code, which determines whether it is allowed in an HIR subtree,
+/// and if so, what meaning it has.
+#[derive(Debug, Copy, Clone, PartialEq, Eq)]
+enum ImplTraitContext {
+ /// Treat `impl Trait` as shorthand for a new universal generic parameter.
+ /// Example: `fn foo(x: impl Debug)`, where `impl Debug` is conceptually
+ /// equivalent to a fresh universal parameter like `fn foo<T: Debug>(x: T)`.
+ ///
+ /// Newly generated parameters should be inserted into the given `Vec`.
+ Universal,
+
+ /// Treat `impl Trait` as shorthand for a new opaque type.
+ /// Example: `fn foo() -> impl Debug`, where `impl Debug` is conceptually
+ /// equivalent to a new opaque type like `type T = impl Debug; fn foo() -> T`.
+ ///
+ ReturnPositionOpaqueTy {
+ /// Origin: Either OpaqueTyOrigin::FnReturn or OpaqueTyOrigin::AsyncFn,
+ origin: hir::OpaqueTyOrigin,
+ },
+ /// Impl trait in type aliases.
+ TypeAliasesOpaqueTy,
+ /// `impl Trait` is not accepted in this position.
+ Disallowed(ImplTraitPosition),
+}
+
+/// Position in which `impl Trait` is disallowed.
+#[derive(Debug, Copy, Clone, PartialEq, Eq)]
+enum ImplTraitPosition {
+ Path,
+ Variable,
+ Type,
+ Trait,
+ AsyncBlock,
+ Bound,
+ Generic,
+ ExternFnParam,
+ ClosureParam,
+ PointerParam,
+ FnTraitParam,
+ TraitParam,
+ ImplParam,
+ ExternFnReturn,
+ ClosureReturn,
+ PointerReturn,
+ FnTraitReturn,
+ TraitReturn,
+ ImplReturn,
+}
+
+impl std::fmt::Display for ImplTraitPosition {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ let name = match self {
+ ImplTraitPosition::Path => "path",
+ ImplTraitPosition::Variable => "variable binding",
+ ImplTraitPosition::Type => "type",
+ ImplTraitPosition::Trait => "trait",
+ ImplTraitPosition::AsyncBlock => "async block",
+ ImplTraitPosition::Bound => "bound",
+ ImplTraitPosition::Generic => "generic",
+ ImplTraitPosition::ExternFnParam => "`extern fn` param",
+ ImplTraitPosition::ClosureParam => "closure param",
+ ImplTraitPosition::PointerParam => "`fn` pointer param",
+ ImplTraitPosition::FnTraitParam => "`Fn` trait param",
+ ImplTraitPosition::TraitParam => "trait method param",
+ ImplTraitPosition::ImplParam => "`impl` method param",
+ ImplTraitPosition::ExternFnReturn => "`extern fn` return",
+ ImplTraitPosition::ClosureReturn => "closure return",
+ ImplTraitPosition::PointerReturn => "`fn` pointer return",
+ ImplTraitPosition::FnTraitReturn => "`Fn` trait return",
+ ImplTraitPosition::TraitReturn => "trait method return",
+ ImplTraitPosition::ImplReturn => "`impl` method return",
+ };
+
+ write!(f, "{}", name)
+ }
+}
+
+#[derive(Debug)]
+enum FnDeclKind {
+ Fn,
+ Inherent,
+ ExternFn,
+ Closure,
+ Pointer,
+ Trait,
+ Impl,
+}
+
+impl FnDeclKind {
+ fn impl_trait_return_allowed(&self) -> bool {
+ match self {
+ FnDeclKind::Fn | FnDeclKind::Inherent => true,
+ _ => false,
+ }
+ }
+}
+
+#[derive(Copy, Clone)]
+enum AstOwner<'a> {
+ NonOwner,
+ Crate(&'a ast::Crate),
+ Item(&'a ast::Item),
+ AssocItem(&'a ast::AssocItem, visit::AssocCtxt),
+ ForeignItem(&'a ast::ForeignItem),
+}
+
+fn index_crate<'a>(
+ node_id_to_def_id: &FxHashMap<NodeId, LocalDefId>,
+ krate: &'a Crate,
+) -> IndexVec<LocalDefId, AstOwner<'a>> {
+ let mut indexer = Indexer { node_id_to_def_id, index: IndexVec::new() };
+ indexer.index.ensure_contains_elem(CRATE_DEF_ID, || AstOwner::NonOwner);
+ indexer.index[CRATE_DEF_ID] = AstOwner::Crate(krate);
+ visit::walk_crate(&mut indexer, krate);
+ return indexer.index;
+
+ struct Indexer<'s, 'a> {
+ node_id_to_def_id: &'s FxHashMap<NodeId, LocalDefId>,
+ index: IndexVec<LocalDefId, AstOwner<'a>>,
+ }
+
+ impl<'a> visit::Visitor<'a> for Indexer<'_, 'a> {
+ fn visit_attribute(&mut self, _: &'a Attribute) {
+ // We do not want to lower expressions that appear in attributes,
+ // as they are not accessible to the rest of the HIR.
+ }
+
+ fn visit_item(&mut self, item: &'a ast::Item) {
+ let def_id = self.node_id_to_def_id[&item.id];
+ self.index.ensure_contains_elem(def_id, || AstOwner::NonOwner);
+ self.index[def_id] = AstOwner::Item(item);
+ visit::walk_item(self, item)
+ }
+
+ fn visit_assoc_item(&mut self, item: &'a ast::AssocItem, ctxt: visit::AssocCtxt) {
+ let def_id = self.node_id_to_def_id[&item.id];
+ self.index.ensure_contains_elem(def_id, || AstOwner::NonOwner);
+ self.index[def_id] = AstOwner::AssocItem(item, ctxt);
+ visit::walk_assoc_item(self, item, ctxt);
+ }
+
+ fn visit_foreign_item(&mut self, item: &'a ast::ForeignItem) {
+ let def_id = self.node_id_to_def_id[&item.id];
+ self.index.ensure_contains_elem(def_id, || AstOwner::NonOwner);
+ self.index[def_id] = AstOwner::ForeignItem(item);
+ visit::walk_foreign_item(self, item);
+ }
+ }
+}
+
+/// Compute the hash for the HIR of the full crate.
+/// This hash will then be part of the crate_hash which is stored in the metadata.
+fn compute_hir_hash(
+ tcx: TyCtxt<'_>,
+ owners: &IndexVec<LocalDefId, hir::MaybeOwner<&hir::OwnerInfo<'_>>>,
+) -> Fingerprint {
+ let mut hir_body_nodes: Vec<_> = owners
+ .iter_enumerated()
+ .filter_map(|(def_id, info)| {
+ let info = info.as_owner()?;
+ let def_path_hash = tcx.hir().def_path_hash(def_id);
+ Some((def_path_hash, info))
+ })
+ .collect();
+ hir_body_nodes.sort_unstable_by_key(|bn| bn.0);
+
+ tcx.with_stable_hashing_context(|mut hcx| {
+ let mut stable_hasher = StableHasher::new();
+ hir_body_nodes.hash_stable(&mut hcx, &mut stable_hasher);
+ stable_hasher.finish()
+ })
+}
+
+pub fn lower_to_hir<'hir>(tcx: TyCtxt<'hir>, (): ()) -> hir::Crate<'hir> {
+ let sess = tcx.sess;
+ let krate = tcx.untracked_crate.steal();
+ let mut resolver = tcx.resolver_for_lowering(()).steal();
+
+ let ast_index = index_crate(&resolver.node_id_to_def_id, &krate);
+ let mut owners = IndexVec::from_fn_n(
+ |_| hir::MaybeOwner::Phantom,
+ tcx.definitions_untracked().def_index_count(),
+ );
+
+ for def_id in ast_index.indices() {
+ item::ItemLowerer {
+ tcx,
+ resolver: &mut resolver,
+ ast_index: &ast_index,
+ owners: &mut owners,
+ }
+ .lower_node(def_id);
+ }
+
+ // Drop AST to free memory
+ std::mem::drop(ast_index);
+ sess.time("drop_ast", || std::mem::drop(krate));
+
+ // Discard hygiene data, which isn't required after lowering to HIR.
+ if !sess.opts.unstable_opts.keep_hygiene_data {
+ rustc_span::hygiene::clear_syntax_context_map();
+ }
+
+ let hir_hash = compute_hir_hash(tcx, &owners);
+ hir::Crate { owners, hir_hash }
+}
+
+#[derive(Copy, Clone, PartialEq, Debug)]
+enum ParamMode {
+ /// Any path in a type context.
+ Explicit,
+ /// Path in a type definition, where the anonymous lifetime `'_` is not allowed.
+ ExplicitNamed,
+ /// The `module::Type` in `module::Type::method` in an expression.
+ Optional,
+}
+
+enum ParenthesizedGenericArgs {
+ Ok,
+ Err,
+}
+
+impl<'a, 'hir> LoweringContext<'a, 'hir> {
+ fn create_def(
+ &mut self,
+ parent: LocalDefId,
+ node_id: ast::NodeId,
+ data: DefPathData,
+ ) -> LocalDefId {
+ debug_assert_ne!(node_id, ast::DUMMY_NODE_ID);
+ assert!(
+ self.opt_local_def_id(node_id).is_none(),
+ "adding a def'n for node-id {:?} and data {:?} but a previous def'n exists: {:?}",
+ node_id,
+ data,
+ self.tcx.hir().def_key(self.local_def_id(node_id)),
+ );
+
+ let def_id = self.tcx.create_def(parent, data);
+
+ debug!("create_def: def_id_to_node_id[{:?}] <-> {:?}", def_id, node_id);
+ self.resolver.node_id_to_def_id.insert(node_id, def_id);
+
+ def_id
+ }
+
+ fn next_node_id(&mut self) -> NodeId {
+ let start = self.resolver.next_node_id;
+ let next = start.as_u32().checked_add(1).expect("input too large; ran out of NodeIds");
+ self.resolver.next_node_id = ast::NodeId::from_u32(next);
+ start
+ }
+
+ /// Given the id of some node in the AST, finds the `LocalDefId` associated with it by the name
+ /// resolver (if any), after applying any remapping from `get_remapped_def_id`.
+ ///
+ /// For example, in a function like `fn foo<'a>(x: &'a u32)`,
+ /// invoking with the id from the `ast::Lifetime` node found inside
+ /// the `&'a u32` type would return the `LocalDefId` of the
+ /// `'a` parameter declared on `foo`.
+ ///
+ /// This function also applies remapping from `get_remapped_def_id`.
+ /// These are used when synthesizing opaque types from `-> impl Trait` return types and so forth.
+ /// For example, in a function like `fn foo<'a>() -> impl Debug + 'a`,
+ /// we would create an opaque type `type FooReturn<'a1> = impl Debug + 'a1`.
+ /// When lowering the `Debug + 'a` bounds, we add a remapping to map `'a` to `'a1`.
+ fn opt_local_def_id(&self, node: NodeId) -> Option<LocalDefId> {
+ self.resolver
+ .node_id_to_def_id
+ .get(&node)
+ .map(|local_def_id| self.resolver.get_remapped_def_id(*local_def_id))
+ }
+
+ fn local_def_id(&self, node: NodeId) -> LocalDefId {
+ self.opt_local_def_id(node).unwrap_or_else(|| panic!("no entry for node id: `{:?}`", node))
+ }
+
+ /// Freshen the `LoweringContext` and ready it to lower a nested item.
+ /// The lowered item is registered into `self.children`.
+ ///
+ /// This function sets up `HirId` lowering infrastructure,
+ /// and stashes the shared mutable state to avoid pollution by the closure.
+ #[instrument(level = "debug", skip(self, f))]
+ fn with_hir_id_owner(
+ &mut self,
+ owner: NodeId,
+ f: impl FnOnce(&mut Self) -> hir::OwnerNode<'hir>,
+ ) {
+ let def_id = self.local_def_id(owner);
+
+ let current_attrs = std::mem::take(&mut self.attrs);
+ let current_bodies = std::mem::take(&mut self.bodies);
+ let current_node_ids = std::mem::take(&mut self.node_id_to_local_id);
+ let current_id_to_def_id = std::mem::take(&mut self.local_id_to_def_id);
+ let current_trait_map = std::mem::take(&mut self.trait_map);
+ let current_owner = std::mem::replace(&mut self.current_hir_id_owner, def_id);
+ let current_local_counter =
+ std::mem::replace(&mut self.item_local_id_counter, hir::ItemLocalId::new(1));
+ let current_impl_trait_defs = std::mem::take(&mut self.impl_trait_defs);
+ let current_impl_trait_bounds = std::mem::take(&mut self.impl_trait_bounds);
+
+ // Do not reset `next_node_id` and `node_id_to_def_id`:
+ // we want `f` to be able to refer to the `LocalDefId`s that the caller created.
+ // and the caller to refer to some of the subdefinitions' nodes' `LocalDefId`s.
+
+ // Always allocate the first `HirId` for the owner itself.
+ let _old = self.node_id_to_local_id.insert(owner, hir::ItemLocalId::new(0));
+ debug_assert_eq!(_old, None);
+
+ let item = f(self);
+ debug_assert_eq!(def_id, item.def_id());
+ // `f` should have consumed all the elements in these vectors when constructing `item`.
+ debug_assert!(self.impl_trait_defs.is_empty());
+ debug_assert!(self.impl_trait_bounds.is_empty());
+ let info = self.make_owner_info(item);
+
+ self.attrs = current_attrs;
+ self.bodies = current_bodies;
+ self.node_id_to_local_id = current_node_ids;
+ self.local_id_to_def_id = current_id_to_def_id;
+ self.trait_map = current_trait_map;
+ self.current_hir_id_owner = current_owner;
+ self.item_local_id_counter = current_local_counter;
+ self.impl_trait_defs = current_impl_trait_defs;
+ self.impl_trait_bounds = current_impl_trait_bounds;
+
+ let _old = self.children.insert(def_id, hir::MaybeOwner::Owner(info));
+ debug_assert!(_old.is_none())
+ }
+
+ /// Installs the remapping `remap` in scope while `f` is being executed.
+ /// This causes references to the `LocalDefId` keys to be changed to
+ /// refer to the values instead.
+ ///
+ /// The remapping is used when one piece of AST expands to multiple
+ /// pieces of HIR. For example, the function `fn foo<'a>(...) -> impl Debug + 'a`,
+ /// expands to both a function definition (`foo`) and a TAIT for the return value,
+ /// both of which have a lifetime parameter `'a`. The remapping allows us to
+ /// rewrite the `'a` in the return value to refer to the
+ /// `'a` declared on the TAIT, instead of the function.
+ fn with_remapping<R>(
+ &mut self,
+ remap: FxHashMap<LocalDefId, LocalDefId>,
+ f: impl FnOnce(&mut Self) -> R,
+ ) -> R {
+ self.resolver.generics_def_id_map.push(remap);
+ let res = f(self);
+ self.resolver.generics_def_id_map.pop();
+ res
+ }
+
+ fn make_owner_info(&mut self, node: hir::OwnerNode<'hir>) -> &'hir hir::OwnerInfo<'hir> {
+ let attrs = std::mem::take(&mut self.attrs);
+ let mut bodies = std::mem::take(&mut self.bodies);
+ let local_id_to_def_id = std::mem::take(&mut self.local_id_to_def_id);
+ let trait_map = std::mem::take(&mut self.trait_map);
+
+ #[cfg(debug_assertions)]
+ for (id, attrs) in attrs.iter() {
+ // Verify that we do not store empty slices in the map.
+ if attrs.is_empty() {
+ panic!("Stored empty attributes for {:?}", id);
+ }
+ }
+
+ bodies.sort_by_key(|(k, _)| *k);
+ let bodies = SortedMap::from_presorted_elements(bodies);
+ let (hash_including_bodies, hash_without_bodies) = self.hash_owner(node, &bodies);
+ let (nodes, parenting) =
+ index::index_hir(self.tcx.sess, &*self.tcx.definitions_untracked(), node, &bodies);
+ let nodes = hir::OwnerNodes {
+ hash_including_bodies,
+ hash_without_bodies,
+ nodes,
+ bodies,
+ local_id_to_def_id,
+ };
+ let attrs = {
+ let hash = self.tcx.with_stable_hashing_context(|mut hcx| {
+ let mut stable_hasher = StableHasher::new();
+ attrs.hash_stable(&mut hcx, &mut stable_hasher);
+ stable_hasher.finish()
+ });
+ hir::AttributeMap { map: attrs, hash }
+ };
+
+ self.arena.alloc(hir::OwnerInfo { nodes, parenting, attrs, trait_map })
+ }
+
+ /// Hash the HIR node twice, one deep and one shallow hash. This allows to differentiate
+ /// queries which depend on the full HIR tree and those which only depend on the item signature.
+ fn hash_owner(
+ &mut self,
+ node: hir::OwnerNode<'hir>,
+ bodies: &SortedMap<hir::ItemLocalId, &'hir hir::Body<'hir>>,
+ ) -> (Fingerprint, Fingerprint) {
+ self.tcx.with_stable_hashing_context(|mut hcx| {
+ let mut stable_hasher = StableHasher::new();
+ hcx.with_hir_bodies(true, node.def_id(), bodies, |hcx| {
+ node.hash_stable(hcx, &mut stable_hasher)
+ });
+ let hash_including_bodies = stable_hasher.finish();
+ let mut stable_hasher = StableHasher::new();
+ hcx.with_hir_bodies(false, node.def_id(), bodies, |hcx| {
+ node.hash_stable(hcx, &mut stable_hasher)
+ });
+ let hash_without_bodies = stable_hasher.finish();
+ (hash_including_bodies, hash_without_bodies)
+ })
+ }
+
+ /// This method allocates a new `HirId` for the given `NodeId` and stores it in
+ /// the `LoweringContext`'s `NodeId => HirId` map.
+ /// Take care not to call this method if the resulting `HirId` is then not
+ /// actually used in the HIR, as that would trigger an assertion in the
+ /// `HirIdValidator` later on, which makes sure that all `NodeId`s got mapped
+ /// properly. Calling the method twice with the same `NodeId` is fine though.
+ fn lower_node_id(&mut self, ast_node_id: NodeId) -> hir::HirId {
+ assert_ne!(ast_node_id, DUMMY_NODE_ID);
+
+ match self.node_id_to_local_id.entry(ast_node_id) {
+ Entry::Occupied(o) => {
+ hir::HirId { owner: self.current_hir_id_owner, local_id: *o.get() }
+ }
+ Entry::Vacant(v) => {
+ // Generate a new `HirId`.
+ let owner = self.current_hir_id_owner;
+ let local_id = self.item_local_id_counter;
+ let hir_id = hir::HirId { owner, local_id };
+
+ v.insert(local_id);
+ self.item_local_id_counter.increment_by(1);
+
+ assert_ne!(local_id, hir::ItemLocalId::new(0));
+ if let Some(def_id) = self.opt_local_def_id(ast_node_id) {
+ // Do not override a `MaybeOwner::Owner` that may already here.
+ self.children.entry(def_id).or_insert(hir::MaybeOwner::NonOwner(hir_id));
+ self.local_id_to_def_id.insert(local_id, def_id);
+ }
+
+ if let Some(traits) = self.resolver.trait_map.remove(&ast_node_id) {
+ self.trait_map.insert(hir_id.local_id, traits.into_boxed_slice());
+ }
+
+ hir_id
+ }
+ }
+ }
+
+ /// Generate a new `HirId` without a backing `NodeId`.
+ fn next_id(&mut self) -> hir::HirId {
+ let owner = self.current_hir_id_owner;
+ let local_id = self.item_local_id_counter;
+ assert_ne!(local_id, hir::ItemLocalId::new(0));
+ self.item_local_id_counter.increment_by(1);
+ hir::HirId { owner, local_id }
+ }
+
+ #[instrument(level = "trace", skip(self))]
+ fn lower_res(&mut self, res: Res<NodeId>) -> Res {
+ let res: Result<Res, ()> = res.apply_id(|id| {
+ let owner = self.current_hir_id_owner;
+ let local_id = self.node_id_to_local_id.get(&id).copied().ok_or(())?;
+ Ok(hir::HirId { owner, local_id })
+ });
+ trace!(?res);
+
+ // We may fail to find a HirId when the Res points to a Local from an enclosing HIR owner.
+ // This can happen when trying to lower the return type `x` in erroneous code like
+ // async fn foo(x: u8) -> x {}
+ // In that case, `x` is lowered as a function parameter, and the return type is lowered as
+ // an opaque type as a synthesized HIR owner.
+ res.unwrap_or(Res::Err)
+ }
+
+ fn expect_full_res(&mut self, id: NodeId) -> Res<NodeId> {
+ self.resolver.get_partial_res(id).map_or(Res::Err, |pr| {
+ if pr.unresolved_segments() != 0 {
+ panic!("path not fully resolved: {:?}", pr);
+ }
+ pr.base_res()
+ })
+ }
+
+ fn expect_full_res_from_use(&mut self, id: NodeId) -> impl Iterator<Item = Res<NodeId>> {
+ self.resolver.get_import_res(id).present_items()
+ }
+
+ fn diagnostic(&self) -> &Handler {
+ self.tcx.sess.diagnostic()
+ }
+
+ /// Reuses the span but adds information like the kind of the desugaring and features that are
+ /// allowed inside this span.
+ fn mark_span_with_reason(
+ &self,
+ reason: DesugaringKind,
+ span: Span,
+ allow_internal_unstable: Option<Lrc<[Symbol]>>,
+ ) -> Span {
+ self.tcx.with_stable_hashing_context(|hcx| {
+ span.mark_with_reason(allow_internal_unstable, reason, self.tcx.sess.edition(), hcx)
+ })
+ }
+
+ /// Intercept all spans entering HIR.
+ /// Mark a span as relative to the current owning item.
+ fn lower_span(&self, span: Span) -> Span {
+ if self.tcx.sess.opts.unstable_opts.incremental_relative_spans {
+ span.with_parent(Some(self.current_hir_id_owner))
+ } else {
+ // Do not make spans relative when not using incremental compilation.
+ span
+ }
+ }
+
+ fn lower_ident(&self, ident: Ident) -> Ident {
+ Ident::new(ident.name, self.lower_span(ident.span))
+ }
+
+ /// Converts a lifetime into a new generic parameter.
+ #[tracing::instrument(level = "debug", skip(self))]
+ fn lifetime_res_to_generic_param(
+ &mut self,
+ ident: Ident,
+ node_id: NodeId,
+ res: LifetimeRes,
+ ) -> Option<hir::GenericParam<'hir>> {
+ let (name, kind) = match res {
+ LifetimeRes::Param { .. } => {
+ (hir::ParamName::Plain(ident), hir::LifetimeParamKind::Explicit)
+ }
+ LifetimeRes::Fresh { param, .. } => {
+ // Late resolution delegates to us the creation of the `LocalDefId`.
+ let _def_id = self.create_def(
+ self.current_hir_id_owner,
+ param,
+ DefPathData::LifetimeNs(kw::UnderscoreLifetime),
+ );
+ debug!(?_def_id);
+
+ (hir::ParamName::Fresh, hir::LifetimeParamKind::Elided)
+ }
+ LifetimeRes::Static | LifetimeRes::Error => return None,
+ res => panic!(
+ "Unexpected lifetime resolution {:?} for {:?} at {:?}",
+ res, ident, ident.span
+ ),
+ };
+ let hir_id = self.lower_node_id(node_id);
+ Some(hir::GenericParam {
+ hir_id,
+ name,
+ span: self.lower_span(ident.span),
+ pure_wrt_drop: false,
+ kind: hir::GenericParamKind::Lifetime { kind },
+ colon_span: None,
+ })
+ }
+
+ /// Lowers a lifetime binder that defines `generic_params`, returning the corresponding HIR
+ /// nodes. The returned list includes any "extra" lifetime parameters that were added by the
+ /// name resolver owing to lifetime elision; this also populates the resolver's node-id->def-id
+ /// map, so that later calls to `opt_node_id_to_def_id` that refer to these extra lifetime
+ /// parameters will be successful.
+ #[tracing::instrument(level = "debug", skip(self))]
+ #[inline]
+ fn lower_lifetime_binder(
+ &mut self,
+ binder: NodeId,
+ generic_params: &[GenericParam],
+ ) -> &'hir [hir::GenericParam<'hir>] {
+ let mut generic_params: Vec<_> = self.lower_generic_params_mut(generic_params).collect();
+ let extra_lifetimes = self.resolver.take_extra_lifetime_params(binder);
+ debug!(?extra_lifetimes);
+ generic_params.extend(extra_lifetimes.into_iter().filter_map(|(ident, node_id, res)| {
+ self.lifetime_res_to_generic_param(ident, node_id, res)
+ }));
+ let generic_params = self.arena.alloc_from_iter(generic_params);
+ debug!(?generic_params);
+
+ generic_params
+ }
+
+ fn with_dyn_type_scope<T>(&mut self, in_scope: bool, f: impl FnOnce(&mut Self) -> T) -> T {
+ let was_in_dyn_type = self.is_in_dyn_type;
+ self.is_in_dyn_type = in_scope;
+
+ let result = f(self);
+
+ self.is_in_dyn_type = was_in_dyn_type;
+
+ result
+ }
+
+ fn with_new_scopes<T>(&mut self, f: impl FnOnce(&mut Self) -> T) -> T {
+ let was_in_loop_condition = self.is_in_loop_condition;
+ self.is_in_loop_condition = false;
+
+ let catch_scope = self.catch_scope.take();
+ let loop_scope = self.loop_scope.take();
+ let ret = f(self);
+ self.catch_scope = catch_scope;
+ self.loop_scope = loop_scope;
+
+ self.is_in_loop_condition = was_in_loop_condition;
+
+ ret
+ }
+
+ fn lower_attrs(&mut self, id: hir::HirId, attrs: &[Attribute]) -> Option<&'hir [Attribute]> {
+ if attrs.is_empty() {
+ None
+ } else {
+ debug_assert_eq!(id.owner, self.current_hir_id_owner);
+ let ret = self.arena.alloc_from_iter(attrs.iter().map(|a| self.lower_attr(a)));
+ debug_assert!(!ret.is_empty());
+ self.attrs.insert(id.local_id, ret);
+ Some(ret)
+ }
+ }
+
+ fn lower_attr(&self, attr: &Attribute) -> Attribute {
+ // Note that we explicitly do not walk the path. Since we don't really
+ // lower attributes (we use the AST version) there is nowhere to keep
+ // the `HirId`s. We don't actually need HIR version of attributes anyway.
+ // Tokens are also not needed after macro expansion and parsing.
+ let kind = match attr.kind {
+ AttrKind::Normal(ref item, _) => AttrKind::Normal(
+ AttrItem {
+ path: item.path.clone(),
+ args: self.lower_mac_args(&item.args),
+ tokens: None,
+ },
+ None,
+ ),
+ AttrKind::DocComment(comment_kind, data) => AttrKind::DocComment(comment_kind, data),
+ };
+
+ Attribute { kind, id: attr.id, style: attr.style, span: self.lower_span(attr.span) }
+ }
+
+ fn alias_attrs(&mut self, id: hir::HirId, target_id: hir::HirId) {
+ debug_assert_eq!(id.owner, self.current_hir_id_owner);
+ debug_assert_eq!(target_id.owner, self.current_hir_id_owner);
+ if let Some(&a) = self.attrs.get(&target_id.local_id) {
+ debug_assert!(!a.is_empty());
+ self.attrs.insert(id.local_id, a);
+ }
+ }
+
+ fn lower_mac_args(&self, args: &MacArgs) -> MacArgs {
+ match *args {
+ MacArgs::Empty => MacArgs::Empty,
+ MacArgs::Delimited(dspan, delim, ref tokens) => {
+ // This is either a non-key-value attribute, or a `macro_rules!` body.
+ // We either not have any nonterminals present (in the case of an attribute),
+ // or have tokens available for all nonterminals in the case of a nested
+ // `macro_rules`: e.g:
+ //
+ // ```rust
+ // macro_rules! outer {
+ // ($e:expr) => {
+ // macro_rules! inner {
+ // () => { $e }
+ // }
+ // }
+ // }
+ // ```
+ //
+ // In both cases, we don't want to synthesize any tokens
+ MacArgs::Delimited(dspan, delim, tokens.flattened())
+ }
+ // This is an inert key-value attribute - it will never be visible to macros
+ // after it gets lowered to HIR. Therefore, we can extract literals to handle
+ // nonterminals in `#[doc]` (e.g. `#[doc = $e]`).
+ MacArgs::Eq(eq_span, MacArgsEq::Ast(ref expr)) => {
+ // In valid code the value always ends up as a single literal. Otherwise, a dummy
+ // literal suffices because the error is handled elsewhere.
+ let lit = if let ExprKind::Lit(lit) = &expr.kind {
+ lit.clone()
+ } else {
+ Lit {
+ token: token::Lit::new(token::LitKind::Err, kw::Empty, None),
+ kind: LitKind::Err(kw::Empty),
+ span: DUMMY_SP,
+ }
+ };
+ MacArgs::Eq(eq_span, MacArgsEq::Hir(lit))
+ }
+ MacArgs::Eq(_, MacArgsEq::Hir(ref lit)) => {
+ unreachable!("in literal form when lowering mac args eq: {:?}", lit)
+ }
+ }
+ }
+
+ /// Given an associated type constraint like one of these:
+ ///
+ /// ```ignore (illustrative)
+ /// T: Iterator<Item: Debug>
+ /// ^^^^^^^^^^^
+ /// T: Iterator<Item = Debug>
+ /// ^^^^^^^^^^^^
+ /// ```
+ ///
+ /// returns a `hir::TypeBinding` representing `Item`.
+ #[instrument(level = "debug", skip(self))]
+ fn lower_assoc_ty_constraint(
+ &mut self,
+ constraint: &AssocConstraint,
+ itctx: ImplTraitContext,
+ ) -> hir::TypeBinding<'hir> {
+ debug!("lower_assoc_ty_constraint(constraint={:?}, itctx={:?})", constraint, itctx);
+ // lower generic arguments of identifier in constraint
+ let gen_args = if let Some(ref gen_args) = constraint.gen_args {
+ let gen_args_ctor = match gen_args {
+ GenericArgs::AngleBracketed(ref data) => {
+ self.lower_angle_bracketed_parameter_data(data, ParamMode::Explicit, itctx).0
+ }
+ GenericArgs::Parenthesized(ref data) => {
+ self.emit_bad_parenthesized_trait_in_assoc_ty(data);
+ self.lower_angle_bracketed_parameter_data(
+ &data.as_angle_bracketed_args(),
+ ParamMode::Explicit,
+ itctx,
+ )
+ .0
+ }
+ };
+ gen_args_ctor.into_generic_args(self)
+ } else {
+ self.arena.alloc(hir::GenericArgs::none())
+ };
+
+ let kind = match constraint.kind {
+ AssocConstraintKind::Equality { ref term } => {
+ let term = match term {
+ Term::Ty(ref ty) => self.lower_ty(ty, itctx).into(),
+ Term::Const(ref c) => self.lower_anon_const(c).into(),
+ };
+ hir::TypeBindingKind::Equality { term }
+ }
+ AssocConstraintKind::Bound { ref bounds } => {
+ // Piggy-back on the `impl Trait` context to figure out the correct behavior.
+ let (desugar_to_impl_trait, itctx) = match itctx {
+ // We are in the return position:
+ //
+ // fn foo() -> impl Iterator<Item: Debug>
+ //
+ // so desugar to
+ //
+ // fn foo() -> impl Iterator<Item = impl Debug>
+ ImplTraitContext::ReturnPositionOpaqueTy { .. }
+ | ImplTraitContext::TypeAliasesOpaqueTy { .. } => (true, itctx),
+
+ // We are in the argument position, but within a dyn type:
+ //
+ // fn foo(x: dyn Iterator<Item: Debug>)
+ //
+ // so desugar to
+ //
+ // fn foo(x: dyn Iterator<Item = impl Debug>)
+ ImplTraitContext::Universal if self.is_in_dyn_type => (true, itctx),
+
+ // In `type Foo = dyn Iterator<Item: Debug>` we desugar to
+ // `type Foo = dyn Iterator<Item = impl Debug>` but we have to override the
+ // "impl trait context" to permit `impl Debug` in this position (it desugars
+ // then to an opaque type).
+ //
+ // FIXME: this is only needed until `impl Trait` is allowed in type aliases.
+ ImplTraitContext::Disallowed(_) if self.is_in_dyn_type => {
+ (true, ImplTraitContext::TypeAliasesOpaqueTy)
+ }
+
+ // We are in the parameter position, but not within a dyn type:
+ //
+ // fn foo(x: impl Iterator<Item: Debug>)
+ //
+ // so we leave it as is and this gets expanded in astconv to a bound like
+ // `<T as Iterator>::Item: Debug` where `T` is the type parameter for the
+ // `impl Iterator`.
+ _ => (false, itctx),
+ };
+
+ if desugar_to_impl_trait {
+ // Desugar `AssocTy: Bounds` into `AssocTy = impl Bounds`. We do this by
+ // constructing the HIR for `impl bounds...` and then lowering that.
+
+ let parent_def_id = self.current_hir_id_owner;
+ let impl_trait_node_id = self.next_node_id();
+ self.create_def(parent_def_id, impl_trait_node_id, DefPathData::ImplTrait);
+
+ self.with_dyn_type_scope(false, |this| {
+ let node_id = this.next_node_id();
+ let ty = this.lower_ty(
+ &Ty {
+ id: node_id,
+ kind: TyKind::ImplTrait(impl_trait_node_id, bounds.clone()),
+ span: this.lower_span(constraint.span),
+ tokens: None,
+ },
+ itctx,
+ );
+
+ hir::TypeBindingKind::Equality { term: ty.into() }
+ })
+ } else {
+ // Desugar `AssocTy: Bounds` into a type binding where the
+ // later desugars into a trait predicate.
+ let bounds = self.lower_param_bounds(bounds, itctx);
+
+ hir::TypeBindingKind::Constraint { bounds }
+ }
+ }
+ };
+
+ hir::TypeBinding {
+ hir_id: self.lower_node_id(constraint.id),
+ ident: self.lower_ident(constraint.ident),
+ gen_args,
+ kind,
+ span: self.lower_span(constraint.span),
+ }
+ }
+
+ fn emit_bad_parenthesized_trait_in_assoc_ty(&self, data: &ParenthesizedArgs) {
+ let mut err = self.tcx.sess.struct_span_err(
+ data.span,
+ "parenthesized generic arguments cannot be used in associated type constraints",
+ );
+ // Suggest removing empty parentheses: "Trait()" -> "Trait"
+ if data.inputs.is_empty() {
+ let parentheses_span =
+ data.inputs_span.shrink_to_lo().to(data.inputs_span.shrink_to_hi());
+ err.multipart_suggestion(
+ "remove these parentheses",
+ vec![(parentheses_span, String::new())],
+ Applicability::MaybeIncorrect,
+ );
+ }
+ // Suggest replacing parentheses with angle brackets `Trait(params...)` to `Trait<params...>`
+ else {
+ // Start of parameters to the 1st argument
+ let open_param = data.inputs_span.shrink_to_lo().to(data
+ .inputs
+ .first()
+ .unwrap()
+ .span
+ .shrink_to_lo());
+ // End of last argument to end of parameters
+ let close_param =
+ data.inputs.last().unwrap().span.shrink_to_hi().to(data.inputs_span.shrink_to_hi());
+ err.multipart_suggestion(
+ &format!("use angle brackets instead",),
+ vec![(open_param, String::from("<")), (close_param, String::from(">"))],
+ Applicability::MaybeIncorrect,
+ );
+ }
+ err.emit();
+ }
+
+ #[instrument(level = "debug", skip(self))]
+ fn lower_generic_arg(
+ &mut self,
+ arg: &ast::GenericArg,
+ itctx: ImplTraitContext,
+ ) -> hir::GenericArg<'hir> {
+ match arg {
+ ast::GenericArg::Lifetime(lt) => GenericArg::Lifetime(self.lower_lifetime(&lt)),
+ ast::GenericArg::Type(ty) => {
+ match ty.kind {
+ TyKind::Infer if self.tcx.features().generic_arg_infer => {
+ return GenericArg::Infer(hir::InferArg {
+ hir_id: self.lower_node_id(ty.id),
+ span: self.lower_span(ty.span),
+ });
+ }
+ // We parse const arguments as path types as we cannot distinguish them during
+ // parsing. We try to resolve that ambiguity by attempting resolution in both the
+ // type and value namespaces. If we resolved the path in the value namespace, we
+ // transform it into a generic const argument.
+ TyKind::Path(ref qself, ref path) => {
+ if let Some(partial_res) = self.resolver.get_partial_res(ty.id) {
+ let res = partial_res.base_res();
+ if !res.matches_ns(Namespace::TypeNS) {
+ debug!(
+ "lower_generic_arg: Lowering type argument as const argument: {:?}",
+ ty,
+ );
+
+ // Construct an AnonConst where the expr is the "ty"'s path.
+
+ let parent_def_id = self.current_hir_id_owner;
+ let node_id = self.next_node_id();
+
+ // Add a definition for the in-band const def.
+ self.create_def(parent_def_id, node_id, DefPathData::AnonConst);
+
+ let span = self.lower_span(ty.span);
+ let path_expr = Expr {
+ id: ty.id,
+ kind: ExprKind::Path(qself.clone(), path.clone()),
+ span,
+ attrs: AttrVec::new(),
+ tokens: None,
+ };
+
+ let ct = self.with_new_scopes(|this| hir::AnonConst {
+ hir_id: this.lower_node_id(node_id),
+ body: this.lower_const_body(path_expr.span, Some(&path_expr)),
+ });
+ return GenericArg::Const(ConstArg { value: ct, span });
+ }
+ }
+ }
+ _ => {}
+ }
+ GenericArg::Type(self.lower_ty_direct(&ty, itctx))
+ }
+ ast::GenericArg::Const(ct) => GenericArg::Const(ConstArg {
+ value: self.lower_anon_const(&ct),
+ span: self.lower_span(ct.value.span),
+ }),
+ }
+ }
+
+ #[instrument(level = "debug", skip(self))]
+ fn lower_ty(&mut self, t: &Ty, itctx: ImplTraitContext) -> &'hir hir::Ty<'hir> {
+ self.arena.alloc(self.lower_ty_direct(t, itctx))
+ }
+
+ fn lower_path_ty(
+ &mut self,
+ t: &Ty,
+ qself: &Option<QSelf>,
+ path: &Path,
+ param_mode: ParamMode,
+ itctx: ImplTraitContext,
+ ) -> hir::Ty<'hir> {
+ // Check whether we should interpret this as a bare trait object.
+ // This check mirrors the one in late resolution. We only introduce this special case in
+ // the rare occurence we need to lower `Fresh` anonymous lifetimes.
+ // The other cases when a qpath should be opportunistically made a trait object are handled
+ // by `ty_path`.
+ if qself.is_none()
+ && let Some(partial_res) = self.resolver.get_partial_res(t.id)
+ && partial_res.unresolved_segments() == 0
+ && let Res::Def(DefKind::Trait | DefKind::TraitAlias, _) = partial_res.base_res()
+ {
+ let (bounds, lifetime_bound) = self.with_dyn_type_scope(true, |this| {
+ let bound = this.lower_poly_trait_ref(
+ &PolyTraitRef {
+ bound_generic_params: vec![],
+ trait_ref: TraitRef { path: path.clone(), ref_id: t.id },
+ span: t.span
+ },
+ itctx,
+ );
+ let bounds = this.arena.alloc_from_iter([bound]);
+ let lifetime_bound = this.elided_dyn_bound(t.span);
+ (bounds, lifetime_bound)
+ });
+ let kind = hir::TyKind::TraitObject(bounds, lifetime_bound, TraitObjectSyntax::None);
+ return hir::Ty { kind, span: self.lower_span(t.span), hir_id: self.next_id() };
+ }
+
+ let id = self.lower_node_id(t.id);
+ let qpath = self.lower_qpath(t.id, qself, path, param_mode, itctx);
+ self.ty_path(id, t.span, qpath)
+ }
+
+ fn ty(&mut self, span: Span, kind: hir::TyKind<'hir>) -> hir::Ty<'hir> {
+ hir::Ty { hir_id: self.next_id(), kind, span: self.lower_span(span) }
+ }
+
+ fn ty_tup(&mut self, span: Span, tys: &'hir [hir::Ty<'hir>]) -> hir::Ty<'hir> {
+ self.ty(span, hir::TyKind::Tup(tys))
+ }
+
+ fn lower_ty_direct(&mut self, t: &Ty, itctx: ImplTraitContext) -> hir::Ty<'hir> {
+ let kind = match t.kind {
+ TyKind::Infer => hir::TyKind::Infer,
+ TyKind::Err => hir::TyKind::Err,
+ TyKind::Slice(ref ty) => hir::TyKind::Slice(self.lower_ty(ty, itctx)),
+ TyKind::Ptr(ref mt) => hir::TyKind::Ptr(self.lower_mt(mt, itctx)),
+ TyKind::Rptr(ref region, ref mt) => {
+ let region = region.unwrap_or_else(|| {
+ let id = if let Some(LifetimeRes::ElidedAnchor { start, end }) =
+ self.resolver.get_lifetime_res(t.id)
+ {
+ debug_assert_eq!(start.plus(1), end);
+ start
+ } else {
+ self.next_node_id()
+ };
+ let span = self.tcx.sess.source_map().next_point(t.span.shrink_to_lo());
+ Lifetime { ident: Ident::new(kw::UnderscoreLifetime, span), id }
+ });
+ let lifetime = self.lower_lifetime(&region);
+ hir::TyKind::Rptr(lifetime, self.lower_mt(mt, itctx))
+ }
+ TyKind::BareFn(ref f) => {
+ let generic_params = self.lower_lifetime_binder(t.id, &f.generic_params);
+ hir::TyKind::BareFn(self.arena.alloc(hir::BareFnTy {
+ generic_params,
+ unsafety: self.lower_unsafety(f.unsafety),
+ abi: self.lower_extern(f.ext),
+ decl: self.lower_fn_decl(&f.decl, None, FnDeclKind::Pointer, None),
+ param_names: self.lower_fn_params_to_names(&f.decl),
+ }))
+ }
+ TyKind::Never => hir::TyKind::Never,
+ TyKind::Tup(ref tys) => hir::TyKind::Tup(
+ self.arena.alloc_from_iter(tys.iter().map(|ty| self.lower_ty_direct(ty, itctx))),
+ ),
+ TyKind::Paren(ref ty) => {
+ return self.lower_ty_direct(ty, itctx);
+ }
+ TyKind::Path(ref qself, ref path) => {
+ return self.lower_path_ty(t, qself, path, ParamMode::Explicit, itctx);
+ }
+ TyKind::ImplicitSelf => {
+ let res = self.expect_full_res(t.id);
+ let res = self.lower_res(res);
+ hir::TyKind::Path(hir::QPath::Resolved(
+ None,
+ self.arena.alloc(hir::Path {
+ res,
+ segments: arena_vec![self; hir::PathSegment::from_ident(
+ Ident::with_dummy_span(kw::SelfUpper)
+ )],
+ span: self.lower_span(t.span),
+ }),
+ ))
+ }
+ TyKind::Array(ref ty, ref length) => {
+ hir::TyKind::Array(self.lower_ty(ty, itctx), self.lower_array_length(length))
+ }
+ TyKind::Typeof(ref expr) => hir::TyKind::Typeof(self.lower_anon_const(expr)),
+ TyKind::TraitObject(ref bounds, kind) => {
+ let mut lifetime_bound = None;
+ let (bounds, lifetime_bound) = self.with_dyn_type_scope(true, |this| {
+ let bounds =
+ this.arena.alloc_from_iter(bounds.iter().filter_map(
+ |bound| match *bound {
+ GenericBound::Trait(
+ ref ty,
+ TraitBoundModifier::None | TraitBoundModifier::MaybeConst,
+ ) => Some(this.lower_poly_trait_ref(ty, itctx)),
+ // `~const ?Bound` will cause an error during AST validation
+ // anyways, so treat it like `?Bound` as compilation proceeds.
+ GenericBound::Trait(
+ _,
+ TraitBoundModifier::Maybe | TraitBoundModifier::MaybeConstMaybe,
+ ) => None,
+ GenericBound::Outlives(ref lifetime) => {
+ if lifetime_bound.is_none() {
+ lifetime_bound = Some(this.lower_lifetime(lifetime));
+ }
+ None
+ }
+ },
+ ));
+ let lifetime_bound =
+ lifetime_bound.unwrap_or_else(|| this.elided_dyn_bound(t.span));
+ (bounds, lifetime_bound)
+ });
+ hir::TyKind::TraitObject(bounds, lifetime_bound, kind)
+ }
+ TyKind::ImplTrait(def_node_id, ref bounds) => {
+ let span = t.span;
+ match itctx {
+ ImplTraitContext::ReturnPositionOpaqueTy { origin } => {
+ self.lower_opaque_impl_trait(span, origin, def_node_id, bounds, itctx)
+ }
+ ImplTraitContext::TypeAliasesOpaqueTy => {
+ let nested_itctx = ImplTraitContext::TypeAliasesOpaqueTy;
+ self.lower_opaque_impl_trait(
+ span,
+ hir::OpaqueTyOrigin::TyAlias,
+ def_node_id,
+ bounds,
+ nested_itctx,
+ )
+ }
+ ImplTraitContext::Universal => {
+ let span = t.span;
+ let ident = Ident::from_str_and_span(&pprust::ty_to_string(t), span);
+ let (param, bounds, path) =
+ self.lower_generic_and_bounds(def_node_id, span, ident, bounds);
+ self.impl_trait_defs.push(param);
+ if let Some(bounds) = bounds {
+ self.impl_trait_bounds.push(bounds);
+ }
+ path
+ }
+ ImplTraitContext::Disallowed(position) => {
+ let mut err = struct_span_err!(
+ self.tcx.sess,
+ t.span,
+ E0562,
+ "`impl Trait` only allowed in function and inherent method return types, not in {}",
+ position
+ );
+ err.emit();
+ hir::TyKind::Err
+ }
+ }
+ }
+ TyKind::MacCall(_) => panic!("`TyKind::MacCall` should have been expanded by now"),
+ TyKind::CVarArgs => {
+ self.tcx.sess.delay_span_bug(
+ t.span,
+ "`TyKind::CVarArgs` should have been handled elsewhere",
+ );
+ hir::TyKind::Err
+ }
+ };
+
+ hir::Ty { kind, span: self.lower_span(t.span), hir_id: self.lower_node_id(t.id) }
+ }
+
+ /// Lowers a `ReturnPositionOpaqueTy` (`-> impl Trait`) or a `TypeAliasesOpaqueTy` (`type F =
+ /// impl Trait`): this creates the associated Opaque Type (TAIT) definition and then returns a
+ /// HIR type that references the TAIT.
+ ///
+ /// Given a function definition like:
+ ///
+ /// ```rust
+ /// fn test<'a, T: Debug>(x: &'a T) -> impl Debug + 'a {
+ /// x
+ /// }
+ /// ```
+ ///
+ /// we will create a TAIT definition in the HIR like
+ ///
+ /// ```
+ /// type TestReturn<'a, T, 'x> = impl Debug + 'x
+ /// ```
+ ///
+ /// and return a type like `TestReturn<'static, T, 'a>`, so that the function looks like:
+ ///
+ /// ```rust
+ /// fn test<'a, T: Debug>(x: &'a T) -> TestReturn<'static, T, 'a>
+ /// ```
+ ///
+ /// Note the subtlety around type parameters! The new TAIT, `TestReturn`, inherits all the
+ /// type parameters from the function `test` (this is implemented in the query layer, they aren't
+ /// added explicitly in the HIR). But this includes all the lifetimes, and we only want to
+ /// capture the lifetimes that are referenced in the bounds. Therefore, we add *extra* lifetime parameters
+ /// for the lifetimes that get captured (`'x`, in our example above) and reference those.
+ #[tracing::instrument(level = "debug", skip(self))]
+ fn lower_opaque_impl_trait(
+ &mut self,
+ span: Span,
+ origin: hir::OpaqueTyOrigin,
+ opaque_ty_node_id: NodeId,
+ bounds: &GenericBounds,
+ itctx: ImplTraitContext,
+ ) -> hir::TyKind<'hir> {
+ // Make sure we know that some funky desugaring has been going on here.
+ // This is a first: there is code in other places like for loop
+ // desugaring that explicitly states that we don't want to track that.
+ // Not tracking it makes lints in rustc and clippy very fragile, as
+ // frequently opened issues show.
+ let opaque_ty_span = self.mark_span_with_reason(DesugaringKind::OpaqueTy, span, None);
+
+ let opaque_ty_def_id = self.local_def_id(opaque_ty_node_id);
+ debug!(?opaque_ty_def_id);
+
+ // Contains the new lifetime definitions created for the TAIT (if any).
+ let mut collected_lifetimes = Vec::new();
+
+ // If this came from a TAIT (as opposed to a function that returns an RPIT), we only want
+ // to capture the lifetimes that appear in the bounds. So visit the bounds to find out
+ // exactly which ones those are.
+ let lifetimes_to_remap = if origin == hir::OpaqueTyOrigin::TyAlias {
+ // in a TAIT like `type Foo<'a> = impl Foo<'a>`, we don't keep all the lifetime parameters
+ Vec::new()
+ } else {
+ // in fn return position, like the `fn test<'a>() -> impl Debug + 'a` example,
+ // we only keep the lifetimes that appear in the `impl Debug` itself:
+ lifetime_collector::lifetimes_in_bounds(&self.resolver, bounds)
+ };
+ debug!(?lifetimes_to_remap);
+
+ self.with_hir_id_owner(opaque_ty_node_id, |lctx| {
+ let mut new_remapping = FxHashMap::default();
+
+ // If this opaque type is only capturing a subset of the lifetimes (those that appear
+ // in bounds), then create the new lifetime parameters required and create a mapping
+ // from the old `'a` (on the function) to the new `'a` (on the opaque type).
+ collected_lifetimes = lctx.create_lifetime_defs(
+ opaque_ty_def_id,
+ &lifetimes_to_remap,
+ &mut new_remapping,
+ );
+ debug!(?collected_lifetimes);
+ debug!(?new_remapping);
+
+ // Install the remapping from old to new (if any):
+ lctx.with_remapping(new_remapping, |lctx| {
+ // This creates HIR lifetime definitions as `hir::GenericParam`, in the given
+ // example `type TestReturn<'a, T, 'x> = impl Debug + 'x`, it creates a collection
+ // containing `&['x]`.
+ let lifetime_defs = lctx.arena.alloc_from_iter(collected_lifetimes.iter().map(
+ |&(new_node_id, lifetime)| {
+ let hir_id = lctx.lower_node_id(new_node_id);
+ debug_assert_ne!(lctx.opt_local_def_id(new_node_id), None);
+
+ let (name, kind) = if lifetime.ident.name == kw::UnderscoreLifetime {
+ (hir::ParamName::Fresh, hir::LifetimeParamKind::Elided)
+ } else {
+ (
+ hir::ParamName::Plain(lifetime.ident),
+ hir::LifetimeParamKind::Explicit,
+ )
+ };
+
+ hir::GenericParam {
+ hir_id,
+ name,
+ span: lifetime.ident.span,
+ pure_wrt_drop: false,
+ kind: hir::GenericParamKind::Lifetime { kind },
+ colon_span: None,
+ }
+ },
+ ));
+ debug!(?lifetime_defs);
+
+ // Then when we lower the param bounds, references to 'a are remapped to 'a1, so we
+ // get back Debug + 'a1, which is suitable for use on the TAIT.
+ let hir_bounds = lctx.lower_param_bounds(bounds, itctx);
+ debug!(?hir_bounds);
+
+ let opaque_ty_item = hir::OpaqueTy {
+ generics: self.arena.alloc(hir::Generics {
+ params: lifetime_defs,
+ predicates: &[],
+ has_where_clause_predicates: false,
+ where_clause_span: lctx.lower_span(span),
+ span: lctx.lower_span(span),
+ }),
+ bounds: hir_bounds,
+ origin,
+ };
+ debug!(?opaque_ty_item);
+
+ lctx.generate_opaque_type(opaque_ty_def_id, opaque_ty_item, span, opaque_ty_span)
+ })
+ });
+
+ // This creates HIR lifetime arguments as `hir::GenericArg`, in the given example `type
+ // TestReturn<'a, T, 'x> = impl Debug + 'x`, it creates a collection containing `&['x]`.
+ let lifetimes =
+ self.arena.alloc_from_iter(collected_lifetimes.into_iter().map(|(_, lifetime)| {
+ let id = self.next_node_id();
+ let span = lifetime.ident.span;
+
+ let ident = if lifetime.ident.name == kw::UnderscoreLifetime {
+ Ident::with_dummy_span(kw::UnderscoreLifetime)
+ } else {
+ lifetime.ident
+ };
+
+ let l = self.new_named_lifetime(lifetime.id, id, span, ident);
+ hir::GenericArg::Lifetime(l)
+ }));
+ debug!(?lifetimes);
+
+ // `impl Trait` now just becomes `Foo<'a, 'b, ..>`.
+ hir::TyKind::OpaqueDef(hir::ItemId { def_id: opaque_ty_def_id }, lifetimes)
+ }
+
+ /// Registers a new opaque type with the proper `NodeId`s and
+ /// returns the lowered node-ID for the opaque type.
+ fn generate_opaque_type(
+ &mut self,
+ opaque_ty_id: LocalDefId,
+ opaque_ty_item: hir::OpaqueTy<'hir>,
+ span: Span,
+ opaque_ty_span: Span,
+ ) -> hir::OwnerNode<'hir> {
+ let opaque_ty_item_kind = hir::ItemKind::OpaqueTy(opaque_ty_item);
+ // Generate an `type Foo = impl Trait;` declaration.
+ trace!("registering opaque type with id {:#?}", opaque_ty_id);
+ let opaque_ty_item = hir::Item {
+ def_id: opaque_ty_id,
+ ident: Ident::empty(),
+ kind: opaque_ty_item_kind,
+ vis_span: self.lower_span(span.shrink_to_lo()),
+ span: self.lower_span(opaque_ty_span),
+ };
+ hir::OwnerNode::Item(self.arena.alloc(opaque_ty_item))
+ }
+
+ /// Given a `parent_def_id`, a list of `lifetimes_in_bounds and a `remapping` hash to be
+ /// filled, this function creates new definitions for `Param` and `Fresh` lifetimes, inserts the
+ /// new definition, adds it to the remapping with the definition of the given lifetime and
+ /// returns a list of lifetimes to be lowered afterwards.
+ fn create_lifetime_defs(
+ &mut self,
+ parent_def_id: LocalDefId,
+ lifetimes_in_bounds: &[Lifetime],
+ remapping: &mut FxHashMap<LocalDefId, LocalDefId>,
+ ) -> Vec<(NodeId, Lifetime)> {
+ let mut result = Vec::new();
+
+ for lifetime in lifetimes_in_bounds {
+ let res = self.resolver.get_lifetime_res(lifetime.id).unwrap_or(LifetimeRes::Error);
+ debug!(?res);
+
+ match res {
+ LifetimeRes::Param { param: old_def_id, binder: _ } => {
+ if remapping.get(&old_def_id).is_none() {
+ let node_id = self.next_node_id();
+
+ let new_def_id = self.create_def(
+ parent_def_id,
+ node_id,
+ DefPathData::LifetimeNs(lifetime.ident.name),
+ );
+ remapping.insert(old_def_id, new_def_id);
+
+ result.push((node_id, *lifetime));
+ }
+ }
+
+ LifetimeRes::Fresh { param, binder: _ } => {
+ debug_assert_eq!(lifetime.ident.name, kw::UnderscoreLifetime);
+ if let Some(old_def_id) = self.opt_local_def_id(param) && remapping.get(&old_def_id).is_none() {
+ let node_id = self.next_node_id();
+
+ let new_def_id = self.create_def(
+ parent_def_id,
+ node_id,
+ DefPathData::LifetimeNs(kw::UnderscoreLifetime),
+ );
+ remapping.insert(old_def_id, new_def_id);
+
+ result.push((node_id, *lifetime));
+ }
+ }
+
+ LifetimeRes::Static | LifetimeRes::Error => {}
+
+ res => {
+ let bug_msg = format!(
+ "Unexpected lifetime resolution {:?} for {:?} at {:?}",
+ res, lifetime.ident, lifetime.ident.span
+ );
+ span_bug!(lifetime.ident.span, "{}", bug_msg);
+ }
+ }
+ }
+
+ result
+ }
+
+ fn lower_fn_params_to_names(&mut self, decl: &FnDecl) -> &'hir [Ident] {
+ // Skip the `...` (`CVarArgs`) trailing arguments from the AST,
+ // as they are not explicit in HIR/Ty function signatures.
+ // (instead, the `c_variadic` flag is set to `true`)
+ let mut inputs = &decl.inputs[..];
+ if decl.c_variadic() {
+ inputs = &inputs[..inputs.len() - 1];
+ }
+ self.arena.alloc_from_iter(inputs.iter().map(|param| match param.pat.kind {
+ PatKind::Ident(_, ident, _) => self.lower_ident(ident),
+ _ => Ident::new(kw::Empty, self.lower_span(param.pat.span)),
+ }))
+ }
+
+ // Lowers a function declaration.
+ //
+ // `decl`: the unlowered (AST) function declaration.
+ // `fn_def_id`: if `Some`, impl Trait arguments are lowered into generic parameters on the
+ // given DefId, otherwise impl Trait is disallowed. Must be `Some` if
+ // `make_ret_async` is also `Some`.
+ // `impl_trait_return_allow`: determines whether `impl Trait` can be used in return position.
+ // This guards against trait declarations and implementations where `impl Trait` is
+ // disallowed.
+ // `make_ret_async`: if `Some`, converts `-> T` into `-> impl Future<Output = T>` in the
+ // return type. This is used for `async fn` declarations. The `NodeId` is the ID of the
+ // return type `impl Trait` item.
+ #[tracing::instrument(level = "debug", skip(self))]
+ fn lower_fn_decl(
+ &mut self,
+ decl: &FnDecl,
+ fn_node_id: Option<NodeId>,
+ kind: FnDeclKind,
+ make_ret_async: Option<NodeId>,
+ ) -> &'hir hir::FnDecl<'hir> {
+ let c_variadic = decl.c_variadic();
+
+ // Skip the `...` (`CVarArgs`) trailing arguments from the AST,
+ // as they are not explicit in HIR/Ty function signatures.
+ // (instead, the `c_variadic` flag is set to `true`)
+ let mut inputs = &decl.inputs[..];
+ if c_variadic {
+ inputs = &inputs[..inputs.len() - 1];
+ }
+ let inputs = self.arena.alloc_from_iter(inputs.iter().map(|param| {
+ if fn_node_id.is_some() {
+ self.lower_ty_direct(&param.ty, ImplTraitContext::Universal)
+ } else {
+ self.lower_ty_direct(
+ &param.ty,
+ ImplTraitContext::Disallowed(match kind {
+ FnDeclKind::Fn | FnDeclKind::Inherent => {
+ unreachable!("fn should allow in-band lifetimes")
+ }
+ FnDeclKind::ExternFn => ImplTraitPosition::ExternFnParam,
+ FnDeclKind::Closure => ImplTraitPosition::ClosureParam,
+ FnDeclKind::Pointer => ImplTraitPosition::PointerParam,
+ FnDeclKind::Trait => ImplTraitPosition::TraitParam,
+ FnDeclKind::Impl => ImplTraitPosition::ImplParam,
+ }),
+ )
+ }
+ }));
+
+ let output = if let Some(ret_id) = make_ret_async {
+ self.lower_async_fn_ret_ty(
+ &decl.output,
+ fn_node_id.expect("`make_ret_async` but no `fn_def_id`"),
+ ret_id,
+ )
+ } else {
+ match decl.output {
+ FnRetTy::Ty(ref ty) => {
+ let context = match fn_node_id {
+ Some(fn_node_id) if kind.impl_trait_return_allowed() => {
+ let fn_def_id = self.local_def_id(fn_node_id);
+ ImplTraitContext::ReturnPositionOpaqueTy {
+ origin: hir::OpaqueTyOrigin::FnReturn(fn_def_id),
+ }
+ }
+ _ => ImplTraitContext::Disallowed(match kind {
+ FnDeclKind::Fn | FnDeclKind::Inherent => {
+ unreachable!("fn should allow in-band lifetimes")
+ }
+ FnDeclKind::ExternFn => ImplTraitPosition::ExternFnReturn,
+ FnDeclKind::Closure => ImplTraitPosition::ClosureReturn,
+ FnDeclKind::Pointer => ImplTraitPosition::PointerReturn,
+ FnDeclKind::Trait => ImplTraitPosition::TraitReturn,
+ FnDeclKind::Impl => ImplTraitPosition::ImplReturn,
+ }),
+ };
+ hir::FnRetTy::Return(self.lower_ty(ty, context))
+ }
+ FnRetTy::Default(span) => hir::FnRetTy::DefaultReturn(self.lower_span(span)),
+ }
+ };
+
+ self.arena.alloc(hir::FnDecl {
+ inputs,
+ output,
+ c_variadic,
+ implicit_self: decl.inputs.get(0).map_or(hir::ImplicitSelfKind::None, |arg| {
+ use BindingMode::{ByRef, ByValue};
+ let is_mutable_pat = matches!(
+ arg.pat.kind,
+ PatKind::Ident(ByValue(Mutability::Mut) | ByRef(Mutability::Mut), ..)
+ );
+
+ match arg.ty.kind {
+ TyKind::ImplicitSelf if is_mutable_pat => hir::ImplicitSelfKind::Mut,
+ TyKind::ImplicitSelf => hir::ImplicitSelfKind::Imm,
+ // Given we are only considering `ImplicitSelf` types, we needn't consider
+ // the case where we have a mutable pattern to a reference as that would
+ // no longer be an `ImplicitSelf`.
+ TyKind::Rptr(_, ref mt)
+ if mt.ty.kind.is_implicit_self() && mt.mutbl == ast::Mutability::Mut =>
+ {
+ hir::ImplicitSelfKind::MutRef
+ }
+ TyKind::Rptr(_, ref mt) if mt.ty.kind.is_implicit_self() => {
+ hir::ImplicitSelfKind::ImmRef
+ }
+ _ => hir::ImplicitSelfKind::None,
+ }
+ }),
+ })
+ }
+
+ // Transforms `-> T` for `async fn` into `-> OpaqueTy { .. }`
+ // combined with the following definition of `OpaqueTy`:
+ //
+ // type OpaqueTy<generics_from_parent_fn> = impl Future<Output = T>;
+ //
+ // `output`: unlowered output type (`T` in `-> T`)
+ // `fn_def_id`: `DefId` of the parent function (used to create child impl trait definition)
+ // `opaque_ty_node_id`: `NodeId` of the opaque `impl Trait` type that should be created
+ #[tracing::instrument(level = "debug", skip(self))]
+ fn lower_async_fn_ret_ty(
+ &mut self,
+ output: &FnRetTy,
+ fn_node_id: NodeId,
+ opaque_ty_node_id: NodeId,
+ ) -> hir::FnRetTy<'hir> {
+ let span = output.span();
+
+ let opaque_ty_span = self.mark_span_with_reason(DesugaringKind::Async, span, None);
+
+ let opaque_ty_def_id = self.local_def_id(opaque_ty_node_id);
+ let fn_def_id = self.local_def_id(fn_node_id);
+
+ // When we create the opaque type for this async fn, it is going to have
+ // to capture all the lifetimes involved in the signature (including in the
+ // return type). This is done by introducing lifetime parameters for:
+ //
+ // - all the explicitly declared lifetimes from the impl and function itself;
+ // - all the elided lifetimes in the fn arguments;
+ // - all the elided lifetimes in the return type.
+ //
+ // So for example in this snippet:
+ //
+ // ```rust
+ // impl<'a> Foo<'a> {
+ // async fn bar<'b>(&self, x: &'b Vec<f64>, y: &str) -> &u32 {
+ // // ^ '0 ^ '1 ^ '2
+ // // elided lifetimes used below
+ // }
+ // }
+ // ```
+ //
+ // we would create an opaque type like:
+ //
+ // ```
+ // type Bar<'a, 'b, '0, '1, '2> = impl Future<Output = &'2 u32>;
+ // ```
+ //
+ // and we would then desugar `bar` to the equivalent of:
+ //
+ // ```rust
+ // impl<'a> Foo<'a> {
+ // fn bar<'b, '0, '1>(&'0 self, x: &'b Vec<f64>, y: &'1 str) -> Bar<'a, 'b, '0, '1, '_>
+ // }
+ // ```
+ //
+ // Note that the final parameter to `Bar` is `'_`, not `'2` --
+ // this is because the elided lifetimes from the return type
+ // should be figured out using the ordinary elision rules, and
+ // this desugaring achieves that.
+
+ // Calculate all the lifetimes that should be captured
+ // by the opaque type. This should include all in-scope
+ // lifetime parameters, including those defined in-band.
+
+ // Contains the new lifetime definitions created for the TAIT (if any) generated for the
+ // return type.
+ let mut collected_lifetimes = Vec::new();
+ let mut new_remapping = FxHashMap::default();
+
+ let extra_lifetime_params = self.resolver.take_extra_lifetime_params(opaque_ty_node_id);
+ debug!(?extra_lifetime_params);
+ for (ident, outer_node_id, outer_res) in extra_lifetime_params {
+ let outer_def_id = self.local_def_id(outer_node_id);
+ let inner_node_id = self.next_node_id();
+
+ // Add a definition for the in scope lifetime def.
+ let inner_def_id = self.create_def(
+ opaque_ty_def_id,
+ inner_node_id,
+ DefPathData::LifetimeNs(ident.name),
+ );
+ new_remapping.insert(outer_def_id, inner_def_id);
+
+ let inner_res = match outer_res {
+ // Input lifetime like `'a`:
+ LifetimeRes::Param { param, .. } => {
+ LifetimeRes::Param { param, binder: fn_node_id }
+ }
+ // Input lifetime like `'1`:
+ LifetimeRes::Fresh { param, .. } => {
+ LifetimeRes::Fresh { param, binder: fn_node_id }
+ }
+ LifetimeRes::Static | LifetimeRes::Error => continue,
+ res => {
+ panic!(
+ "Unexpected lifetime resolution {:?} for {:?} at {:?}",
+ res, ident, ident.span
+ )
+ }
+ };
+
+ let lifetime = Lifetime { id: outer_node_id, ident };
+ collected_lifetimes.push((inner_node_id, lifetime, Some(inner_res)));
+ }
+
+ debug!(?collected_lifetimes);
+
+ // We only want to capture the lifetimes that appear in the bounds. So visit the bounds to
+ // find out exactly which ones those are.
+ // in fn return position, like the `fn test<'a>() -> impl Debug + 'a` example,
+ // we only keep the lifetimes that appear in the `impl Debug` itself:
+ let lifetimes_to_remap = lifetime_collector::lifetimes_in_ret_ty(&self.resolver, output);
+ debug!(?lifetimes_to_remap);
+
+ self.with_hir_id_owner(opaque_ty_node_id, |this| {
+ // If this opaque type is only capturing a subset of the lifetimes (those that appear
+ // in bounds), then create the new lifetime parameters required and create a mapping
+ // from the old `'a` (on the function) to the new `'a` (on the opaque type).
+ collected_lifetimes.extend(
+ this.create_lifetime_defs(
+ opaque_ty_def_id,
+ &lifetimes_to_remap,
+ &mut new_remapping,
+ )
+ .into_iter()
+ .map(|(new_node_id, lifetime)| (new_node_id, lifetime, None)),
+ );
+ debug!(?collected_lifetimes);
+ debug!(?new_remapping);
+
+ // Install the remapping from old to new (if any):
+ this.with_remapping(new_remapping, |this| {
+ // We have to be careful to get elision right here. The
+ // idea is that we create a lifetime parameter for each
+ // lifetime in the return type. So, given a return type
+ // like `async fn foo(..) -> &[&u32]`, we lower to `impl
+ // Future<Output = &'1 [ &'2 u32 ]>`.
+ //
+ // Then, we will create `fn foo(..) -> Foo<'_, '_>`, and
+ // hence the elision takes place at the fn site.
+ let future_bound =
+ this.lower_async_fn_output_type_to_future_bound(output, fn_def_id, span);
+
+ let generic_params = this.arena.alloc_from_iter(collected_lifetimes.iter().map(
+ |&(new_node_id, lifetime, _)| {
+ let hir_id = this.lower_node_id(new_node_id);
+ debug_assert_ne!(this.opt_local_def_id(new_node_id), None);
+
+ let (name, kind) = if lifetime.ident.name == kw::UnderscoreLifetime {
+ (hir::ParamName::Fresh, hir::LifetimeParamKind::Elided)
+ } else {
+ (
+ hir::ParamName::Plain(lifetime.ident),
+ hir::LifetimeParamKind::Explicit,
+ )
+ };
+
+ hir::GenericParam {
+ hir_id,
+ name,
+ span: lifetime.ident.span,
+ pure_wrt_drop: false,
+ kind: hir::GenericParamKind::Lifetime { kind },
+ colon_span: None,
+ }
+ },
+ ));
+ debug!("lower_async_fn_ret_ty: generic_params={:#?}", generic_params);
+
+ let opaque_ty_item = hir::OpaqueTy {
+ generics: this.arena.alloc(hir::Generics {
+ params: generic_params,
+ predicates: &[],
+ has_where_clause_predicates: false,
+ where_clause_span: this.lower_span(span),
+ span: this.lower_span(span),
+ }),
+ bounds: arena_vec![this; future_bound],
+ origin: hir::OpaqueTyOrigin::AsyncFn(fn_def_id),
+ };
+
+ trace!("exist ty from async fn def id: {:#?}", opaque_ty_def_id);
+ this.generate_opaque_type(opaque_ty_def_id, opaque_ty_item, span, opaque_ty_span)
+ })
+ });
+
+ // As documented above, we need to create the lifetime
+ // arguments to our opaque type. Continuing with our example,
+ // we're creating the type arguments for the return type:
+ //
+ // ```
+ // Bar<'a, 'b, '0, '1, '_>
+ // ```
+ //
+ // For the "input" lifetime parameters, we wish to create
+ // references to the parameters themselves, including the
+ // "implicit" ones created from parameter types (`'a`, `'b`,
+ // '`0`, `'1`).
+ //
+ // For the "output" lifetime parameters, we just want to
+ // generate `'_`.
+ let generic_args = self.arena.alloc_from_iter(collected_lifetimes.into_iter().map(
+ |(_, lifetime, res)| {
+ let id = self.next_node_id();
+ let span = lifetime.ident.span;
+
+ let ident = if lifetime.ident.name == kw::UnderscoreLifetime {
+ Ident::with_dummy_span(kw::UnderscoreLifetime)
+ } else {
+ lifetime.ident
+ };
+
+ let res = res.unwrap_or(
+ self.resolver.get_lifetime_res(lifetime.id).unwrap_or(LifetimeRes::Error),
+ );
+ let l = self.new_named_lifetime_with_res(id, span, ident, res);
+ hir::GenericArg::Lifetime(l)
+ },
+ ));
+
+ // Create the `Foo<...>` reference itself. Note that the `type
+ // Foo = impl Trait` is, internally, created as a child of the
+ // async fn, so the *type parameters* are inherited. It's
+ // only the lifetime parameters that we must supply.
+ let opaque_ty_ref =
+ hir::TyKind::OpaqueDef(hir::ItemId { def_id: opaque_ty_def_id }, generic_args);
+ let opaque_ty = self.ty(opaque_ty_span, opaque_ty_ref);
+ hir::FnRetTy::Return(self.arena.alloc(opaque_ty))
+ }
+
+ /// Transforms `-> T` into `Future<Output = T>`.
+ fn lower_async_fn_output_type_to_future_bound(
+ &mut self,
+ output: &FnRetTy,
+ fn_def_id: LocalDefId,
+ span: Span,
+ ) -> hir::GenericBound<'hir> {
+ // Compute the `T` in `Future<Output = T>` from the return type.
+ let output_ty = match output {
+ FnRetTy::Ty(ty) => {
+ // Not `OpaqueTyOrigin::AsyncFn`: that's only used for the
+ // `impl Future` opaque type that `async fn` implicitly
+ // generates.
+ let context = ImplTraitContext::ReturnPositionOpaqueTy {
+ origin: hir::OpaqueTyOrigin::FnReturn(fn_def_id),
+ };
+ self.lower_ty(ty, context)
+ }
+ FnRetTy::Default(ret_ty_span) => self.arena.alloc(self.ty_tup(*ret_ty_span, &[])),
+ };
+
+ // "<Output = T>"
+ let future_args = self.arena.alloc(hir::GenericArgs {
+ args: &[],
+ bindings: arena_vec![self; self.output_ty_binding(span, output_ty)],
+ parenthesized: false,
+ span_ext: DUMMY_SP,
+ });
+
+ hir::GenericBound::LangItemTrait(
+ // ::std::future::Future<future_params>
+ hir::LangItem::Future,
+ self.lower_span(span),
+ self.next_id(),
+ future_args,
+ )
+ }
+
+ #[instrument(level = "trace", skip(self))]
+ fn lower_param_bound(
+ &mut self,
+ tpb: &GenericBound,
+ itctx: ImplTraitContext,
+ ) -> hir::GenericBound<'hir> {
+ match tpb {
+ GenericBound::Trait(p, modifier) => hir::GenericBound::Trait(
+ self.lower_poly_trait_ref(p, itctx),
+ self.lower_trait_bound_modifier(*modifier),
+ ),
+ GenericBound::Outlives(lifetime) => {
+ hir::GenericBound::Outlives(self.lower_lifetime(lifetime))
+ }
+ }
+ }
+
+ fn lower_lifetime(&mut self, l: &Lifetime) -> hir::Lifetime {
+ let span = self.lower_span(l.ident.span);
+ let ident = self.lower_ident(l.ident);
+ self.new_named_lifetime(l.id, l.id, span, ident)
+ }
+
+ #[tracing::instrument(level = "debug", skip(self))]
+ fn new_named_lifetime_with_res(
+ &mut self,
+ id: NodeId,
+ span: Span,
+ ident: Ident,
+ res: LifetimeRes,
+ ) -> hir::Lifetime {
+ let name = match res {
+ LifetimeRes::Param { param, .. } => {
+ let p_name = ParamName::Plain(ident);
+ let param = self.resolver.get_remapped_def_id(param);
+
+ hir::LifetimeName::Param(param, p_name)
+ }
+ LifetimeRes::Fresh { param, .. } => {
+ debug_assert_eq!(ident.name, kw::UnderscoreLifetime);
+ let param = self.local_def_id(param);
+
+ hir::LifetimeName::Param(param, ParamName::Fresh)
+ }
+ LifetimeRes::Infer => hir::LifetimeName::Infer,
+ LifetimeRes::Static => hir::LifetimeName::Static,
+ LifetimeRes::Error => hir::LifetimeName::Error,
+ res => panic!("Unexpected lifetime resolution {:?} for {:?} at {:?}", res, ident, span),
+ };
+
+ debug!(?name);
+ hir::Lifetime { hir_id: self.lower_node_id(id), span: self.lower_span(span), name }
+ }
+
+ #[tracing::instrument(level = "debug", skip(self))]
+ fn new_named_lifetime(
+ &mut self,
+ id: NodeId,
+ new_id: NodeId,
+ span: Span,
+ ident: Ident,
+ ) -> hir::Lifetime {
+ let res = self.resolver.get_lifetime_res(id).unwrap_or(LifetimeRes::Error);
+ self.new_named_lifetime_with_res(new_id, span, ident, res)
+ }
+
+ fn lower_generic_params_mut<'s>(
+ &'s mut self,
+ params: &'s [GenericParam],
+ ) -> impl Iterator<Item = hir::GenericParam<'hir>> + Captures<'a> + Captures<'s> {
+ params.iter().map(move |param| self.lower_generic_param(param))
+ }
+
+ fn lower_generic_params(&mut self, params: &[GenericParam]) -> &'hir [hir::GenericParam<'hir>] {
+ self.arena.alloc_from_iter(self.lower_generic_params_mut(params))
+ }
+
+ #[instrument(level = "trace", skip(self))]
+ fn lower_generic_param(&mut self, param: &GenericParam) -> hir::GenericParam<'hir> {
+ let (name, kind) = self.lower_generic_param_kind(param);
+
+ let hir_id = self.lower_node_id(param.id);
+ self.lower_attrs(hir_id, &param.attrs);
+ hir::GenericParam {
+ hir_id,
+ name,
+ span: self.lower_span(param.span()),
+ pure_wrt_drop: self.tcx.sess.contains_name(&param.attrs, sym::may_dangle),
+ kind,
+ colon_span: param.colon_span.map(|s| self.lower_span(s)),
+ }
+ }
+
+ fn lower_generic_param_kind(
+ &mut self,
+ param: &GenericParam,
+ ) -> (hir::ParamName, hir::GenericParamKind<'hir>) {
+ match param.kind {
+ GenericParamKind::Lifetime => {
+ // AST resolution emitted an error on those parameters, so we lower them using
+ // `ParamName::Error`.
+ let param_name =
+ if let Some(LifetimeRes::Error) = self.resolver.get_lifetime_res(param.id) {
+ ParamName::Error
+ } else {
+ let ident = self.lower_ident(param.ident);
+ ParamName::Plain(ident)
+ };
+ let kind =
+ hir::GenericParamKind::Lifetime { kind: hir::LifetimeParamKind::Explicit };
+
+ (param_name, kind)
+ }
+ GenericParamKind::Type { ref default, .. } => {
+ let kind = hir::GenericParamKind::Type {
+ default: default.as_ref().map(|x| {
+ self.lower_ty(x, ImplTraitContext::Disallowed(ImplTraitPosition::Type))
+ }),
+ synthetic: false,
+ };
+
+ (hir::ParamName::Plain(self.lower_ident(param.ident)), kind)
+ }
+ GenericParamKind::Const { ref ty, kw_span: _, ref default } => {
+ let ty = self.lower_ty(&ty, ImplTraitContext::Disallowed(ImplTraitPosition::Type));
+ let default = default.as_ref().map(|def| self.lower_anon_const(def));
+ (
+ hir::ParamName::Plain(self.lower_ident(param.ident)),
+ hir::GenericParamKind::Const { ty, default },
+ )
+ }
+ }
+ }
+
+ fn lower_trait_ref(&mut self, p: &TraitRef, itctx: ImplTraitContext) -> hir::TraitRef<'hir> {
+ let path = match self.lower_qpath(p.ref_id, &None, &p.path, ParamMode::Explicit, itctx) {
+ hir::QPath::Resolved(None, path) => path,
+ qpath => panic!("lower_trait_ref: unexpected QPath `{:?}`", qpath),
+ };
+ hir::TraitRef { path, hir_ref_id: self.lower_node_id(p.ref_id) }
+ }
+
+ #[tracing::instrument(level = "debug", skip(self))]
+ fn lower_poly_trait_ref(
+ &mut self,
+ p: &PolyTraitRef,
+ itctx: ImplTraitContext,
+ ) -> hir::PolyTraitRef<'hir> {
+ let bound_generic_params =
+ self.lower_lifetime_binder(p.trait_ref.ref_id, &p.bound_generic_params);
+ let trait_ref = self.lower_trait_ref(&p.trait_ref, itctx);
+ hir::PolyTraitRef { bound_generic_params, trait_ref, span: self.lower_span(p.span) }
+ }
+
+ fn lower_mt(&mut self, mt: &MutTy, itctx: ImplTraitContext) -> hir::MutTy<'hir> {
+ hir::MutTy { ty: self.lower_ty(&mt.ty, itctx), mutbl: mt.mutbl }
+ }
+
+ fn lower_param_bounds(
+ &mut self,
+ bounds: &[GenericBound],
+ itctx: ImplTraitContext,
+ ) -> hir::GenericBounds<'hir> {
+ self.arena.alloc_from_iter(self.lower_param_bounds_mut(bounds, itctx))
+ }
+
+ fn lower_param_bounds_mut<'s>(
+ &'s mut self,
+ bounds: &'s [GenericBound],
+ itctx: ImplTraitContext,
+ ) -> impl Iterator<Item = hir::GenericBound<'hir>> + Captures<'s> + Captures<'a> {
+ bounds.iter().map(move |bound| self.lower_param_bound(bound, itctx))
+ }
+
+ fn lower_generic_and_bounds(
+ &mut self,
+ node_id: NodeId,
+ span: Span,
+ ident: Ident,
+ bounds: &[GenericBound],
+ ) -> (hir::GenericParam<'hir>, Option<hir::WherePredicate<'hir>>, hir::TyKind<'hir>) {
+ // Add a definition for the in-band `Param`.
+ let def_id = self.local_def_id(node_id);
+
+ // Set the name to `impl Bound1 + Bound2`.
+ let param = hir::GenericParam {
+ hir_id: self.lower_node_id(node_id),
+ name: ParamName::Plain(self.lower_ident(ident)),
+ pure_wrt_drop: false,
+ span: self.lower_span(span),
+ kind: hir::GenericParamKind::Type { default: None, synthetic: true },
+ colon_span: None,
+ };
+
+ let preds = self.lower_generic_bound_predicate(
+ ident,
+ node_id,
+ &GenericParamKind::Type { default: None },
+ bounds,
+ ImplTraitContext::Universal,
+ hir::PredicateOrigin::ImplTrait,
+ );
+
+ let ty = hir::TyKind::Path(hir::QPath::Resolved(
+ None,
+ self.arena.alloc(hir::Path {
+ span: self.lower_span(span),
+ res: Res::Def(DefKind::TyParam, def_id.to_def_id()),
+ segments: arena_vec![self; hir::PathSegment::from_ident(self.lower_ident(ident))],
+ }),
+ ));
+
+ (param, preds, ty)
+ }
+
+ /// Lowers a block directly to an expression, presuming that it
+ /// has no attributes and is not targeted by a `break`.
+ fn lower_block_expr(&mut self, b: &Block) -> hir::Expr<'hir> {
+ let block = self.lower_block(b, false);
+ self.expr_block(block, AttrVec::new())
+ }
+
+ fn lower_array_length(&mut self, c: &AnonConst) -> hir::ArrayLen {
+ match c.value.kind {
+ ExprKind::Underscore => {
+ if self.tcx.features().generic_arg_infer {
+ hir::ArrayLen::Infer(self.lower_node_id(c.id), c.value.span)
+ } else {
+ feature_err(
+ &self.tcx.sess.parse_sess,
+ sym::generic_arg_infer,
+ c.value.span,
+ "using `_` for array lengths is unstable",
+ )
+ .emit();
+ hir::ArrayLen::Body(self.lower_anon_const(c))
+ }
+ }
+ _ => hir::ArrayLen::Body(self.lower_anon_const(c)),
+ }
+ }
+
+ fn lower_anon_const(&mut self, c: &AnonConst) -> hir::AnonConst {
+ self.with_new_scopes(|this| hir::AnonConst {
+ hir_id: this.lower_node_id(c.id),
+ body: this.lower_const_body(c.value.span, Some(&c.value)),
+ })
+ }
+
+ fn lower_unsafe_source(&mut self, u: UnsafeSource) -> hir::UnsafeSource {
+ match u {
+ CompilerGenerated => hir::UnsafeSource::CompilerGenerated,
+ UserProvided => hir::UnsafeSource::UserProvided,
+ }
+ }
+
+ fn lower_trait_bound_modifier(&mut self, f: TraitBoundModifier) -> hir::TraitBoundModifier {
+ match f {
+ TraitBoundModifier::None => hir::TraitBoundModifier::None,
+ TraitBoundModifier::MaybeConst => hir::TraitBoundModifier::MaybeConst,
+
+ // `MaybeConstMaybe` will cause an error during AST validation, but we need to pick a
+ // placeholder for compilation to proceed.
+ TraitBoundModifier::MaybeConstMaybe | TraitBoundModifier::Maybe => {
+ hir::TraitBoundModifier::Maybe
+ }
+ }
+ }
+
+ // Helper methods for building HIR.
+
+ fn stmt(&mut self, span: Span, kind: hir::StmtKind<'hir>) -> hir::Stmt<'hir> {
+ hir::Stmt { span: self.lower_span(span), kind, hir_id: self.next_id() }
+ }
+
+ fn stmt_expr(&mut self, span: Span, expr: hir::Expr<'hir>) -> hir::Stmt<'hir> {
+ self.stmt(span, hir::StmtKind::Expr(self.arena.alloc(expr)))
+ }
+
+ fn stmt_let_pat(
+ &mut self,
+ attrs: Option<&'hir [Attribute]>,
+ span: Span,
+ init: Option<&'hir hir::Expr<'hir>>,
+ pat: &'hir hir::Pat<'hir>,
+ source: hir::LocalSource,
+ ) -> hir::Stmt<'hir> {
+ let hir_id = self.next_id();
+ if let Some(a) = attrs {
+ debug_assert!(!a.is_empty());
+ self.attrs.insert(hir_id.local_id, a);
+ }
+ let local = hir::Local {
+ hir_id,
+ init,
+ pat,
+ els: None,
+ source,
+ span: self.lower_span(span),
+ ty: None,
+ };
+ self.stmt(span, hir::StmtKind::Local(self.arena.alloc(local)))
+ }
+
+ fn block_expr(&mut self, expr: &'hir hir::Expr<'hir>) -> &'hir hir::Block<'hir> {
+ self.block_all(expr.span, &[], Some(expr))
+ }
+
+ fn block_all(
+ &mut self,
+ span: Span,
+ stmts: &'hir [hir::Stmt<'hir>],
+ expr: Option<&'hir hir::Expr<'hir>>,
+ ) -> &'hir hir::Block<'hir> {
+ let blk = hir::Block {
+ stmts,
+ expr,
+ hir_id: self.next_id(),
+ rules: hir::BlockCheckMode::DefaultBlock,
+ span: self.lower_span(span),
+ targeted_by_break: false,
+ };
+ self.arena.alloc(blk)
+ }
+
+ fn pat_cf_continue(&mut self, span: Span, pat: &'hir hir::Pat<'hir>) -> &'hir hir::Pat<'hir> {
+ let field = self.single_pat_field(span, pat);
+ self.pat_lang_item_variant(span, hir::LangItem::ControlFlowContinue, field, None)
+ }
+
+ fn pat_cf_break(&mut self, span: Span, pat: &'hir hir::Pat<'hir>) -> &'hir hir::Pat<'hir> {
+ let field = self.single_pat_field(span, pat);
+ self.pat_lang_item_variant(span, hir::LangItem::ControlFlowBreak, field, None)
+ }
+
+ fn pat_some(&mut self, span: Span, pat: &'hir hir::Pat<'hir>) -> &'hir hir::Pat<'hir> {
+ let field = self.single_pat_field(span, pat);
+ self.pat_lang_item_variant(span, hir::LangItem::OptionSome, field, None)
+ }
+
+ fn pat_none(&mut self, span: Span) -> &'hir hir::Pat<'hir> {
+ self.pat_lang_item_variant(span, hir::LangItem::OptionNone, &[], None)
+ }
+
+ fn single_pat_field(
+ &mut self,
+ span: Span,
+ pat: &'hir hir::Pat<'hir>,
+ ) -> &'hir [hir::PatField<'hir>] {
+ let field = hir::PatField {
+ hir_id: self.next_id(),
+ ident: Ident::new(sym::integer(0), self.lower_span(span)),
+ is_shorthand: false,
+ pat,
+ span: self.lower_span(span),
+ };
+ arena_vec![self; field]
+ }
+
+ fn pat_lang_item_variant(
+ &mut self,
+ span: Span,
+ lang_item: hir::LangItem,
+ fields: &'hir [hir::PatField<'hir>],
+ hir_id: Option<hir::HirId>,
+ ) -> &'hir hir::Pat<'hir> {
+ let qpath = hir::QPath::LangItem(lang_item, self.lower_span(span), hir_id);
+ self.pat(span, hir::PatKind::Struct(qpath, fields, false))
+ }
+
+ fn pat_ident(&mut self, span: Span, ident: Ident) -> (&'hir hir::Pat<'hir>, hir::HirId) {
+ self.pat_ident_binding_mode(span, ident, hir::BindingAnnotation::Unannotated)
+ }
+
+ fn pat_ident_mut(&mut self, span: Span, ident: Ident) -> (hir::Pat<'hir>, hir::HirId) {
+ self.pat_ident_binding_mode_mut(span, ident, hir::BindingAnnotation::Unannotated)
+ }
+
+ fn pat_ident_binding_mode(
+ &mut self,
+ span: Span,
+ ident: Ident,
+ bm: hir::BindingAnnotation,
+ ) -> (&'hir hir::Pat<'hir>, hir::HirId) {
+ let (pat, hir_id) = self.pat_ident_binding_mode_mut(span, ident, bm);
+ (self.arena.alloc(pat), hir_id)
+ }
+
+ fn pat_ident_binding_mode_mut(
+ &mut self,
+ span: Span,
+ ident: Ident,
+ bm: hir::BindingAnnotation,
+ ) -> (hir::Pat<'hir>, hir::HirId) {
+ let hir_id = self.next_id();
+
+ (
+ hir::Pat {
+ hir_id,
+ kind: hir::PatKind::Binding(bm, hir_id, self.lower_ident(ident), None),
+ span: self.lower_span(span),
+ default_binding_modes: true,
+ },
+ hir_id,
+ )
+ }
+
+ fn pat(&mut self, span: Span, kind: hir::PatKind<'hir>) -> &'hir hir::Pat<'hir> {
+ self.arena.alloc(hir::Pat {
+ hir_id: self.next_id(),
+ kind,
+ span: self.lower_span(span),
+ default_binding_modes: true,
+ })
+ }
+
+ fn pat_without_dbm(&mut self, span: Span, kind: hir::PatKind<'hir>) -> hir::Pat<'hir> {
+ hir::Pat {
+ hir_id: self.next_id(),
+ kind,
+ span: self.lower_span(span),
+ default_binding_modes: false,
+ }
+ }
+
+ fn ty_path(
+ &mut self,
+ mut hir_id: hir::HirId,
+ span: Span,
+ qpath: hir::QPath<'hir>,
+ ) -> hir::Ty<'hir> {
+ let kind = match qpath {
+ hir::QPath::Resolved(None, path) => {
+ // Turn trait object paths into `TyKind::TraitObject` instead.
+ match path.res {
+ Res::Def(DefKind::Trait | DefKind::TraitAlias, _) => {
+ let principal = hir::PolyTraitRef {
+ bound_generic_params: &[],
+ trait_ref: hir::TraitRef { path, hir_ref_id: hir_id },
+ span: self.lower_span(span),
+ };
+
+ // The original ID is taken by the `PolyTraitRef`,
+ // so the `Ty` itself needs a different one.
+ hir_id = self.next_id();
+ hir::TyKind::TraitObject(
+ arena_vec![self; principal],
+ self.elided_dyn_bound(span),
+ TraitObjectSyntax::None,
+ )
+ }
+ _ => hir::TyKind::Path(hir::QPath::Resolved(None, path)),
+ }
+ }
+ _ => hir::TyKind::Path(qpath),
+ };
+
+ hir::Ty { hir_id, kind, span: self.lower_span(span) }
+ }
+
+ /// Invoked to create the lifetime argument(s) for an elided trait object
+ /// bound, like the bound in `Box<dyn Debug>`. This method is not invoked
+ /// when the bound is written, even if it is written with `'_` like in
+ /// `Box<dyn Debug + '_>`. In those cases, `lower_lifetime` is invoked.
+ fn elided_dyn_bound(&mut self, span: Span) -> hir::Lifetime {
+ let r = hir::Lifetime {
+ hir_id: self.next_id(),
+ span: self.lower_span(span),
+ name: hir::LifetimeName::ImplicitObjectLifetimeDefault,
+ };
+ debug!("elided_dyn_bound: r={:?}", r);
+ r
+ }
+}
+
+/// Helper struct for delayed construction of GenericArgs.
+struct GenericArgsCtor<'hir> {
+ args: SmallVec<[hir::GenericArg<'hir>; 4]>,
+ bindings: &'hir [hir::TypeBinding<'hir>],
+ parenthesized: bool,
+ span: Span,
+}
+
+impl<'hir> GenericArgsCtor<'hir> {
+ fn is_empty(&self) -> bool {
+ self.args.is_empty() && self.bindings.is_empty() && !self.parenthesized
+ }
+
+ fn into_generic_args(self, this: &LoweringContext<'_, 'hir>) -> &'hir hir::GenericArgs<'hir> {
+ let ga = hir::GenericArgs {
+ args: this.arena.alloc_from_iter(self.args),
+ bindings: self.bindings,
+ parenthesized: self.parenthesized,
+ span_ext: this.lower_span(self.span),
+ };
+ this.arena.alloc(ga)
+ }
+}
diff --git a/compiler/rustc_ast_lowering/src/lifetime_collector.rs b/compiler/rustc_ast_lowering/src/lifetime_collector.rs
new file mode 100644
index 000000000..81006e00f
--- /dev/null
+++ b/compiler/rustc_ast_lowering/src/lifetime_collector.rs
@@ -0,0 +1,115 @@
+use super::ResolverAstLoweringExt;
+use rustc_ast::visit::{self, BoundKind, LifetimeCtxt, Visitor};
+use rustc_ast::{
+ FnRetTy, GenericBounds, Lifetime, NodeId, PathSegment, PolyTraitRef, TraitBoundModifier, Ty,
+ TyKind,
+};
+use rustc_hir::def::LifetimeRes;
+use rustc_middle::span_bug;
+use rustc_middle::ty::ResolverAstLowering;
+use rustc_span::symbol::{kw, Ident};
+use rustc_span::Span;
+
+struct LifetimeCollectVisitor<'ast> {
+ resolver: &'ast ResolverAstLowering,
+ current_binders: Vec<NodeId>,
+ collected_lifetimes: Vec<Lifetime>,
+}
+
+impl<'ast> LifetimeCollectVisitor<'ast> {
+ fn new(resolver: &'ast ResolverAstLowering) -> Self {
+ Self { resolver, current_binders: Vec::new(), collected_lifetimes: Vec::new() }
+ }
+
+ fn record_lifetime_use(&mut self, lifetime: Lifetime) {
+ match self.resolver.get_lifetime_res(lifetime.id).unwrap_or(LifetimeRes::Error) {
+ LifetimeRes::Param { binder, .. } | LifetimeRes::Fresh { binder, .. } => {
+ if !self.current_binders.contains(&binder) {
+ if !self.collected_lifetimes.contains(&lifetime) {
+ self.collected_lifetimes.push(lifetime);
+ }
+ }
+ }
+ LifetimeRes::Static | LifetimeRes::Error => {
+ if !self.collected_lifetimes.contains(&lifetime) {
+ self.collected_lifetimes.push(lifetime);
+ }
+ }
+ LifetimeRes::Infer => {}
+ res => {
+ let bug_msg = format!(
+ "Unexpected lifetime resolution {:?} for {:?} at {:?}",
+ res, lifetime.ident, lifetime.ident.span
+ );
+ span_bug!(lifetime.ident.span, "{}", bug_msg);
+ }
+ }
+ }
+
+ /// This collect lifetimes that are elided, for nodes like `Foo<T>` where there are no explicit
+ /// lifetime nodes. Is equivalent to having "pseudo" nodes introduced for each of the node ids
+ /// in the list start..end.
+ fn record_elided_anchor(&mut self, node_id: NodeId, span: Span) {
+ if let Some(LifetimeRes::ElidedAnchor { start, end }) =
+ self.resolver.get_lifetime_res(node_id)
+ {
+ for i in start..end {
+ let lifetime = Lifetime { id: i, ident: Ident::new(kw::UnderscoreLifetime, span) };
+ self.record_lifetime_use(lifetime);
+ }
+ }
+ }
+}
+
+impl<'ast> Visitor<'ast> for LifetimeCollectVisitor<'ast> {
+ fn visit_lifetime(&mut self, lifetime: &'ast Lifetime, _: LifetimeCtxt) {
+ self.record_lifetime_use(*lifetime);
+ }
+
+ fn visit_path_segment(&mut self, path_span: Span, path_segment: &'ast PathSegment) {
+ self.record_elided_anchor(path_segment.id, path_span);
+ visit::walk_path_segment(self, path_span, path_segment);
+ }
+
+ fn visit_poly_trait_ref(&mut self, t: &'ast PolyTraitRef, m: &'ast TraitBoundModifier) {
+ self.current_binders.push(t.trait_ref.ref_id);
+
+ visit::walk_poly_trait_ref(self, t, m);
+
+ self.current_binders.pop();
+ }
+
+ fn visit_ty(&mut self, t: &'ast Ty) {
+ match t.kind {
+ TyKind::BareFn(_) => {
+ self.current_binders.push(t.id);
+ visit::walk_ty(self, t);
+ self.current_binders.pop();
+ }
+ TyKind::Rptr(None, _) => {
+ self.record_elided_anchor(t.id, t.span);
+ visit::walk_ty(self, t);
+ }
+ _ => {
+ visit::walk_ty(self, t);
+ }
+ }
+ }
+}
+
+pub fn lifetimes_in_ret_ty(resolver: &ResolverAstLowering, ret_ty: &FnRetTy) -> Vec<Lifetime> {
+ let mut visitor = LifetimeCollectVisitor::new(resolver);
+ visitor.visit_fn_ret_ty(ret_ty);
+ visitor.collected_lifetimes
+}
+
+pub fn lifetimes_in_bounds(
+ resolver: &ResolverAstLowering,
+ bounds: &GenericBounds,
+) -> Vec<Lifetime> {
+ let mut visitor = LifetimeCollectVisitor::new(resolver);
+ for bound in bounds {
+ visitor.visit_param_bound(bound, BoundKind::Bound);
+ }
+ visitor.collected_lifetimes
+}
diff --git a/compiler/rustc_ast_lowering/src/pat.rs b/compiler/rustc_ast_lowering/src/pat.rs
new file mode 100644
index 000000000..bd2e76e55
--- /dev/null
+++ b/compiler/rustc_ast_lowering/src/pat.rs
@@ -0,0 +1,350 @@
+use super::ResolverAstLoweringExt;
+use super::{ImplTraitContext, LoweringContext, ParamMode};
+use crate::ImplTraitPosition;
+
+use rustc_ast::ptr::P;
+use rustc_ast::*;
+use rustc_data_structures::stack::ensure_sufficient_stack;
+use rustc_errors::Applicability;
+use rustc_hir as hir;
+use rustc_hir::def::Res;
+use rustc_span::symbol::Ident;
+use rustc_span::{source_map::Spanned, Span};
+
+impl<'a, 'hir> LoweringContext<'a, 'hir> {
+ pub(crate) fn lower_pat(&mut self, pattern: &Pat) -> &'hir hir::Pat<'hir> {
+ self.arena.alloc(self.lower_pat_mut(pattern))
+ }
+
+ pub(crate) fn lower_pat_mut(&mut self, mut pattern: &Pat) -> hir::Pat<'hir> {
+ ensure_sufficient_stack(|| {
+ // loop here to avoid recursion
+ let node = loop {
+ match pattern.kind {
+ PatKind::Wild => break hir::PatKind::Wild,
+ PatKind::Ident(ref binding_mode, ident, ref sub) => {
+ let lower_sub = |this: &mut Self| sub.as_ref().map(|s| this.lower_pat(&*s));
+ break self.lower_pat_ident(pattern, binding_mode, ident, lower_sub);
+ }
+ PatKind::Lit(ref e) => {
+ break hir::PatKind::Lit(self.lower_expr_within_pat(e, false));
+ }
+ PatKind::TupleStruct(ref qself, ref path, ref pats) => {
+ let qpath = self.lower_qpath(
+ pattern.id,
+ qself,
+ path,
+ ParamMode::Optional,
+ ImplTraitContext::Disallowed(ImplTraitPosition::Path),
+ );
+ let (pats, ddpos) = self.lower_pat_tuple(pats, "tuple struct");
+ break hir::PatKind::TupleStruct(qpath, pats, ddpos);
+ }
+ PatKind::Or(ref pats) => {
+ break hir::PatKind::Or(
+ self.arena.alloc_from_iter(pats.iter().map(|x| self.lower_pat_mut(x))),
+ );
+ }
+ PatKind::Path(ref qself, ref path) => {
+ let qpath = self.lower_qpath(
+ pattern.id,
+ qself,
+ path,
+ ParamMode::Optional,
+ ImplTraitContext::Disallowed(ImplTraitPosition::Path),
+ );
+ break hir::PatKind::Path(qpath);
+ }
+ PatKind::Struct(ref qself, ref path, ref fields, etc) => {
+ let qpath = self.lower_qpath(
+ pattern.id,
+ qself,
+ path,
+ ParamMode::Optional,
+ ImplTraitContext::Disallowed(ImplTraitPosition::Path),
+ );
+
+ let fs = self.arena.alloc_from_iter(fields.iter().map(|f| hir::PatField {
+ hir_id: self.next_id(),
+ ident: self.lower_ident(f.ident),
+ pat: self.lower_pat(&f.pat),
+ is_shorthand: f.is_shorthand,
+ span: self.lower_span(f.span),
+ }));
+ break hir::PatKind::Struct(qpath, fs, etc);
+ }
+ PatKind::Tuple(ref pats) => {
+ let (pats, ddpos) = self.lower_pat_tuple(pats, "tuple");
+ break hir::PatKind::Tuple(pats, ddpos);
+ }
+ PatKind::Box(ref inner) => {
+ break hir::PatKind::Box(self.lower_pat(inner));
+ }
+ PatKind::Ref(ref inner, mutbl) => {
+ break hir::PatKind::Ref(self.lower_pat(inner), mutbl);
+ }
+ PatKind::Range(ref e1, ref e2, Spanned { node: ref end, .. }) => {
+ break hir::PatKind::Range(
+ e1.as_deref().map(|e| self.lower_expr_within_pat(e, true)),
+ e2.as_deref().map(|e| self.lower_expr_within_pat(e, true)),
+ self.lower_range_end(end, e2.is_some()),
+ );
+ }
+ PatKind::Slice(ref pats) => break self.lower_pat_slice(pats),
+ PatKind::Rest => {
+ // If we reach here the `..` pattern is not semantically allowed.
+ break self.ban_illegal_rest_pat(pattern.span);
+ }
+ // return inner to be processed in next loop
+ PatKind::Paren(ref inner) => pattern = inner,
+ PatKind::MacCall(_) => panic!("{:?} shouldn't exist here", pattern.span),
+ }
+ };
+
+ self.pat_with_node_id_of(pattern, node)
+ })
+ }
+
+ fn lower_pat_tuple(
+ &mut self,
+ pats: &[P<Pat>],
+ ctx: &str,
+ ) -> (&'hir [hir::Pat<'hir>], Option<usize>) {
+ let mut elems = Vec::with_capacity(pats.len());
+ let mut rest = None;
+
+ let mut iter = pats.iter().enumerate();
+ for (idx, pat) in iter.by_ref() {
+ // Interpret the first `..` pattern as a sub-tuple pattern.
+ // Note that unlike for slice patterns,
+ // where `xs @ ..` is a legal sub-slice pattern,
+ // it is not a legal sub-tuple pattern.
+ match pat.kind {
+ // Found a sub-tuple rest pattern
+ PatKind::Rest => {
+ rest = Some((idx, pat.span));
+ break;
+ }
+ // Found a sub-tuple pattern `$binding_mode $ident @ ..`.
+ // This is not allowed as a sub-tuple pattern
+ PatKind::Ident(ref _bm, ident, Some(ref sub)) if sub.is_rest() => {
+ let sp = pat.span;
+ self.diagnostic()
+ .struct_span_err(
+ sp,
+ &format!("`{} @` is not allowed in a {}", ident.name, ctx),
+ )
+ .span_label(sp, "this is only allowed in slice patterns")
+ .help("remove this and bind each tuple field independently")
+ .span_suggestion_verbose(
+ sp,
+ &format!("if you don't need to use the contents of {}, discard the tuple's remaining fields", ident),
+ "..",
+ Applicability::MaybeIncorrect,
+ )
+ .emit();
+ }
+ _ => {}
+ }
+
+ // It was not a sub-tuple pattern so lower it normally.
+ elems.push(self.lower_pat_mut(pat));
+ }
+
+ for (_, pat) in iter {
+ // There was a previous sub-tuple pattern; make sure we don't allow more...
+ if pat.is_rest() {
+ // ...but there was one again, so error.
+ self.ban_extra_rest_pat(pat.span, rest.unwrap().1, ctx);
+ } else {
+ elems.push(self.lower_pat_mut(pat));
+ }
+ }
+
+ (self.arena.alloc_from_iter(elems), rest.map(|(ddpos, _)| ddpos))
+ }
+
+ /// Lower a slice pattern of form `[pat_0, ..., pat_n]` into
+ /// `hir::PatKind::Slice(before, slice, after)`.
+ ///
+ /// When encountering `($binding_mode $ident @)? ..` (`slice`),
+ /// this is interpreted as a sub-slice pattern semantically.
+ /// Patterns that follow, which are not like `slice` -- or an error occurs, are in `after`.
+ fn lower_pat_slice(&mut self, pats: &[P<Pat>]) -> hir::PatKind<'hir> {
+ let mut before = Vec::new();
+ let mut after = Vec::new();
+ let mut slice = None;
+ let mut prev_rest_span = None;
+
+ // Lowers `$bm $ident @ ..` to `$bm $ident @ _`.
+ let lower_rest_sub = |this: &mut Self, pat, bm, ident, sub| {
+ let lower_sub = |this: &mut Self| Some(this.pat_wild_with_node_id_of(sub));
+ let node = this.lower_pat_ident(pat, bm, ident, lower_sub);
+ this.pat_with_node_id_of(pat, node)
+ };
+
+ let mut iter = pats.iter();
+ // Lower all the patterns until the first occurrence of a sub-slice pattern.
+ for pat in iter.by_ref() {
+ match pat.kind {
+ // Found a sub-slice pattern `..`. Record, lower it to `_`, and stop here.
+ PatKind::Rest => {
+ prev_rest_span = Some(pat.span);
+ slice = Some(self.pat_wild_with_node_id_of(pat));
+ break;
+ }
+ // Found a sub-slice pattern `$binding_mode $ident @ ..`.
+ // Record, lower it to `$binding_mode $ident @ _`, and stop here.
+ PatKind::Ident(ref bm, ident, Some(ref sub)) if sub.is_rest() => {
+ prev_rest_span = Some(sub.span);
+ slice = Some(self.arena.alloc(lower_rest_sub(self, pat, bm, ident, sub)));
+ break;
+ }
+ // It was not a subslice pattern so lower it normally.
+ _ => before.push(self.lower_pat_mut(pat)),
+ }
+ }
+
+ // Lower all the patterns after the first sub-slice pattern.
+ for pat in iter {
+ // There was a previous subslice pattern; make sure we don't allow more.
+ let rest_span = match pat.kind {
+ PatKind::Rest => Some(pat.span),
+ PatKind::Ident(ref bm, ident, Some(ref sub)) if sub.is_rest() => {
+ // #69103: Lower into `binding @ _` as above to avoid ICEs.
+ after.push(lower_rest_sub(self, pat, bm, ident, sub));
+ Some(sub.span)
+ }
+ _ => None,
+ };
+ if let Some(rest_span) = rest_span {
+ // We have e.g., `[a, .., b, ..]`. That's no good, error!
+ self.ban_extra_rest_pat(rest_span, prev_rest_span.unwrap(), "slice");
+ } else {
+ // Lower the pattern normally.
+ after.push(self.lower_pat_mut(pat));
+ }
+ }
+
+ hir::PatKind::Slice(
+ self.arena.alloc_from_iter(before),
+ slice,
+ self.arena.alloc_from_iter(after),
+ )
+ }
+
+ fn lower_pat_ident(
+ &mut self,
+ p: &Pat,
+ binding_mode: &BindingMode,
+ ident: Ident,
+ lower_sub: impl FnOnce(&mut Self) -> Option<&'hir hir::Pat<'hir>>,
+ ) -> hir::PatKind<'hir> {
+ match self.resolver.get_partial_res(p.id).map(|d| d.base_res()) {
+ // `None` can occur in body-less function signatures
+ res @ (None | Some(Res::Local(_))) => {
+ let canonical_id = match res {
+ Some(Res::Local(id)) => id,
+ _ => p.id,
+ };
+
+ hir::PatKind::Binding(
+ self.lower_binding_mode(binding_mode),
+ self.lower_node_id(canonical_id),
+ self.lower_ident(ident),
+ lower_sub(self),
+ )
+ }
+ Some(res) => hir::PatKind::Path(hir::QPath::Resolved(
+ None,
+ self.arena.alloc(hir::Path {
+ span: self.lower_span(ident.span),
+ res: self.lower_res(res),
+ segments: arena_vec![self; hir::PathSegment::from_ident(self.lower_ident(ident))],
+ }),
+ )),
+ }
+ }
+
+ fn lower_binding_mode(&mut self, b: &BindingMode) -> hir::BindingAnnotation {
+ match *b {
+ BindingMode::ByValue(Mutability::Not) => hir::BindingAnnotation::Unannotated,
+ BindingMode::ByRef(Mutability::Not) => hir::BindingAnnotation::Ref,
+ BindingMode::ByValue(Mutability::Mut) => hir::BindingAnnotation::Mutable,
+ BindingMode::ByRef(Mutability::Mut) => hir::BindingAnnotation::RefMut,
+ }
+ }
+
+ fn pat_wild_with_node_id_of(&mut self, p: &Pat) -> &'hir hir::Pat<'hir> {
+ self.arena.alloc(self.pat_with_node_id_of(p, hir::PatKind::Wild))
+ }
+
+ /// Construct a `Pat` with the `HirId` of `p.id` lowered.
+ fn pat_with_node_id_of(&mut self, p: &Pat, kind: hir::PatKind<'hir>) -> hir::Pat<'hir> {
+ hir::Pat {
+ hir_id: self.lower_node_id(p.id),
+ kind,
+ span: self.lower_span(p.span),
+ default_binding_modes: true,
+ }
+ }
+
+ /// Emit a friendly error for extra `..` patterns in a tuple/tuple struct/slice pattern.
+ pub(crate) fn ban_extra_rest_pat(&self, sp: Span, prev_sp: Span, ctx: &str) {
+ self.diagnostic()
+ .struct_span_err(sp, &format!("`..` can only be used once per {} pattern", ctx))
+ .span_label(sp, &format!("can only be used once per {} pattern", ctx))
+ .span_label(prev_sp, "previously used here")
+ .emit();
+ }
+
+ /// Used to ban the `..` pattern in places it shouldn't be semantically.
+ fn ban_illegal_rest_pat(&self, sp: Span) -> hir::PatKind<'hir> {
+ self.diagnostic()
+ .struct_span_err(sp, "`..` patterns are not allowed here")
+ .note("only allowed in tuple, tuple struct, and slice patterns")
+ .emit();
+
+ // We're not in a list context so `..` can be reasonably treated
+ // as `_` because it should always be valid and roughly matches the
+ // intent of `..` (notice that the rest of a single slot is that slot).
+ hir::PatKind::Wild
+ }
+
+ fn lower_range_end(&mut self, e: &RangeEnd, has_end: bool) -> hir::RangeEnd {
+ match *e {
+ RangeEnd::Excluded if has_end => hir::RangeEnd::Excluded,
+ // No end; so `X..` behaves like `RangeFrom`.
+ RangeEnd::Excluded | RangeEnd::Included(_) => hir::RangeEnd::Included,
+ }
+ }
+
+ /// Matches `'-' lit | lit (cf. parser::Parser::parse_literal_maybe_minus)`,
+ /// or paths for ranges.
+ //
+ // FIXME: do we want to allow `expr -> pattern` conversion to create path expressions?
+ // That means making this work:
+ //
+ // ```rust,ignore (FIXME)
+ // struct S;
+ // macro_rules! m {
+ // ($a:expr) => {
+ // let $a = S;
+ // }
+ // }
+ // m!(S);
+ // ```
+ fn lower_expr_within_pat(&mut self, expr: &Expr, allow_paths: bool) -> &'hir hir::Expr<'hir> {
+ match expr.kind {
+ ExprKind::Lit(..) | ExprKind::ConstBlock(..) | ExprKind::Err => {}
+ ExprKind::Path(..) if allow_paths => {}
+ ExprKind::Unary(UnOp::Neg, ref inner) if matches!(inner.kind, ExprKind::Lit(_)) => {}
+ _ => {
+ self.diagnostic()
+ .span_err(expr.span, "arbitrary expressions aren't allowed in patterns");
+ return self.arena.alloc(self.expr_err(expr.span));
+ }
+ }
+ self.lower_expr(expr)
+ }
+}
diff --git a/compiler/rustc_ast_lowering/src/path.rs b/compiler/rustc_ast_lowering/src/path.rs
new file mode 100644
index 000000000..393be3b45
--- /dev/null
+++ b/compiler/rustc_ast_lowering/src/path.rs
@@ -0,0 +1,406 @@
+use crate::ImplTraitPosition;
+
+use super::ResolverAstLoweringExt;
+use super::{GenericArgsCtor, LifetimeRes, ParenthesizedGenericArgs};
+use super::{ImplTraitContext, LoweringContext, ParamMode};
+
+use rustc_ast::{self as ast, *};
+use rustc_errors::{struct_span_err, Applicability};
+use rustc_hir as hir;
+use rustc_hir::def::{DefKind, PartialRes, Res};
+use rustc_hir::GenericArg;
+use rustc_span::symbol::{kw, Ident};
+use rustc_span::{BytePos, Span, DUMMY_SP};
+
+use smallvec::smallvec;
+use tracing::debug;
+
+impl<'a, 'hir> LoweringContext<'a, 'hir> {
+ #[instrument(level = "trace", skip(self))]
+ pub(crate) fn lower_qpath(
+ &mut self,
+ id: NodeId,
+ qself: &Option<QSelf>,
+ p: &Path,
+ param_mode: ParamMode,
+ itctx: ImplTraitContext,
+ ) -> hir::QPath<'hir> {
+ let qself_position = qself.as_ref().map(|q| q.position);
+ let qself = qself.as_ref().map(|q| self.lower_ty(&q.ty, itctx));
+
+ let partial_res =
+ self.resolver.get_partial_res(id).unwrap_or_else(|| PartialRes::new(Res::Err));
+
+ let path_span_lo = p.span.shrink_to_lo();
+ let proj_start = p.segments.len() - partial_res.unresolved_segments();
+ let path = self.arena.alloc(hir::Path {
+ res: self.lower_res(partial_res.base_res()),
+ segments: self.arena.alloc_from_iter(p.segments[..proj_start].iter().enumerate().map(
+ |(i, segment)| {
+ let param_mode = match (qself_position, param_mode) {
+ (Some(j), ParamMode::Optional) if i < j => {
+ // This segment is part of the trait path in a
+ // qualified path - one of `a`, `b` or `Trait`
+ // in `<X as a::b::Trait>::T::U::method`.
+ ParamMode::Explicit
+ }
+ _ => param_mode,
+ };
+
+ let parenthesized_generic_args = match partial_res.base_res() {
+ // `a::b::Trait(Args)`
+ Res::Def(DefKind::Trait, _) if i + 1 == proj_start => {
+ ParenthesizedGenericArgs::Ok
+ }
+ // `a::b::Trait(Args)::TraitItem`
+ Res::Def(DefKind::AssocFn, _)
+ | Res::Def(DefKind::AssocConst, _)
+ | Res::Def(DefKind::AssocTy, _)
+ if i + 2 == proj_start =>
+ {
+ ParenthesizedGenericArgs::Ok
+ }
+ // Avoid duplicated errors.
+ Res::Err => ParenthesizedGenericArgs::Ok,
+ // An error
+ _ => ParenthesizedGenericArgs::Err,
+ };
+
+ self.lower_path_segment(
+ p.span,
+ segment,
+ param_mode,
+ parenthesized_generic_args,
+ itctx,
+ )
+ },
+ )),
+ span: self.lower_span(
+ p.segments[..proj_start]
+ .last()
+ .map_or(path_span_lo, |segment| path_span_lo.to(segment.span())),
+ ),
+ });
+
+ // Simple case, either no projections, or only fully-qualified.
+ // E.g., `std::mem::size_of` or `<I as Iterator>::Item`.
+ if partial_res.unresolved_segments() == 0 {
+ return hir::QPath::Resolved(qself, path);
+ }
+
+ // Create the innermost type that we're projecting from.
+ let mut ty = if path.segments.is_empty() {
+ // If the base path is empty that means there exists a
+ // syntactical `Self`, e.g., `&i32` in `<&i32>::clone`.
+ qself.expect("missing QSelf for <T>::...")
+ } else {
+ // Otherwise, the base path is an implicit `Self` type path,
+ // e.g., `Vec` in `Vec::new` or `<I as Iterator>::Item` in
+ // `<I as Iterator>::Item::default`.
+ let new_id = self.next_id();
+ self.arena.alloc(self.ty_path(new_id, path.span, hir::QPath::Resolved(qself, path)))
+ };
+
+ // Anything after the base path are associated "extensions",
+ // out of which all but the last one are associated types,
+ // e.g., for `std::vec::Vec::<T>::IntoIter::Item::clone`:
+ // * base path is `std::vec::Vec<T>`
+ // * "extensions" are `IntoIter`, `Item` and `clone`
+ // * type nodes are:
+ // 1. `std::vec::Vec<T>` (created above)
+ // 2. `<std::vec::Vec<T>>::IntoIter`
+ // 3. `<<std::vec::Vec<T>>::IntoIter>::Item`
+ // * final path is `<<<std::vec::Vec<T>>::IntoIter>::Item>::clone`
+ for (i, segment) in p.segments.iter().enumerate().skip(proj_start) {
+ let hir_segment = self.arena.alloc(self.lower_path_segment(
+ p.span,
+ segment,
+ param_mode,
+ ParenthesizedGenericArgs::Err,
+ itctx,
+ ));
+ let qpath = hir::QPath::TypeRelative(ty, hir_segment);
+
+ // It's finished, return the extension of the right node type.
+ if i == p.segments.len() - 1 {
+ return qpath;
+ }
+
+ // Wrap the associated extension in another type node.
+ let new_id = self.next_id();
+ ty = self.arena.alloc(self.ty_path(new_id, path_span_lo.to(segment.span()), qpath));
+ }
+
+ // We should've returned in the for loop above.
+
+ self.diagnostic().span_bug(
+ p.span,
+ &format!(
+ "lower_qpath: no final extension segment in {}..{}",
+ proj_start,
+ p.segments.len()
+ ),
+ );
+ }
+
+ pub(crate) fn lower_path_extra(
+ &mut self,
+ res: Res,
+ p: &Path,
+ param_mode: ParamMode,
+ ) -> &'hir hir::Path<'hir> {
+ self.arena.alloc(hir::Path {
+ res,
+ segments: self.arena.alloc_from_iter(p.segments.iter().map(|segment| {
+ self.lower_path_segment(
+ p.span,
+ segment,
+ param_mode,
+ ParenthesizedGenericArgs::Err,
+ ImplTraitContext::Disallowed(ImplTraitPosition::Path),
+ )
+ })),
+ span: self.lower_span(p.span),
+ })
+ }
+
+ pub(crate) fn lower_path(
+ &mut self,
+ id: NodeId,
+ p: &Path,
+ param_mode: ParamMode,
+ ) -> &'hir hir::Path<'hir> {
+ let res = self.expect_full_res(id);
+ let res = self.lower_res(res);
+ self.lower_path_extra(res, p, param_mode)
+ }
+
+ pub(crate) fn lower_path_segment(
+ &mut self,
+ path_span: Span,
+ segment: &PathSegment,
+ param_mode: ParamMode,
+ parenthesized_generic_args: ParenthesizedGenericArgs,
+ itctx: ImplTraitContext,
+ ) -> hir::PathSegment<'hir> {
+ debug!("path_span: {:?}, lower_path_segment(segment: {:?})", path_span, segment,);
+ let (mut generic_args, infer_args) = if let Some(ref generic_args) = segment.args {
+ let msg = "parenthesized type parameters may only be used with a `Fn` trait";
+ match **generic_args {
+ GenericArgs::AngleBracketed(ref data) => {
+ self.lower_angle_bracketed_parameter_data(data, param_mode, itctx)
+ }
+ GenericArgs::Parenthesized(ref data) => match parenthesized_generic_args {
+ ParenthesizedGenericArgs::Ok => self.lower_parenthesized_parameter_data(data),
+ ParenthesizedGenericArgs::Err => {
+ let mut err = struct_span_err!(self.tcx.sess, data.span, E0214, "{}", msg);
+ err.span_label(data.span, "only `Fn` traits may use parentheses");
+ // Suggest replacing parentheses with angle brackets `Trait(params...)` to `Trait<params...>`
+ if !data.inputs.is_empty() {
+ // Start of the span to the 1st character of 1st argument
+ let open_param = data.inputs_span.shrink_to_lo().to(data
+ .inputs
+ .first()
+ .unwrap()
+ .span
+ .shrink_to_lo());
+ // Last character position of last argument to the end of the span
+ let close_param = data
+ .inputs
+ .last()
+ .unwrap()
+ .span
+ .shrink_to_hi()
+ .to(data.inputs_span.shrink_to_hi());
+ err.multipart_suggestion(
+ &format!("use angle brackets instead",),
+ vec![
+ (open_param, String::from("<")),
+ (close_param, String::from(">")),
+ ],
+ Applicability::MaybeIncorrect,
+ );
+ }
+ err.emit();
+ (
+ self.lower_angle_bracketed_parameter_data(
+ &data.as_angle_bracketed_args(),
+ param_mode,
+ itctx,
+ )
+ .0,
+ false,
+ )
+ }
+ },
+ }
+ } else {
+ (
+ GenericArgsCtor {
+ args: Default::default(),
+ bindings: &[],
+ parenthesized: false,
+ span: path_span.shrink_to_hi(),
+ },
+ param_mode == ParamMode::Optional,
+ )
+ };
+
+ let has_lifetimes =
+ generic_args.args.iter().any(|arg| matches!(arg, GenericArg::Lifetime(_)));
+ if !generic_args.parenthesized && !has_lifetimes {
+ self.maybe_insert_elided_lifetimes_in_path(
+ path_span,
+ segment.id,
+ segment.ident.span,
+ &mut generic_args,
+ );
+ }
+
+ let res = self.expect_full_res(segment.id);
+ let id = self.lower_node_id(segment.id);
+ debug!(
+ "lower_path_segment: ident={:?} original-id={:?} new-id={:?}",
+ segment.ident, segment.id, id,
+ );
+
+ hir::PathSegment {
+ ident: self.lower_ident(segment.ident),
+ hir_id: Some(id),
+ res: Some(self.lower_res(res)),
+ infer_args,
+ args: if generic_args.is_empty() && generic_args.span.is_empty() {
+ None
+ } else {
+ Some(generic_args.into_generic_args(self))
+ },
+ }
+ }
+
+ fn maybe_insert_elided_lifetimes_in_path(
+ &mut self,
+ path_span: Span,
+ segment_id: NodeId,
+ segment_ident_span: Span,
+ generic_args: &mut GenericArgsCtor<'hir>,
+ ) {
+ let (start, end) = match self.resolver.get_lifetime_res(segment_id) {
+ Some(LifetimeRes::ElidedAnchor { start, end }) => (start, end),
+ None => return,
+ Some(_) => panic!(),
+ };
+ let expected_lifetimes = end.as_usize() - start.as_usize();
+ debug!(expected_lifetimes);
+
+ // Note: these spans are used for diagnostics when they can't be inferred.
+ // See rustc_resolve::late::lifetimes::LifetimeContext::add_missing_lifetime_specifiers_label
+ let elided_lifetime_span = if generic_args.span.is_empty() {
+ // If there are no brackets, use the identifier span.
+ // HACK: we use find_ancestor_inside to properly suggest elided spans in paths
+ // originating from macros, since the segment's span might be from a macro arg.
+ segment_ident_span.find_ancestor_inside(path_span).unwrap_or(path_span)
+ } else if generic_args.is_empty() {
+ // If there are brackets, but not generic arguments, then use the opening bracket
+ generic_args.span.with_hi(generic_args.span.lo() + BytePos(1))
+ } else {
+ // Else use an empty span right after the opening bracket.
+ generic_args.span.with_lo(generic_args.span.lo() + BytePos(1)).shrink_to_lo()
+ };
+
+ generic_args.args.insert_many(
+ 0,
+ (start.as_u32()..end.as_u32()).map(|i| {
+ let id = NodeId::from_u32(i);
+ let l = self.lower_lifetime(&Lifetime {
+ id,
+ ident: Ident::new(kw::UnderscoreLifetime, elided_lifetime_span),
+ });
+ GenericArg::Lifetime(l)
+ }),
+ );
+ }
+
+ pub(crate) fn lower_angle_bracketed_parameter_data(
+ &mut self,
+ data: &AngleBracketedArgs,
+ param_mode: ParamMode,
+ itctx: ImplTraitContext,
+ ) -> (GenericArgsCtor<'hir>, bool) {
+ let has_non_lt_args = data.args.iter().any(|arg| match arg {
+ AngleBracketedArg::Arg(ast::GenericArg::Lifetime(_))
+ | AngleBracketedArg::Constraint(_) => false,
+ AngleBracketedArg::Arg(ast::GenericArg::Type(_) | ast::GenericArg::Const(_)) => true,
+ });
+ let args = data
+ .args
+ .iter()
+ .filter_map(|arg| match arg {
+ AngleBracketedArg::Arg(arg) => Some(self.lower_generic_arg(arg, itctx)),
+ AngleBracketedArg::Constraint(_) => None,
+ })
+ .collect();
+ let bindings = self.arena.alloc_from_iter(data.args.iter().filter_map(|arg| match arg {
+ AngleBracketedArg::Constraint(c) => Some(self.lower_assoc_ty_constraint(c, itctx)),
+ AngleBracketedArg::Arg(_) => None,
+ }));
+ let ctor = GenericArgsCtor { args, bindings, parenthesized: false, span: data.span };
+ (ctor, !has_non_lt_args && param_mode == ParamMode::Optional)
+ }
+
+ fn lower_parenthesized_parameter_data(
+ &mut self,
+ data: &ParenthesizedArgs,
+ ) -> (GenericArgsCtor<'hir>, bool) {
+ // Switch to `PassThrough` mode for anonymous lifetimes; this
+ // means that we permit things like `&Ref<T>`, where `Ref` has
+ // a hidden lifetime parameter. This is needed for backwards
+ // compatibility, even in contexts like an impl header where
+ // we generally don't permit such things (see #51008).
+ let ParenthesizedArgs { span, inputs, inputs_span, output } = data;
+ let inputs = self.arena.alloc_from_iter(inputs.iter().map(|ty| {
+ self.lower_ty_direct(ty, ImplTraitContext::Disallowed(ImplTraitPosition::FnTraitParam))
+ }));
+ let output_ty = match output {
+ FnRetTy::Ty(ty) => {
+ self.lower_ty(&ty, ImplTraitContext::Disallowed(ImplTraitPosition::FnTraitReturn))
+ }
+ FnRetTy::Default(_) => self.arena.alloc(self.ty_tup(*span, &[])),
+ };
+ let args = smallvec![GenericArg::Type(self.ty_tup(*inputs_span, inputs))];
+ let binding = self.output_ty_binding(output_ty.span, output_ty);
+ (
+ GenericArgsCtor {
+ args,
+ bindings: arena_vec![self; binding],
+ parenthesized: true,
+ span: data.inputs_span,
+ },
+ false,
+ )
+ }
+
+ /// An associated type binding `Output = $ty`.
+ pub(crate) fn output_ty_binding(
+ &mut self,
+ span: Span,
+ ty: &'hir hir::Ty<'hir>,
+ ) -> hir::TypeBinding<'hir> {
+ let ident = Ident::with_dummy_span(hir::FN_OUTPUT_NAME);
+ let kind = hir::TypeBindingKind::Equality { term: ty.into() };
+ let args = arena_vec![self;];
+ let bindings = arena_vec![self;];
+ let gen_args = self.arena.alloc(hir::GenericArgs {
+ args,
+ bindings,
+ parenthesized: false,
+ span_ext: DUMMY_SP,
+ });
+ hir::TypeBinding {
+ hir_id: self.next_id(),
+ gen_args,
+ span: self.lower_span(span),
+ ident,
+ kind,
+ }
+ }
+}
diff --git a/compiler/rustc_ast_passes/Cargo.toml b/compiler/rustc_ast_passes/Cargo.toml
new file mode 100644
index 000000000..22742b2ad
--- /dev/null
+++ b/compiler/rustc_ast_passes/Cargo.toml
@@ -0,0 +1,18 @@
+[package]
+name = "rustc_ast_passes"
+version = "0.0.0"
+edition = "2021"
+
+[dependencies]
+itertools = "0.10.1"
+tracing = "0.1"
+rustc_ast_pretty = { path = "../rustc_ast_pretty" }
+rustc_attr = { path = "../rustc_attr" }
+rustc_data_structures = { path = "../rustc_data_structures" }
+rustc_errors = { path = "../rustc_errors" }
+rustc_feature = { path = "../rustc_feature" }
+rustc_parse = { path = "../rustc_parse" }
+rustc_session = { path = "../rustc_session" }
+rustc_span = { path = "../rustc_span" }
+rustc_target = { path = "../rustc_target" }
+rustc_ast = { path = "../rustc_ast" }
diff --git a/compiler/rustc_ast_passes/src/ast_validation.rs b/compiler/rustc_ast_passes/src/ast_validation.rs
new file mode 100644
index 000000000..2d9d0073f
--- /dev/null
+++ b/compiler/rustc_ast_passes/src/ast_validation.rs
@@ -0,0 +1,1909 @@
+// Validate AST before lowering it to HIR.
+//
+// This pass is supposed to catch things that fit into AST data structures,
+// but not permitted by the language. It runs after expansion when AST is frozen,
+// so it can check for erroneous constructions produced by syntax extensions.
+// This pass is supposed to perform only simple checks not requiring name resolution
+// or type checking or some other kind of complex analysis.
+
+use itertools::{Either, Itertools};
+use rustc_ast::ptr::P;
+use rustc_ast::visit::{self, AssocCtxt, BoundKind, FnCtxt, FnKind, Visitor};
+use rustc_ast::walk_list;
+use rustc_ast::*;
+use rustc_ast_pretty::pprust::{self, State};
+use rustc_data_structures::fx::FxHashMap;
+use rustc_errors::{
+ error_code, pluralize, struct_span_err, Applicability, DiagnosticBuilder, ErrorGuaranteed,
+};
+use rustc_parse::validate_attr;
+use rustc_session::lint::builtin::{
+ DEPRECATED_WHERE_CLAUSE_LOCATION, MISSING_ABI, PATTERNS_IN_FNS_WITHOUT_BODY,
+};
+use rustc_session::lint::{BuiltinLintDiagnostics, LintBuffer};
+use rustc_session::Session;
+use rustc_span::source_map::Spanned;
+use rustc_span::symbol::{kw, sym, Ident};
+use rustc_span::Span;
+use rustc_target::spec::abi;
+use std::mem;
+use std::ops::{Deref, DerefMut};
+
+const MORE_EXTERN: &str =
+ "for more information, visit https://doc.rust-lang.org/std/keyword.extern.html";
+
+/// Is `self` allowed semantically as the first parameter in an `FnDecl`?
+enum SelfSemantic {
+ Yes,
+ No,
+}
+
+struct AstValidator<'a> {
+ session: &'a Session,
+
+ /// The span of the `extern` in an `extern { ... }` block, if any.
+ extern_mod: Option<&'a Item>,
+
+ /// Are we inside a trait impl?
+ in_trait_impl: bool,
+
+ in_const_trait_impl: bool,
+
+ has_proc_macro_decls: bool,
+
+ /// Used to ban nested `impl Trait`, e.g., `impl Into<impl Debug>`.
+ /// Nested `impl Trait` _is_ allowed in associated type position,
+ /// e.g., `impl Iterator<Item = impl Debug>`.
+ outer_impl_trait: Option<Span>,
+
+ is_tilde_const_allowed: bool,
+
+ /// Used to ban `impl Trait` in path projections like `<impl Iterator>::Item`
+ /// or `Foo::Bar<impl Trait>`
+ is_impl_trait_banned: bool,
+
+ /// Used to ban associated type bounds (i.e., `Type<AssocType: Bounds>`) in
+ /// certain positions.
+ is_assoc_ty_bound_banned: bool,
+
+ /// See [ForbiddenLetReason]
+ forbidden_let_reason: Option<ForbiddenLetReason>,
+
+ lint_buffer: &'a mut LintBuffer,
+}
+
+impl<'a> AstValidator<'a> {
+ fn with_in_trait_impl(
+ &mut self,
+ is_in: bool,
+ constness: Option<Const>,
+ f: impl FnOnce(&mut Self),
+ ) {
+ let old = mem::replace(&mut self.in_trait_impl, is_in);
+ let old_const =
+ mem::replace(&mut self.in_const_trait_impl, matches!(constness, Some(Const::Yes(_))));
+ f(self);
+ self.in_trait_impl = old;
+ self.in_const_trait_impl = old_const;
+ }
+
+ fn with_banned_impl_trait(&mut self, f: impl FnOnce(&mut Self)) {
+ let old = mem::replace(&mut self.is_impl_trait_banned, true);
+ f(self);
+ self.is_impl_trait_banned = old;
+ }
+
+ fn with_tilde_const(&mut self, allowed: bool, f: impl FnOnce(&mut Self)) {
+ let old = mem::replace(&mut self.is_tilde_const_allowed, allowed);
+ f(self);
+ self.is_tilde_const_allowed = old;
+ }
+
+ fn with_tilde_const_allowed(&mut self, f: impl FnOnce(&mut Self)) {
+ self.with_tilde_const(true, f)
+ }
+
+ fn with_banned_tilde_const(&mut self, f: impl FnOnce(&mut Self)) {
+ self.with_tilde_const(false, f)
+ }
+
+ fn with_let_management(
+ &mut self,
+ forbidden_let_reason: Option<ForbiddenLetReason>,
+ f: impl FnOnce(&mut Self, Option<ForbiddenLetReason>),
+ ) {
+ let old = mem::replace(&mut self.forbidden_let_reason, forbidden_let_reason);
+ f(self, old);
+ self.forbidden_let_reason = old;
+ }
+
+ /// Emits an error banning the `let` expression provided in the given location.
+ fn ban_let_expr(&self, expr: &'a Expr, forbidden_let_reason: ForbiddenLetReason) {
+ let sess = &self.session;
+ if sess.opts.unstable_features.is_nightly_build() {
+ let err = "`let` expressions are not supported here";
+ let mut diag = sess.struct_span_err(expr.span, err);
+ diag.note("only supported directly in conditions of `if` and `while` expressions");
+ match forbidden_let_reason {
+ ForbiddenLetReason::GenericForbidden => {}
+ ForbiddenLetReason::NotSupportedOr(span) => {
+ diag.span_note(
+ span,
+ "`||` operators are not supported in let chain expressions",
+ );
+ }
+ ForbiddenLetReason::NotSupportedParentheses(span) => {
+ diag.span_note(
+ span,
+ "`let`s wrapped in parentheses are not supported in a context with let \
+ chains",
+ );
+ }
+ }
+ diag.emit();
+ } else {
+ sess.struct_span_err(expr.span, "expected expression, found statement (`let`)")
+ .note("variable declaration using `let` is a statement")
+ .emit();
+ }
+ }
+
+ fn check_gat_where(
+ &mut self,
+ id: NodeId,
+ before_predicates: &[WherePredicate],
+ where_clauses: (ast::TyAliasWhereClause, ast::TyAliasWhereClause),
+ ) {
+ if !before_predicates.is_empty() {
+ let mut state = State::new();
+ if !where_clauses.1.0 {
+ state.space();
+ state.word_space("where");
+ } else {
+ state.word_space(",");
+ }
+ let mut first = true;
+ for p in before_predicates.iter() {
+ if !first {
+ state.word_space(",");
+ }
+ first = false;
+ state.print_where_predicate(p);
+ }
+ let suggestion = state.s.eof();
+ self.lint_buffer.buffer_lint_with_diagnostic(
+ DEPRECATED_WHERE_CLAUSE_LOCATION,
+ id,
+ where_clauses.0.1,
+ "where clause not allowed here",
+ BuiltinLintDiagnostics::DeprecatedWhereclauseLocation(
+ where_clauses.1.1.shrink_to_hi(),
+ suggestion,
+ ),
+ );
+ }
+ }
+
+ fn with_banned_assoc_ty_bound(&mut self, f: impl FnOnce(&mut Self)) {
+ let old = mem::replace(&mut self.is_assoc_ty_bound_banned, true);
+ f(self);
+ self.is_assoc_ty_bound_banned = old;
+ }
+
+ fn with_impl_trait(&mut self, outer: Option<Span>, f: impl FnOnce(&mut Self)) {
+ let old = mem::replace(&mut self.outer_impl_trait, outer);
+ if outer.is_some() {
+ self.with_banned_tilde_const(f);
+ } else {
+ f(self);
+ }
+ self.outer_impl_trait = old;
+ }
+
+ fn visit_assoc_constraint_from_generic_args(&mut self, constraint: &'a AssocConstraint) {
+ match constraint.kind {
+ AssocConstraintKind::Equality { .. } => {}
+ AssocConstraintKind::Bound { .. } => {
+ if self.is_assoc_ty_bound_banned {
+ self.err_handler().span_err(
+ constraint.span,
+ "associated type bounds are not allowed within structs, enums, or unions",
+ );
+ }
+ }
+ }
+ self.visit_assoc_constraint(constraint);
+ }
+
+ // Mirrors `visit::walk_ty`, but tracks relevant state.
+ fn walk_ty(&mut self, t: &'a Ty) {
+ match t.kind {
+ TyKind::ImplTrait(..) => {
+ self.with_impl_trait(Some(t.span), |this| visit::walk_ty(this, t))
+ }
+ TyKind::TraitObject(..) => self.with_banned_tilde_const(|this| visit::walk_ty(this, t)),
+ TyKind::Path(ref qself, ref path) => {
+ // We allow these:
+ // - `Option<impl Trait>`
+ // - `option::Option<impl Trait>`
+ // - `option::Option<T>::Foo<impl Trait>
+ //
+ // But not these:
+ // - `<impl Trait>::Foo`
+ // - `option::Option<impl Trait>::Foo`.
+ //
+ // To implement this, we disallow `impl Trait` from `qself`
+ // (for cases like `<impl Trait>::Foo>`)
+ // but we allow `impl Trait` in `GenericArgs`
+ // iff there are no more PathSegments.
+ if let Some(ref qself) = *qself {
+ // `impl Trait` in `qself` is always illegal
+ self.with_banned_impl_trait(|this| this.visit_ty(&qself.ty));
+ }
+
+ // Note that there should be a call to visit_path here,
+ // so if any logic is added to process `Path`s a call to it should be
+ // added both in visit_path and here. This code mirrors visit::walk_path.
+ for (i, segment) in path.segments.iter().enumerate() {
+ // Allow `impl Trait` iff we're on the final path segment
+ if i == path.segments.len() - 1 {
+ self.visit_path_segment(path.span, segment);
+ } else {
+ self.with_banned_impl_trait(|this| {
+ this.visit_path_segment(path.span, segment)
+ });
+ }
+ }
+ }
+ _ => visit::walk_ty(self, t),
+ }
+ }
+
+ fn visit_struct_field_def(&mut self, field: &'a FieldDef) {
+ if let Some(ident) = field.ident {
+ if ident.name == kw::Underscore {
+ self.visit_vis(&field.vis);
+ self.visit_ident(ident);
+ self.visit_ty_common(&field.ty);
+ self.walk_ty(&field.ty);
+ walk_list!(self, visit_attribute, &field.attrs);
+ return;
+ }
+ }
+ self.visit_field_def(field);
+ }
+
+ fn err_handler(&self) -> &rustc_errors::Handler {
+ &self.session.diagnostic()
+ }
+
+ fn check_lifetime(&self, ident: Ident) {
+ let valid_names = [kw::UnderscoreLifetime, kw::StaticLifetime, kw::Empty];
+ if !valid_names.contains(&ident.name) && ident.without_first_quote().is_reserved() {
+ self.err_handler().span_err(ident.span, "lifetimes cannot use keyword names");
+ }
+ }
+
+ fn check_label(&self, ident: Ident) {
+ if ident.without_first_quote().is_reserved() {
+ self.err_handler()
+ .span_err(ident.span, &format!("invalid label name `{}`", ident.name));
+ }
+ }
+
+ fn invalid_visibility(&self, vis: &Visibility, note: Option<&str>) {
+ if let VisibilityKind::Inherited = vis.kind {
+ return;
+ }
+
+ let mut err =
+ struct_span_err!(self.session, vis.span, E0449, "unnecessary visibility qualifier");
+ if vis.kind.is_pub() {
+ err.span_label(vis.span, "`pub` not permitted here because it's implied");
+ }
+ if let Some(note) = note {
+ err.note(note);
+ }
+ err.emit();
+ }
+
+ fn check_decl_no_pat(decl: &FnDecl, mut report_err: impl FnMut(Span, Option<Ident>, bool)) {
+ for Param { pat, .. } in &decl.inputs {
+ match pat.kind {
+ PatKind::Ident(BindingMode::ByValue(Mutability::Not), _, None) | PatKind::Wild => {}
+ PatKind::Ident(BindingMode::ByValue(Mutability::Mut), ident, None) => {
+ report_err(pat.span, Some(ident), true)
+ }
+ _ => report_err(pat.span, None, false),
+ }
+ }
+ }
+
+ fn check_trait_fn_not_async(&self, fn_span: Span, asyncness: Async) {
+ if let Async::Yes { span, .. } = asyncness {
+ struct_span_err!(
+ self.session,
+ fn_span,
+ E0706,
+ "functions in traits cannot be declared `async`"
+ )
+ .span_label(span, "`async` because of this")
+ .note("`async` trait functions are not currently supported")
+ .note("consider using the `async-trait` crate: https://crates.io/crates/async-trait")
+ .emit();
+ }
+ }
+
+ fn check_trait_fn_not_const(&self, constness: Const) {
+ if let Const::Yes(span) = constness {
+ struct_span_err!(
+ self.session,
+ span,
+ E0379,
+ "functions in traits cannot be declared const"
+ )
+ .span_label(span, "functions in traits cannot be const")
+ .emit();
+ }
+ }
+
+ fn check_late_bound_lifetime_defs(&self, params: &[GenericParam]) {
+ // Check only lifetime parameters are present and that the lifetime
+ // parameters that are present have no bounds.
+ let non_lt_param_spans: Vec<_> = params
+ .iter()
+ .filter_map(|param| match param.kind {
+ GenericParamKind::Lifetime { .. } => {
+ if !param.bounds.is_empty() {
+ let spans: Vec<_> = param.bounds.iter().map(|b| b.span()).collect();
+ self.err_handler()
+ .span_err(spans, "lifetime bounds cannot be used in this context");
+ }
+ None
+ }
+ _ => Some(param.ident.span),
+ })
+ .collect();
+ if !non_lt_param_spans.is_empty() {
+ self.err_handler().span_err(
+ non_lt_param_spans,
+ "only lifetime parameters can be used in this context",
+ );
+ }
+ }
+
+ fn check_fn_decl(&self, fn_decl: &FnDecl, self_semantic: SelfSemantic) {
+ self.check_decl_num_args(fn_decl);
+ self.check_decl_cvaradic_pos(fn_decl);
+ self.check_decl_attrs(fn_decl);
+ self.check_decl_self_param(fn_decl, self_semantic);
+ }
+
+ /// Emits fatal error if function declaration has more than `u16::MAX` arguments
+ /// Error is fatal to prevent errors during typechecking
+ fn check_decl_num_args(&self, fn_decl: &FnDecl) {
+ let max_num_args: usize = u16::MAX.into();
+ if fn_decl.inputs.len() > max_num_args {
+ let Param { span, .. } = fn_decl.inputs[0];
+ self.err_handler().span_fatal(
+ span,
+ &format!("function can not have more than {} arguments", max_num_args),
+ );
+ }
+ }
+
+ fn check_decl_cvaradic_pos(&self, fn_decl: &FnDecl) {
+ match &*fn_decl.inputs {
+ [Param { ty, span, .. }] => {
+ if let TyKind::CVarArgs = ty.kind {
+ self.err_handler().span_err(
+ *span,
+ "C-variadic function must be declared with at least one named argument",
+ );
+ }
+ }
+ [ps @ .., _] => {
+ for Param { ty, span, .. } in ps {
+ if let TyKind::CVarArgs = ty.kind {
+ self.err_handler().span_err(
+ *span,
+ "`...` must be the last argument of a C-variadic function",
+ );
+ }
+ }
+ }
+ _ => {}
+ }
+ }
+
+ fn check_decl_attrs(&self, fn_decl: &FnDecl) {
+ fn_decl
+ .inputs
+ .iter()
+ .flat_map(|i| i.attrs.as_ref())
+ .filter(|attr| {
+ let arr = [
+ sym::allow,
+ sym::cfg,
+ sym::cfg_attr,
+ sym::deny,
+ sym::expect,
+ sym::forbid,
+ sym::warn,
+ ];
+ !arr.contains(&attr.name_or_empty()) && rustc_attr::is_builtin_attr(attr)
+ })
+ .for_each(|attr| {
+ if attr.is_doc_comment() {
+ self.err_handler()
+ .struct_span_err(
+ attr.span,
+ "documentation comments cannot be applied to function parameters",
+ )
+ .span_label(attr.span, "doc comments are not allowed here")
+ .emit();
+ } else {
+ self.err_handler().span_err(
+ attr.span,
+ "allow, cfg, cfg_attr, deny, expect, \
+ forbid, and warn are the only allowed built-in attributes in function parameters",
+ );
+ }
+ });
+ }
+
+ fn check_decl_self_param(&self, fn_decl: &FnDecl, self_semantic: SelfSemantic) {
+ if let (SelfSemantic::No, [param, ..]) = (self_semantic, &*fn_decl.inputs) {
+ if param.is_self() {
+ self.err_handler()
+ .struct_span_err(
+ param.span,
+ "`self` parameter is only allowed in associated functions",
+ )
+ .span_label(param.span, "not semantically valid as function parameter")
+ .note("associated functions are those in `impl` or `trait` definitions")
+ .emit();
+ }
+ }
+ }
+
+ fn check_defaultness(&self, span: Span, defaultness: Defaultness) {
+ if let Defaultness::Default(def_span) = defaultness {
+ let span = self.session.source_map().guess_head_span(span);
+ self.err_handler()
+ .struct_span_err(span, "`default` is only allowed on items in trait impls")
+ .span_label(def_span, "`default` because of this")
+ .emit();
+ }
+ }
+
+ fn error_item_without_body(&self, sp: Span, ctx: &str, msg: &str, sugg: &str) {
+ self.error_item_without_body_with_help(sp, ctx, msg, sugg, |_| ());
+ }
+
+ fn error_item_without_body_with_help(
+ &self,
+ sp: Span,
+ ctx: &str,
+ msg: &str,
+ sugg: &str,
+ help: impl FnOnce(&mut DiagnosticBuilder<'_, ErrorGuaranteed>),
+ ) {
+ let source_map = self.session.source_map();
+ let end = source_map.end_point(sp);
+ let replace_span = if source_map.span_to_snippet(end).map(|s| s == ";").unwrap_or(false) {
+ end
+ } else {
+ sp.shrink_to_hi()
+ };
+ let mut err = self.err_handler().struct_span_err(sp, msg);
+ err.span_suggestion(
+ replace_span,
+ &format!("provide a definition for the {}", ctx),
+ sugg,
+ Applicability::HasPlaceholders,
+ );
+ help(&mut err);
+ err.emit();
+ }
+
+ fn check_impl_item_provided<T>(&self, sp: Span, body: &Option<T>, ctx: &str, sugg: &str) {
+ if body.is_none() {
+ let msg = format!("associated {} in `impl` without body", ctx);
+ self.error_item_without_body(sp, ctx, &msg, sugg);
+ }
+ }
+
+ fn check_type_no_bounds(&self, bounds: &[GenericBound], ctx: &str) {
+ let span = match bounds {
+ [] => return,
+ [b0] => b0.span(),
+ [b0, .., bl] => b0.span().to(bl.span()),
+ };
+ self.err_handler()
+ .struct_span_err(span, &format!("bounds on `type`s in {} have no effect", ctx))
+ .emit();
+ }
+
+ fn check_foreign_ty_genericless(&self, generics: &Generics, where_span: Span) {
+ let cannot_have = |span, descr, remove_descr| {
+ self.err_handler()
+ .struct_span_err(
+ span,
+ &format!("`type`s inside `extern` blocks cannot have {}", descr),
+ )
+ .span_suggestion(
+ span,
+ &format!("remove the {}", remove_descr),
+ "",
+ Applicability::MaybeIncorrect,
+ )
+ .span_label(self.current_extern_span(), "`extern` block begins here")
+ .note(MORE_EXTERN)
+ .emit();
+ };
+
+ if !generics.params.is_empty() {
+ cannot_have(generics.span, "generic parameters", "generic parameters");
+ }
+
+ if !generics.where_clause.predicates.is_empty() {
+ cannot_have(where_span, "`where` clauses", "`where` clause");
+ }
+ }
+
+ fn check_foreign_kind_bodyless(&self, ident: Ident, kind: &str, body: Option<Span>) {
+ let Some(body) = body else {
+ return;
+ };
+ self.err_handler()
+ .struct_span_err(ident.span, &format!("incorrect `{}` inside `extern` block", kind))
+ .span_label(ident.span, "cannot have a body")
+ .span_label(body, "the invalid body")
+ .span_label(
+ self.current_extern_span(),
+ format!(
+ "`extern` blocks define existing foreign {0}s and {0}s \
+ inside of them cannot have a body",
+ kind
+ ),
+ )
+ .note(MORE_EXTERN)
+ .emit();
+ }
+
+ /// An `fn` in `extern { ... }` cannot have a body `{ ... }`.
+ fn check_foreign_fn_bodyless(&self, ident: Ident, body: Option<&Block>) {
+ let Some(body) = body else {
+ return;
+ };
+ self.err_handler()
+ .struct_span_err(ident.span, "incorrect function inside `extern` block")
+ .span_label(ident.span, "cannot have a body")
+ .span_suggestion(
+ body.span,
+ "remove the invalid body",
+ ";",
+ Applicability::MaybeIncorrect,
+ )
+ .help(
+ "you might have meant to write a function accessible through FFI, \
+ which can be done by writing `extern fn` outside of the `extern` block",
+ )
+ .span_label(
+ self.current_extern_span(),
+ "`extern` blocks define existing foreign functions and functions \
+ inside of them cannot have a body",
+ )
+ .note(MORE_EXTERN)
+ .emit();
+ }
+
+ fn current_extern_span(&self) -> Span {
+ self.session.source_map().guess_head_span(self.extern_mod.unwrap().span)
+ }
+
+ /// An `fn` in `extern { ... }` cannot have qualifiers, e.g. `async fn`.
+ fn check_foreign_fn_headerless(&self, ident: Ident, span: Span, header: FnHeader) {
+ if header.has_qualifiers() {
+ self.err_handler()
+ .struct_span_err(ident.span, "functions in `extern` blocks cannot have qualifiers")
+ .span_label(self.current_extern_span(), "in this `extern` block")
+ .span_suggestion_verbose(
+ span.until(ident.span.shrink_to_lo()),
+ "remove the qualifiers",
+ "fn ",
+ Applicability::MaybeIncorrect,
+ )
+ .emit();
+ }
+ }
+
+ /// An item in `extern { ... }` cannot use non-ascii identifier.
+ fn check_foreign_item_ascii_only(&self, ident: Ident) {
+ if !ident.as_str().is_ascii() {
+ let n = 83942;
+ self.err_handler()
+ .struct_span_err(
+ ident.span,
+ "items in `extern` blocks cannot use non-ascii identifiers",
+ )
+ .span_label(self.current_extern_span(), "in this `extern` block")
+ .note(&format!(
+ "this limitation may be lifted in the future; see issue #{} <https://github.com/rust-lang/rust/issues/{}> for more information",
+ n, n,
+ ))
+ .emit();
+ }
+ }
+
+ /// Reject C-variadic type unless the function is foreign,
+ /// or free and `unsafe extern "C"` semantically.
+ fn check_c_variadic_type(&self, fk: FnKind<'a>) {
+ match (fk.ctxt(), fk.header()) {
+ (Some(FnCtxt::Foreign), _) => return,
+ (Some(FnCtxt::Free), Some(header)) => match header.ext {
+ Extern::Explicit(StrLit { symbol_unescaped: sym::C, .. }, _)
+ | Extern::Implicit(_)
+ if matches!(header.unsafety, Unsafe::Yes(_)) =>
+ {
+ return;
+ }
+ _ => {}
+ },
+ _ => {}
+ };
+
+ for Param { ty, span, .. } in &fk.decl().inputs {
+ if let TyKind::CVarArgs = ty.kind {
+ self.err_handler()
+ .struct_span_err(
+ *span,
+ "only foreign or `unsafe extern \"C\"` functions may be C-variadic",
+ )
+ .emit();
+ }
+ }
+ }
+
+ fn check_item_named(&self, ident: Ident, kind: &str) {
+ if ident.name != kw::Underscore {
+ return;
+ }
+ self.err_handler()
+ .struct_span_err(ident.span, &format!("`{}` items in this context need a name", kind))
+ .span_label(ident.span, format!("`_` is not a valid name for this `{}` item", kind))
+ .emit();
+ }
+
+ fn check_nomangle_item_asciionly(&self, ident: Ident, item_span: Span) {
+ if ident.name.as_str().is_ascii() {
+ return;
+ }
+ let head_span = self.session.source_map().guess_head_span(item_span);
+ struct_span_err!(
+ self.session,
+ head_span,
+ E0754,
+ "`#[no_mangle]` requires ASCII identifier"
+ )
+ .emit();
+ }
+
+ fn check_mod_file_item_asciionly(&self, ident: Ident) {
+ if ident.name.as_str().is_ascii() {
+ return;
+ }
+ struct_span_err!(
+ self.session,
+ ident.span,
+ E0754,
+ "trying to load file for module `{}` with non-ascii identifier name",
+ ident.name
+ )
+ .help("consider using `#[path]` attribute to specify filesystem path")
+ .emit();
+ }
+
+ fn deny_generic_params(&self, generics: &Generics, ident_span: Span) {
+ if !generics.params.is_empty() {
+ struct_span_err!(
+ self.session,
+ generics.span,
+ E0567,
+ "auto traits cannot have generic parameters"
+ )
+ .span_label(ident_span, "auto trait cannot have generic parameters")
+ .span_suggestion(
+ generics.span,
+ "remove the parameters",
+ "",
+ Applicability::MachineApplicable,
+ )
+ .emit();
+ }
+ }
+
+ fn emit_e0568(&self, span: Span, ident_span: Span) {
+ struct_span_err!(
+ self.session,
+ span,
+ E0568,
+ "auto traits cannot have super traits or lifetime bounds"
+ )
+ .span_label(ident_span, "auto trait cannot have super traits or lifetime bounds")
+ .span_suggestion(
+ span,
+ "remove the super traits or lifetime bounds",
+ "",
+ Applicability::MachineApplicable,
+ )
+ .emit();
+ }
+
+ fn deny_super_traits(&self, bounds: &GenericBounds, ident_span: Span) {
+ if let [.., last] = &bounds[..] {
+ let span = ident_span.shrink_to_hi().to(last.span());
+ self.emit_e0568(span, ident_span);
+ }
+ }
+
+ fn deny_where_clause(&self, where_clause: &WhereClause, ident_span: Span) {
+ if !where_clause.predicates.is_empty() {
+ self.emit_e0568(where_clause.span, ident_span);
+ }
+ }
+
+ fn deny_items(&self, trait_items: &[P<AssocItem>], ident_span: Span) {
+ if !trait_items.is_empty() {
+ let spans: Vec<_> = trait_items.iter().map(|i| i.ident.span).collect();
+ let total_span = trait_items.first().unwrap().span.to(trait_items.last().unwrap().span);
+ struct_span_err!(
+ self.session,
+ spans,
+ E0380,
+ "auto traits cannot have associated items"
+ )
+ .span_suggestion(
+ total_span,
+ "remove these associated items",
+ "",
+ Applicability::MachineApplicable,
+ )
+ .span_label(ident_span, "auto trait cannot have associated items")
+ .emit();
+ }
+ }
+
+ fn correct_generic_order_suggestion(&self, data: &AngleBracketedArgs) -> String {
+ // Lifetimes always come first.
+ let lt_sugg = data.args.iter().filter_map(|arg| match arg {
+ AngleBracketedArg::Arg(lt @ GenericArg::Lifetime(_)) => {
+ Some(pprust::to_string(|s| s.print_generic_arg(lt)))
+ }
+ _ => None,
+ });
+ let args_sugg = data.args.iter().filter_map(|a| match a {
+ AngleBracketedArg::Arg(GenericArg::Lifetime(_)) | AngleBracketedArg::Constraint(_) => {
+ None
+ }
+ AngleBracketedArg::Arg(arg) => Some(pprust::to_string(|s| s.print_generic_arg(arg))),
+ });
+ // Constraints always come last.
+ let constraint_sugg = data.args.iter().filter_map(|a| match a {
+ AngleBracketedArg::Arg(_) => None,
+ AngleBracketedArg::Constraint(c) => {
+ Some(pprust::to_string(|s| s.print_assoc_constraint(c)))
+ }
+ });
+ format!(
+ "<{}>",
+ lt_sugg.chain(args_sugg).chain(constraint_sugg).collect::<Vec<String>>().join(", ")
+ )
+ }
+
+ /// Enforce generic args coming before constraints in `<...>` of a path segment.
+ fn check_generic_args_before_constraints(&self, data: &AngleBracketedArgs) {
+ // Early exit in case it's partitioned as it should be.
+ if data.args.iter().is_partitioned(|arg| matches!(arg, AngleBracketedArg::Arg(_))) {
+ return;
+ }
+ // Find all generic argument coming after the first constraint...
+ let (constraint_spans, arg_spans): (Vec<Span>, Vec<Span>) =
+ data.args.iter().partition_map(|arg| match arg {
+ AngleBracketedArg::Constraint(c) => Either::Left(c.span),
+ AngleBracketedArg::Arg(a) => Either::Right(a.span()),
+ });
+ let args_len = arg_spans.len();
+ let constraint_len = constraint_spans.len();
+ // ...and then error:
+ self.err_handler()
+ .struct_span_err(
+ arg_spans.clone(),
+ "generic arguments must come before the first constraint",
+ )
+ .span_label(constraint_spans[0], &format!("constraint{}", pluralize!(constraint_len)))
+ .span_label(
+ *arg_spans.iter().last().unwrap(),
+ &format!("generic argument{}", pluralize!(args_len)),
+ )
+ .span_labels(constraint_spans, "")
+ .span_labels(arg_spans, "")
+ .span_suggestion_verbose(
+ data.span,
+ &format!(
+ "move the constraint{} after the generic argument{}",
+ pluralize!(constraint_len),
+ pluralize!(args_len)
+ ),
+ self.correct_generic_order_suggestion(&data),
+ Applicability::MachineApplicable,
+ )
+ .emit();
+ }
+
+ fn visit_ty_common(&mut self, ty: &'a Ty) {
+ match ty.kind {
+ TyKind::BareFn(ref bfty) => {
+ self.check_fn_decl(&bfty.decl, SelfSemantic::No);
+ Self::check_decl_no_pat(&bfty.decl, |span, _, _| {
+ struct_span_err!(
+ self.session,
+ span,
+ E0561,
+ "patterns aren't allowed in function pointer types"
+ )
+ .emit();
+ });
+ self.check_late_bound_lifetime_defs(&bfty.generic_params);
+ if let Extern::Implicit(_) = bfty.ext {
+ let sig_span = self.session.source_map().next_point(ty.span.shrink_to_lo());
+ self.maybe_lint_missing_abi(sig_span, ty.id);
+ }
+ }
+ TyKind::TraitObject(ref bounds, ..) => {
+ let mut any_lifetime_bounds = false;
+ for bound in bounds {
+ if let GenericBound::Outlives(ref lifetime) = *bound {
+ if any_lifetime_bounds {
+ struct_span_err!(
+ self.session,
+ lifetime.ident.span,
+ E0226,
+ "only a single explicit lifetime bound is permitted"
+ )
+ .emit();
+ break;
+ }
+ any_lifetime_bounds = true;
+ }
+ }
+ }
+ TyKind::ImplTrait(_, ref bounds) => {
+ if self.is_impl_trait_banned {
+ struct_span_err!(
+ self.session,
+ ty.span,
+ E0667,
+ "`impl Trait` is not allowed in path parameters"
+ )
+ .emit();
+ }
+
+ if let Some(outer_impl_trait_sp) = self.outer_impl_trait {
+ struct_span_err!(
+ self.session,
+ ty.span,
+ E0666,
+ "nested `impl Trait` is not allowed"
+ )
+ .span_label(outer_impl_trait_sp, "outer `impl Trait`")
+ .span_label(ty.span, "nested `impl Trait` here")
+ .emit();
+ }
+
+ if !bounds.iter().any(|b| matches!(b, GenericBound::Trait(..))) {
+ self.err_handler().span_err(ty.span, "at least one trait must be specified");
+ }
+ }
+ _ => {}
+ }
+ }
+
+ fn maybe_lint_missing_abi(&mut self, span: Span, id: NodeId) {
+ // FIXME(davidtwco): This is a hack to detect macros which produce spans of the
+ // call site which do not have a macro backtrace. See #61963.
+ let is_macro_callsite = self
+ .session
+ .source_map()
+ .span_to_snippet(span)
+ .map(|snippet| snippet.starts_with("#["))
+ .unwrap_or(true);
+ if !is_macro_callsite {
+ self.lint_buffer.buffer_lint_with_diagnostic(
+ MISSING_ABI,
+ id,
+ span,
+ "extern declarations without an explicit ABI are deprecated",
+ BuiltinLintDiagnostics::MissingAbi(span, abi::Abi::FALLBACK),
+ )
+ }
+ }
+}
+
+/// Checks that generic parameters are in the correct order,
+/// which is lifetimes, then types and then consts. (`<'a, T, const N: usize>`)
+fn validate_generic_param_order(
+ handler: &rustc_errors::Handler,
+ generics: &[GenericParam],
+ span: Span,
+) {
+ let mut max_param: Option<ParamKindOrd> = None;
+ let mut out_of_order = FxHashMap::default();
+ let mut param_idents = Vec::with_capacity(generics.len());
+
+ for (idx, param) in generics.iter().enumerate() {
+ let ident = param.ident;
+ let (kind, bounds, span) = (&param.kind, &param.bounds, ident.span);
+ let (ord_kind, ident) = match &param.kind {
+ GenericParamKind::Lifetime => (ParamKindOrd::Lifetime, ident.to_string()),
+ GenericParamKind::Type { default: _ } => (ParamKindOrd::Type, ident.to_string()),
+ GenericParamKind::Const { ref ty, kw_span: _, default: _ } => {
+ let ty = pprust::ty_to_string(ty);
+ (ParamKindOrd::Const, format!("const {}: {}", ident, ty))
+ }
+ };
+ param_idents.push((kind, ord_kind, bounds, idx, ident));
+ match max_param {
+ Some(max_param) if max_param > ord_kind => {
+ let entry = out_of_order.entry(ord_kind).or_insert((max_param, vec![]));
+ entry.1.push(span);
+ }
+ Some(_) | None => max_param = Some(ord_kind),
+ };
+ }
+
+ if !out_of_order.is_empty() {
+ let mut ordered_params = "<".to_string();
+ param_idents.sort_by_key(|&(_, po, _, i, _)| (po, i));
+ let mut first = true;
+ for (kind, _, bounds, _, ident) in param_idents {
+ if !first {
+ ordered_params += ", ";
+ }
+ ordered_params += &ident;
+
+ if !bounds.is_empty() {
+ ordered_params += ": ";
+ ordered_params += &pprust::bounds_to_string(&bounds);
+ }
+
+ match kind {
+ GenericParamKind::Type { default: Some(default) } => {
+ ordered_params += " = ";
+ ordered_params += &pprust::ty_to_string(default);
+ }
+ GenericParamKind::Type { default: None } => (),
+ GenericParamKind::Lifetime => (),
+ GenericParamKind::Const { ty: _, kw_span: _, default: Some(default) } => {
+ ordered_params += " = ";
+ ordered_params += &pprust::expr_to_string(&*default.value);
+ }
+ GenericParamKind::Const { ty: _, kw_span: _, default: None } => (),
+ }
+ first = false;
+ }
+
+ ordered_params += ">";
+
+ for (param_ord, (max_param, spans)) in &out_of_order {
+ let mut err = handler.struct_span_err(
+ spans.clone(),
+ &format!(
+ "{} parameters must be declared prior to {} parameters",
+ param_ord, max_param,
+ ),
+ );
+ err.span_suggestion(
+ span,
+ "reorder the parameters: lifetimes, then consts and types",
+ &ordered_params,
+ Applicability::MachineApplicable,
+ );
+ err.emit();
+ }
+ }
+}
+
+impl<'a> Visitor<'a> for AstValidator<'a> {
+ fn visit_attribute(&mut self, attr: &Attribute) {
+ validate_attr::check_meta(&self.session.parse_sess, attr);
+ }
+
+ fn visit_expr(&mut self, expr: &'a Expr) {
+ self.with_let_management(Some(ForbiddenLetReason::GenericForbidden), |this, forbidden_let_reason| {
+ match &expr.kind {
+ ExprKind::Binary(Spanned { node: BinOpKind::Or, span }, lhs, rhs) => {
+ let local_reason = Some(ForbiddenLetReason::NotSupportedOr(*span));
+ this.with_let_management(local_reason, |this, _| this.visit_expr(lhs));
+ this.with_let_management(local_reason, |this, _| this.visit_expr(rhs));
+ }
+ ExprKind::If(cond, then, opt_else) => {
+ this.visit_block(then);
+ walk_list!(this, visit_expr, opt_else);
+ this.with_let_management(None, |this, _| this.visit_expr(cond));
+ return;
+ }
+ ExprKind::Let(..) if let Some(elem) = forbidden_let_reason => {
+ this.ban_let_expr(expr, elem);
+ },
+ ExprKind::Match(scrutinee, arms) => {
+ this.visit_expr(scrutinee);
+ for arm in arms {
+ this.visit_expr(&arm.body);
+ this.visit_pat(&arm.pat);
+ walk_list!(this, visit_attribute, &arm.attrs);
+ if let Some(guard) = &arm.guard && let ExprKind::Let(_, guard_expr, _) = &guard.kind {
+ this.with_let_management(None, |this, _| {
+ this.visit_expr(guard_expr)
+ });
+ return;
+ }
+ }
+ }
+ ExprKind::Paren(local_expr) => {
+ fn has_let_expr(expr: &Expr) -> bool {
+ match expr.kind {
+ ExprKind::Binary(_, ref lhs, ref rhs) => has_let_expr(lhs) || has_let_expr(rhs),
+ ExprKind::Let(..) => true,
+ _ => false,
+ }
+ }
+ let local_reason = if has_let_expr(local_expr) {
+ Some(ForbiddenLetReason::NotSupportedParentheses(local_expr.span))
+ }
+ else {
+ forbidden_let_reason
+ };
+ this.with_let_management(local_reason, |this, _| this.visit_expr(local_expr));
+ }
+ ExprKind::Binary(Spanned { node: BinOpKind::And, .. }, ..) => {
+ this.with_let_management(forbidden_let_reason, |this, _| visit::walk_expr(this, expr));
+ return;
+ }
+ ExprKind::While(cond, then, opt_label) => {
+ walk_list!(this, visit_label, opt_label);
+ this.visit_block(then);
+ this.with_let_management(None, |this, _| this.visit_expr(cond));
+ return;
+ }
+ _ => visit::walk_expr(this, expr),
+ }
+ });
+ }
+
+ fn visit_ty(&mut self, ty: &'a Ty) {
+ self.visit_ty_common(ty);
+ self.walk_ty(ty)
+ }
+
+ fn visit_label(&mut self, label: &'a Label) {
+ self.check_label(label.ident);
+ visit::walk_label(self, label);
+ }
+
+ fn visit_lifetime(&mut self, lifetime: &'a Lifetime, _: visit::LifetimeCtxt) {
+ self.check_lifetime(lifetime.ident);
+ visit::walk_lifetime(self, lifetime);
+ }
+
+ fn visit_field_def(&mut self, s: &'a FieldDef) {
+ visit::walk_field_def(self, s)
+ }
+
+ fn visit_item(&mut self, item: &'a Item) {
+ if item.attrs.iter().any(|attr| self.session.is_proc_macro_attr(attr)) {
+ self.has_proc_macro_decls = true;
+ }
+
+ if self.session.contains_name(&item.attrs, sym::no_mangle) {
+ self.check_nomangle_item_asciionly(item.ident, item.span);
+ }
+
+ match item.kind {
+ ItemKind::Impl(box Impl {
+ unsafety,
+ polarity,
+ defaultness: _,
+ constness,
+ ref generics,
+ of_trait: Some(ref t),
+ ref self_ty,
+ ref items,
+ }) => {
+ self.with_in_trait_impl(true, Some(constness), |this| {
+ this.invalid_visibility(&item.vis, None);
+ if let TyKind::Err = self_ty.kind {
+ this.err_handler()
+ .struct_span_err(
+ item.span,
+ "`impl Trait for .. {}` is an obsolete syntax",
+ )
+ .help("use `auto trait Trait {}` instead")
+ .emit();
+ }
+ if let (Unsafe::Yes(span), ImplPolarity::Negative(sp)) = (unsafety, polarity) {
+ struct_span_err!(
+ this.session,
+ sp.to(t.path.span),
+ E0198,
+ "negative impls cannot be unsafe"
+ )
+ .span_label(sp, "negative because of this")
+ .span_label(span, "unsafe because of this")
+ .emit();
+ }
+
+ this.visit_vis(&item.vis);
+ this.visit_ident(item.ident);
+ if let Const::Yes(_) = constness {
+ this.with_tilde_const_allowed(|this| this.visit_generics(generics));
+ } else {
+ this.visit_generics(generics);
+ }
+ this.visit_trait_ref(t);
+ this.visit_ty(self_ty);
+
+ walk_list!(this, visit_assoc_item, items, AssocCtxt::Impl);
+ });
+ return; // Avoid visiting again.
+ }
+ ItemKind::Impl(box Impl {
+ unsafety,
+ polarity,
+ defaultness,
+ constness,
+ generics: _,
+ of_trait: None,
+ ref self_ty,
+ items: _,
+ }) => {
+ let error = |annotation_span, annotation| {
+ let mut err = self.err_handler().struct_span_err(
+ self_ty.span,
+ &format!("inherent impls cannot be {}", annotation),
+ );
+ err.span_label(annotation_span, &format!("{} because of this", annotation));
+ err.span_label(self_ty.span, "inherent impl for this type");
+ err
+ };
+
+ self.invalid_visibility(
+ &item.vis,
+ Some("place qualifiers on individual impl items instead"),
+ );
+ if let Unsafe::Yes(span) = unsafety {
+ error(span, "unsafe").code(error_code!(E0197)).emit();
+ }
+ if let ImplPolarity::Negative(span) = polarity {
+ error(span, "negative").emit();
+ }
+ if let Defaultness::Default(def_span) = defaultness {
+ error(def_span, "`default`")
+ .note("only trait implementations may be annotated with `default`")
+ .emit();
+ }
+ if let Const::Yes(span) = constness {
+ error(span, "`const`")
+ .note("only trait implementations may be annotated with `const`")
+ .emit();
+ }
+ }
+ ItemKind::Fn(box Fn { defaultness, ref sig, ref generics, ref body }) => {
+ self.check_defaultness(item.span, defaultness);
+
+ if body.is_none() {
+ let msg = "free function without a body";
+ let ext = sig.header.ext;
+
+ let f = |e: &mut DiagnosticBuilder<'_, _>| {
+ if let Extern::Implicit(start_span) | Extern::Explicit(_, start_span) = &ext
+ {
+ let start_suggestion = if let Extern::Explicit(abi, _) = ext {
+ format!("extern \"{}\" {{", abi.symbol_unescaped)
+ } else {
+ "extern {".to_owned()
+ };
+
+ let end_suggestion = " }".to_owned();
+ let end_span = item.span.shrink_to_hi();
+
+ e
+ .multipart_suggestion(
+ "if you meant to declare an externally defined function, use an `extern` block",
+ vec![(*start_span, start_suggestion), (end_span, end_suggestion)],
+ Applicability::MaybeIncorrect,
+ );
+ }
+ };
+
+ self.error_item_without_body_with_help(
+ item.span,
+ "function",
+ msg,
+ " { <body> }",
+ f,
+ );
+ }
+
+ self.visit_vis(&item.vis);
+ self.visit_ident(item.ident);
+ let kind =
+ FnKind::Fn(FnCtxt::Free, item.ident, sig, &item.vis, generics, body.as_deref());
+ self.visit_fn(kind, item.span, item.id);
+ walk_list!(self, visit_attribute, &item.attrs);
+ return; // Avoid visiting again.
+ }
+ ItemKind::ForeignMod(ForeignMod { abi, unsafety, .. }) => {
+ let old_item = mem::replace(&mut self.extern_mod, Some(item));
+ self.invalid_visibility(
+ &item.vis,
+ Some("place qualifiers on individual foreign items instead"),
+ );
+ if let Unsafe::Yes(span) = unsafety {
+ self.err_handler().span_err(span, "extern block cannot be declared unsafe");
+ }
+ if abi.is_none() {
+ self.maybe_lint_missing_abi(item.span, item.id);
+ }
+ visit::walk_item(self, item);
+ self.extern_mod = old_item;
+ return; // Avoid visiting again.
+ }
+ ItemKind::Enum(ref def, _) => {
+ for variant in &def.variants {
+ self.invalid_visibility(&variant.vis, None);
+ for field in variant.data.fields() {
+ self.invalid_visibility(&field.vis, None);
+ }
+ }
+ }
+ ItemKind::Trait(box Trait { is_auto, ref generics, ref bounds, ref items, .. }) => {
+ if is_auto == IsAuto::Yes {
+ // Auto traits cannot have generics, super traits nor contain items.
+ self.deny_generic_params(generics, item.ident.span);
+ self.deny_super_traits(bounds, item.ident.span);
+ self.deny_where_clause(&generics.where_clause, item.ident.span);
+ self.deny_items(items, item.ident.span);
+ }
+
+ // Equivalent of `visit::walk_item` for `ItemKind::Trait` that inserts a bound
+ // context for the supertraits.
+ self.visit_vis(&item.vis);
+ self.visit_ident(item.ident);
+ self.visit_generics(generics);
+ self.with_tilde_const_allowed(|this| {
+ walk_list!(this, visit_param_bound, bounds, BoundKind::SuperTraits)
+ });
+ walk_list!(self, visit_assoc_item, items, AssocCtxt::Trait);
+ walk_list!(self, visit_attribute, &item.attrs);
+ return;
+ }
+ ItemKind::Mod(unsafety, ref mod_kind) => {
+ if let Unsafe::Yes(span) = unsafety {
+ self.err_handler().span_err(span, "module cannot be declared unsafe");
+ }
+ // Ensure that `path` attributes on modules are recorded as used (cf. issue #35584).
+ if !matches!(mod_kind, ModKind::Loaded(_, Inline::Yes, _))
+ && !self.session.contains_name(&item.attrs, sym::path)
+ {
+ self.check_mod_file_item_asciionly(item.ident);
+ }
+ }
+ ItemKind::Struct(ref vdata, ref generics) => match vdata {
+ // Duplicating the `Visitor` logic allows catching all cases
+ // of `Anonymous(Struct, Union)` outside of a field struct or union.
+ //
+ // Inside `visit_ty` the validator catches every `Anonymous(Struct, Union)` it
+ // encounters, and only on `ItemKind::Struct` and `ItemKind::Union`
+ // it uses `visit_ty_common`, which doesn't contain that specific check.
+ VariantData::Struct(ref fields, ..) => {
+ self.visit_vis(&item.vis);
+ self.visit_ident(item.ident);
+ self.visit_generics(generics);
+ self.with_banned_assoc_ty_bound(|this| {
+ walk_list!(this, visit_struct_field_def, fields);
+ });
+ walk_list!(self, visit_attribute, &item.attrs);
+ return;
+ }
+ _ => {}
+ },
+ ItemKind::Union(ref vdata, ref generics) => {
+ if vdata.fields().is_empty() {
+ self.err_handler().span_err(item.span, "unions cannot have zero fields");
+ }
+ match vdata {
+ VariantData::Struct(ref fields, ..) => {
+ self.visit_vis(&item.vis);
+ self.visit_ident(item.ident);
+ self.visit_generics(generics);
+ self.with_banned_assoc_ty_bound(|this| {
+ walk_list!(this, visit_struct_field_def, fields);
+ });
+ walk_list!(self, visit_attribute, &item.attrs);
+ return;
+ }
+ _ => {}
+ }
+ }
+ ItemKind::Const(def, .., None) => {
+ self.check_defaultness(item.span, def);
+ let msg = "free constant item without body";
+ self.error_item_without_body(item.span, "constant", msg, " = <expr>;");
+ }
+ ItemKind::Static(.., None) => {
+ let msg = "free static item without body";
+ self.error_item_without_body(item.span, "static", msg, " = <expr>;");
+ }
+ ItemKind::TyAlias(box TyAlias {
+ defaultness,
+ where_clauses,
+ ref bounds,
+ ref ty,
+ ..
+ }) => {
+ self.check_defaultness(item.span, defaultness);
+ if ty.is_none() {
+ let msg = "free type alias without body";
+ self.error_item_without_body(item.span, "type", msg, " = <type>;");
+ }
+ self.check_type_no_bounds(bounds, "this context");
+ if where_clauses.1.0 {
+ let mut err = self.err_handler().struct_span_err(
+ where_clauses.1.1,
+ "where clauses are not allowed after the type for type aliases",
+ );
+ err.note(
+ "see issue #89122 <https://github.com/rust-lang/rust/issues/89122> for more information",
+ );
+ err.emit();
+ }
+ }
+ _ => {}
+ }
+
+ visit::walk_item(self, item);
+ }
+
+ fn visit_foreign_item(&mut self, fi: &'a ForeignItem) {
+ match &fi.kind {
+ ForeignItemKind::Fn(box Fn { defaultness, sig, body, .. }) => {
+ self.check_defaultness(fi.span, *defaultness);
+ self.check_foreign_fn_bodyless(fi.ident, body.as_deref());
+ self.check_foreign_fn_headerless(fi.ident, fi.span, sig.header);
+ self.check_foreign_item_ascii_only(fi.ident);
+ }
+ ForeignItemKind::TyAlias(box TyAlias {
+ defaultness,
+ generics,
+ where_clauses,
+ bounds,
+ ty,
+ ..
+ }) => {
+ self.check_defaultness(fi.span, *defaultness);
+ self.check_foreign_kind_bodyless(fi.ident, "type", ty.as_ref().map(|b| b.span));
+ self.check_type_no_bounds(bounds, "`extern` blocks");
+ self.check_foreign_ty_genericless(generics, where_clauses.0.1);
+ self.check_foreign_item_ascii_only(fi.ident);
+ }
+ ForeignItemKind::Static(_, _, body) => {
+ self.check_foreign_kind_bodyless(fi.ident, "static", body.as_ref().map(|b| b.span));
+ self.check_foreign_item_ascii_only(fi.ident);
+ }
+ ForeignItemKind::MacCall(..) => {}
+ }
+
+ visit::walk_foreign_item(self, fi)
+ }
+
+ // Mirrors `visit::walk_generic_args`, but tracks relevant state.
+ fn visit_generic_args(&mut self, _: Span, generic_args: &'a GenericArgs) {
+ match *generic_args {
+ GenericArgs::AngleBracketed(ref data) => {
+ self.check_generic_args_before_constraints(data);
+
+ for arg in &data.args {
+ match arg {
+ AngleBracketedArg::Arg(arg) => self.visit_generic_arg(arg),
+ // Type bindings such as `Item = impl Debug` in `Iterator<Item = Debug>`
+ // are allowed to contain nested `impl Trait`.
+ AngleBracketedArg::Constraint(constraint) => {
+ self.with_impl_trait(None, |this| {
+ this.visit_assoc_constraint_from_generic_args(constraint);
+ });
+ }
+ }
+ }
+ }
+ GenericArgs::Parenthesized(ref data) => {
+ walk_list!(self, visit_ty, &data.inputs);
+ if let FnRetTy::Ty(ty) = &data.output {
+ // `-> Foo` syntax is essentially an associated type binding,
+ // so it is also allowed to contain nested `impl Trait`.
+ self.with_impl_trait(None, |this| this.visit_ty(ty));
+ }
+ }
+ }
+ }
+
+ fn visit_generics(&mut self, generics: &'a Generics) {
+ let mut prev_param_default = None;
+ for param in &generics.params {
+ match param.kind {
+ GenericParamKind::Lifetime => (),
+ GenericParamKind::Type { default: Some(_), .. }
+ | GenericParamKind::Const { default: Some(_), .. } => {
+ prev_param_default = Some(param.ident.span);
+ }
+ GenericParamKind::Type { .. } | GenericParamKind::Const { .. } => {
+ if let Some(span) = prev_param_default {
+ let mut err = self.err_handler().struct_span_err(
+ span,
+ "generic parameters with a default must be trailing",
+ );
+ err.emit();
+ break;
+ }
+ }
+ }
+ }
+
+ validate_generic_param_order(self.err_handler(), &generics.params, generics.span);
+
+ for predicate in &generics.where_clause.predicates {
+ if let WherePredicate::EqPredicate(ref predicate) = *predicate {
+ deny_equality_constraints(self, predicate, generics);
+ }
+ }
+ walk_list!(self, visit_generic_param, &generics.params);
+ for predicate in &generics.where_clause.predicates {
+ match predicate {
+ WherePredicate::BoundPredicate(bound_pred) => {
+ // A type binding, eg `for<'c> Foo: Send+Clone+'c`
+ self.check_late_bound_lifetime_defs(&bound_pred.bound_generic_params);
+
+ // This is slightly complicated. Our representation for poly-trait-refs contains a single
+ // binder and thus we only allow a single level of quantification. However,
+ // the syntax of Rust permits quantification in two places in where clauses,
+ // e.g., `T: for <'a> Foo<'a>` and `for <'a, 'b> &'b T: Foo<'a>`. If both are
+ // defined, then error.
+ if !bound_pred.bound_generic_params.is_empty() {
+ for bound in &bound_pred.bounds {
+ match bound {
+ GenericBound::Trait(t, _) => {
+ if !t.bound_generic_params.is_empty() {
+ struct_span_err!(
+ self.err_handler(),
+ t.span,
+ E0316,
+ "nested quantification of lifetimes"
+ )
+ .emit();
+ }
+ }
+ GenericBound::Outlives(_) => {}
+ }
+ }
+ }
+ }
+ _ => {}
+ }
+ self.visit_where_predicate(predicate);
+ }
+ }
+
+ fn visit_generic_param(&mut self, param: &'a GenericParam) {
+ if let GenericParamKind::Lifetime { .. } = param.kind {
+ self.check_lifetime(param.ident);
+ }
+ visit::walk_generic_param(self, param);
+ }
+
+ fn visit_param_bound(&mut self, bound: &'a GenericBound, ctxt: BoundKind) {
+ if let GenericBound::Trait(ref poly, modify) = *bound {
+ match (ctxt, modify) {
+ (BoundKind::SuperTraits, TraitBoundModifier::Maybe) => {
+ let mut err = self
+ .err_handler()
+ .struct_span_err(poly.span, "`?Trait` is not permitted in supertraits");
+ let path_str = pprust::path_to_string(&poly.trait_ref.path);
+ err.note(&format!("traits are `?{}` by default", path_str));
+ err.emit();
+ }
+ (BoundKind::TraitObject, TraitBoundModifier::Maybe) => {
+ let mut err = self.err_handler().struct_span_err(
+ poly.span,
+ "`?Trait` is not permitted in trait object types",
+ );
+ err.emit();
+ }
+ (_, TraitBoundModifier::MaybeConst) => {
+ if !self.is_tilde_const_allowed {
+ self.err_handler()
+ .struct_span_err(bound.span(), "`~const` is not allowed here")
+ .note("only allowed on bounds on traits' associated types and functions, const fns, const impls and its associated functions")
+ .emit();
+ }
+ }
+ (_, TraitBoundModifier::MaybeConstMaybe) => {
+ self.err_handler()
+ .span_err(bound.span(), "`~const` and `?` are mutually exclusive");
+ }
+ _ => {}
+ }
+ }
+
+ visit::walk_param_bound(self, bound)
+ }
+
+ fn visit_poly_trait_ref(&mut self, t: &'a PolyTraitRef, m: &'a TraitBoundModifier) {
+ self.check_late_bound_lifetime_defs(&t.bound_generic_params);
+ visit::walk_poly_trait_ref(self, t, m);
+ }
+
+ fn visit_variant_data(&mut self, s: &'a VariantData) {
+ self.with_banned_assoc_ty_bound(|this| visit::walk_struct_def(this, s))
+ }
+
+ fn visit_enum_def(
+ &mut self,
+ enum_definition: &'a EnumDef,
+ generics: &'a Generics,
+ item_id: NodeId,
+ _: Span,
+ ) {
+ self.with_banned_assoc_ty_bound(|this| {
+ visit::walk_enum_def(this, enum_definition, generics, item_id)
+ })
+ }
+
+ fn visit_fn(&mut self, fk: FnKind<'a>, span: Span, id: NodeId) {
+ // Only associated `fn`s can have `self` parameters.
+ let self_semantic = match fk.ctxt() {
+ Some(FnCtxt::Assoc(_)) => SelfSemantic::Yes,
+ _ => SelfSemantic::No,
+ };
+ self.check_fn_decl(fk.decl(), self_semantic);
+
+ self.check_c_variadic_type(fk);
+
+ // Functions cannot both be `const async`
+ if let Some(FnHeader {
+ constness: Const::Yes(cspan),
+ asyncness: Async::Yes { span: aspan, .. },
+ ..
+ }) = fk.header()
+ {
+ self.err_handler()
+ .struct_span_err(
+ vec![*cspan, *aspan],
+ "functions cannot be both `const` and `async`",
+ )
+ .span_label(*cspan, "`const` because of this")
+ .span_label(*aspan, "`async` because of this")
+ .span_label(span, "") // Point at the fn header.
+ .emit();
+ }
+
+ if let FnKind::Closure(ClosureBinder::For { generic_params, .. }, ..) = fk {
+ self.check_late_bound_lifetime_defs(generic_params);
+ }
+
+ if let FnKind::Fn(
+ _,
+ _,
+ FnSig { span: sig_span, header: FnHeader { ext: Extern::Implicit(_), .. }, .. },
+ _,
+ _,
+ _,
+ ) = fk
+ {
+ self.maybe_lint_missing_abi(*sig_span, id);
+ }
+
+ // Functions without bodies cannot have patterns.
+ if let FnKind::Fn(ctxt, _, sig, _, _, None) = fk {
+ Self::check_decl_no_pat(&sig.decl, |span, ident, mut_ident| {
+ let (code, msg, label) = match ctxt {
+ FnCtxt::Foreign => (
+ error_code!(E0130),
+ "patterns aren't allowed in foreign function declarations",
+ "pattern not allowed in foreign function",
+ ),
+ _ => (
+ error_code!(E0642),
+ "patterns aren't allowed in functions without bodies",
+ "pattern not allowed in function without body",
+ ),
+ };
+ if mut_ident && matches!(ctxt, FnCtxt::Assoc(_)) {
+ if let Some(ident) = ident {
+ let diag = BuiltinLintDiagnostics::PatternsInFnsWithoutBody(span, ident);
+ self.lint_buffer.buffer_lint_with_diagnostic(
+ PATTERNS_IN_FNS_WITHOUT_BODY,
+ id,
+ span,
+ msg,
+ diag,
+ )
+ }
+ } else {
+ self.err_handler()
+ .struct_span_err(span, msg)
+ .span_label(span, label)
+ .code(code)
+ .emit();
+ }
+ });
+ }
+
+ let tilde_const_allowed =
+ matches!(fk.header(), Some(FnHeader { constness: Const::Yes(_), .. }))
+ || matches!(fk.ctxt(), Some(FnCtxt::Assoc(_)));
+
+ self.with_tilde_const(tilde_const_allowed, |this| visit::walk_fn(this, fk, span));
+ }
+
+ fn visit_assoc_item(&mut self, item: &'a AssocItem, ctxt: AssocCtxt) {
+ if self.session.contains_name(&item.attrs, sym::no_mangle) {
+ self.check_nomangle_item_asciionly(item.ident, item.span);
+ }
+
+ if ctxt == AssocCtxt::Trait || !self.in_trait_impl {
+ self.check_defaultness(item.span, item.kind.defaultness());
+ }
+
+ if ctxt == AssocCtxt::Impl {
+ match &item.kind {
+ AssocItemKind::Const(_, _, body) => {
+ self.check_impl_item_provided(item.span, body, "constant", " = <expr>;");
+ }
+ AssocItemKind::Fn(box Fn { body, .. }) => {
+ self.check_impl_item_provided(item.span, body, "function", " { <body> }");
+ }
+ AssocItemKind::TyAlias(box TyAlias {
+ generics,
+ where_clauses,
+ where_predicates_split,
+ bounds,
+ ty,
+ ..
+ }) => {
+ self.check_impl_item_provided(item.span, ty, "type", " = <type>;");
+ self.check_type_no_bounds(bounds, "`impl`s");
+ if ty.is_some() {
+ self.check_gat_where(
+ item.id,
+ generics.where_clause.predicates.split_at(*where_predicates_split).0,
+ *where_clauses,
+ );
+ }
+ }
+ _ => {}
+ }
+ }
+
+ if ctxt == AssocCtxt::Trait || self.in_trait_impl {
+ self.invalid_visibility(&item.vis, None);
+ if let AssocItemKind::Fn(box Fn { sig, .. }) = &item.kind {
+ self.check_trait_fn_not_const(sig.header.constness);
+ self.check_trait_fn_not_async(item.span, sig.header.asyncness);
+ }
+ }
+
+ if let AssocItemKind::Const(..) = item.kind {
+ self.check_item_named(item.ident, "const");
+ }
+
+ match item.kind {
+ AssocItemKind::TyAlias(box TyAlias { ref generics, ref bounds, ref ty, .. })
+ if ctxt == AssocCtxt::Trait =>
+ {
+ self.visit_vis(&item.vis);
+ self.visit_ident(item.ident);
+ walk_list!(self, visit_attribute, &item.attrs);
+ self.with_tilde_const_allowed(|this| {
+ this.visit_generics(generics);
+ walk_list!(this, visit_param_bound, bounds, BoundKind::Bound);
+ });
+ walk_list!(self, visit_ty, ty);
+ }
+ AssocItemKind::Fn(box Fn { ref sig, ref generics, ref body, .. })
+ if self.in_const_trait_impl
+ || ctxt == AssocCtxt::Trait
+ || matches!(sig.header.constness, Const::Yes(_)) =>
+ {
+ self.visit_vis(&item.vis);
+ self.visit_ident(item.ident);
+ let kind = FnKind::Fn(
+ FnCtxt::Assoc(ctxt),
+ item.ident,
+ sig,
+ &item.vis,
+ generics,
+ body.as_deref(),
+ );
+ self.visit_fn(kind, item.span, item.id);
+ }
+ _ => self
+ .with_in_trait_impl(false, None, |this| visit::walk_assoc_item(this, item, ctxt)),
+ }
+ }
+}
+
+/// When encountering an equality constraint in a `where` clause, emit an error. If the code seems
+/// like it's setting an associated type, provide an appropriate suggestion.
+fn deny_equality_constraints(
+ this: &mut AstValidator<'_>,
+ predicate: &WhereEqPredicate,
+ generics: &Generics,
+) {
+ let mut err = this.err_handler().struct_span_err(
+ predicate.span,
+ "equality constraints are not yet supported in `where` clauses",
+ );
+ err.span_label(predicate.span, "not supported");
+
+ // Given `<A as Foo>::Bar = RhsTy`, suggest `A: Foo<Bar = RhsTy>`.
+ if let TyKind::Path(Some(qself), full_path) = &predicate.lhs_ty.kind {
+ if let TyKind::Path(None, path) = &qself.ty.kind {
+ match &path.segments[..] {
+ [PathSegment { ident, args: None, .. }] => {
+ for param in &generics.params {
+ if param.ident == *ident {
+ let param = ident;
+ match &full_path.segments[qself.position..] {
+ [PathSegment { ident, args, .. }] => {
+ // Make a new `Path` from `foo::Bar` to `Foo<Bar = RhsTy>`.
+ let mut assoc_path = full_path.clone();
+ // Remove `Bar` from `Foo::Bar`.
+ assoc_path.segments.pop();
+ let len = assoc_path.segments.len() - 1;
+ let gen_args = args.as_ref().map(|p| (**p).clone());
+ // Build `<Bar = RhsTy>`.
+ let arg = AngleBracketedArg::Constraint(AssocConstraint {
+ id: rustc_ast::node_id::DUMMY_NODE_ID,
+ ident: *ident,
+ gen_args,
+ kind: AssocConstraintKind::Equality {
+ term: predicate.rhs_ty.clone().into(),
+ },
+ span: ident.span,
+ });
+ // Add `<Bar = RhsTy>` to `Foo`.
+ match &mut assoc_path.segments[len].args {
+ Some(args) => match args.deref_mut() {
+ GenericArgs::Parenthesized(_) => continue,
+ GenericArgs::AngleBracketed(args) => {
+ args.args.push(arg);
+ }
+ },
+ empty_args => {
+ *empty_args = AngleBracketedArgs {
+ span: ident.span,
+ args: vec![arg],
+ }
+ .into();
+ }
+ }
+ err.span_suggestion_verbose(
+ predicate.span,
+ &format!(
+ "if `{}` is an associated type you're trying to set, \
+ use the associated type binding syntax",
+ ident
+ ),
+ format!(
+ "{}: {}",
+ param,
+ pprust::path_to_string(&assoc_path)
+ ),
+ Applicability::MaybeIncorrect,
+ );
+ }
+ _ => {}
+ };
+ }
+ }
+ }
+ _ => {}
+ }
+ }
+ }
+ // Given `A: Foo, A::Bar = RhsTy`, suggest `A: Foo<Bar = RhsTy>`.
+ if let TyKind::Path(None, full_path) = &predicate.lhs_ty.kind {
+ if let [potential_param, potential_assoc] = &full_path.segments[..] {
+ for param in &generics.params {
+ if param.ident == potential_param.ident {
+ for bound in &param.bounds {
+ if let ast::GenericBound::Trait(trait_ref, TraitBoundModifier::None) = bound
+ {
+ if let [trait_segment] = &trait_ref.trait_ref.path.segments[..] {
+ let assoc = pprust::path_to_string(&ast::Path::from_ident(
+ potential_assoc.ident,
+ ));
+ let ty = pprust::ty_to_string(&predicate.rhs_ty);
+ let (args, span) = match &trait_segment.args {
+ Some(args) => match args.deref() {
+ ast::GenericArgs::AngleBracketed(args) => {
+ let Some(arg) = args.args.last() else {
+ continue;
+ };
+ (
+ format!(", {} = {}", assoc, ty),
+ arg.span().shrink_to_hi(),
+ )
+ }
+ _ => continue,
+ },
+ None => (
+ format!("<{} = {}>", assoc, ty),
+ trait_segment.span().shrink_to_hi(),
+ ),
+ };
+ err.multipart_suggestion(
+ &format!(
+ "if `{}::{}` is an associated type you're trying to set, \
+ use the associated type binding syntax",
+ trait_segment.ident, potential_assoc.ident,
+ ),
+ vec![(span, args), (predicate.span, String::new())],
+ Applicability::MaybeIncorrect,
+ );
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ err.note(
+ "see issue #20041 <https://github.com/rust-lang/rust/issues/20041> for more information",
+ );
+ err.emit();
+}
+
+pub fn check_crate(session: &Session, krate: &Crate, lints: &mut LintBuffer) -> bool {
+ let mut validator = AstValidator {
+ session,
+ extern_mod: None,
+ in_trait_impl: false,
+ in_const_trait_impl: false,
+ has_proc_macro_decls: false,
+ outer_impl_trait: None,
+ is_tilde_const_allowed: false,
+ is_impl_trait_banned: false,
+ is_assoc_ty_bound_banned: false,
+ forbidden_let_reason: Some(ForbiddenLetReason::GenericForbidden),
+ lint_buffer: lints,
+ };
+ visit::walk_crate(&mut validator, krate);
+
+ validator.has_proc_macro_decls
+}
+
+/// Used to forbid `let` expressions in certain syntactic locations.
+#[derive(Clone, Copy)]
+enum ForbiddenLetReason {
+ /// `let` is not valid and the source environment is not important
+ GenericForbidden,
+ /// A let chain with the `||` operator
+ NotSupportedOr(Span),
+ /// A let chain with invalid parentheses
+ ///
+ /// For exemple, `let 1 = 1 && (expr && expr)` is allowed
+ /// but `(let 1 = 1 && (let 1 = 1 && (let 1 = 1))) && let a = 1` is not
+ NotSupportedParentheses(Span),
+}
diff --git a/compiler/rustc_ast_passes/src/feature_gate.rs b/compiler/rustc_ast_passes/src/feature_gate.rs
new file mode 100644
index 000000000..789eca1f0
--- /dev/null
+++ b/compiler/rustc_ast_passes/src/feature_gate.rs
@@ -0,0 +1,901 @@
+use rustc_ast as ast;
+use rustc_ast::visit::{self, AssocCtxt, FnCtxt, FnKind, Visitor};
+use rustc_ast::{AssocConstraint, AssocConstraintKind, NodeId};
+use rustc_ast::{PatKind, RangeEnd, VariantData};
+use rustc_errors::{struct_span_err, Applicability};
+use rustc_feature::{AttributeGate, BuiltinAttribute, BUILTIN_ATTRIBUTE_MAP};
+use rustc_feature::{Features, GateIssue};
+use rustc_session::parse::{feature_err, feature_err_issue};
+use rustc_session::Session;
+use rustc_span::source_map::Spanned;
+use rustc_span::symbol::sym;
+use rustc_span::Span;
+
+use tracing::debug;
+
+macro_rules! gate_feature_fn {
+ ($visitor: expr, $has_feature: expr, $span: expr, $name: expr, $explain: expr, $help: expr) => {{
+ let (visitor, has_feature, span, name, explain, help) =
+ (&*$visitor, $has_feature, $span, $name, $explain, $help);
+ let has_feature: bool = has_feature(visitor.features);
+ debug!("gate_feature(feature = {:?}, span = {:?}); has? {}", name, span, has_feature);
+ if !has_feature && !span.allows_unstable($name) {
+ feature_err_issue(&visitor.sess.parse_sess, name, span, GateIssue::Language, explain)
+ .help(help)
+ .emit();
+ }
+ }};
+ ($visitor: expr, $has_feature: expr, $span: expr, $name: expr, $explain: expr) => {{
+ let (visitor, has_feature, span, name, explain) =
+ (&*$visitor, $has_feature, $span, $name, $explain);
+ let has_feature: bool = has_feature(visitor.features);
+ debug!("gate_feature(feature = {:?}, span = {:?}); has? {}", name, span, has_feature);
+ if !has_feature && !span.allows_unstable($name) {
+ feature_err_issue(&visitor.sess.parse_sess, name, span, GateIssue::Language, explain)
+ .emit();
+ }
+ }};
+}
+
+macro_rules! gate_feature_post {
+ ($visitor: expr, $feature: ident, $span: expr, $explain: expr, $help: expr) => {
+ gate_feature_fn!($visitor, |x: &Features| x.$feature, $span, sym::$feature, $explain, $help)
+ };
+ ($visitor: expr, $feature: ident, $span: expr, $explain: expr) => {
+ gate_feature_fn!($visitor, |x: &Features| x.$feature, $span, sym::$feature, $explain)
+ };
+}
+
+pub fn check_attribute(attr: &ast::Attribute, sess: &Session, features: &Features) {
+ PostExpansionVisitor { sess, features }.visit_attribute(attr)
+}
+
+struct PostExpansionVisitor<'a> {
+ sess: &'a Session,
+
+ // `sess` contains a `Features`, but this might not be that one.
+ features: &'a Features,
+}
+
+impl<'a> PostExpansionVisitor<'a> {
+ fn check_abi(&self, abi: ast::StrLit, constness: ast::Const) {
+ let ast::StrLit { symbol_unescaped, span, .. } = abi;
+
+ if let ast::Const::Yes(_) = constness {
+ match symbol_unescaped {
+ // Stable
+ sym::Rust | sym::C => {}
+ abi => gate_feature_post!(
+ &self,
+ const_extern_fn,
+ span,
+ &format!("`{}` as a `const fn` ABI is unstable", abi)
+ ),
+ }
+ }
+
+ match symbol_unescaped.as_str() {
+ // Stable
+ "Rust" | "C" | "cdecl" | "stdcall" | "fastcall" | "aapcs" | "win64" | "sysv64"
+ | "system" => {}
+ "rust-intrinsic" => {
+ gate_feature_post!(&self, intrinsics, span, "intrinsics are subject to change");
+ }
+ "platform-intrinsic" => {
+ gate_feature_post!(
+ &self,
+ platform_intrinsics,
+ span,
+ "platform intrinsics are experimental and possibly buggy"
+ );
+ }
+ "vectorcall" => {
+ gate_feature_post!(
+ &self,
+ abi_vectorcall,
+ span,
+ "vectorcall is experimental and subject to change"
+ );
+ }
+ "thiscall" => {
+ gate_feature_post!(
+ &self,
+ abi_thiscall,
+ span,
+ "thiscall is experimental and subject to change"
+ );
+ }
+ "rust-call" => {
+ gate_feature_post!(
+ &self,
+ unboxed_closures,
+ span,
+ "rust-call ABI is subject to change"
+ );
+ }
+ "rust-cold" => {
+ gate_feature_post!(
+ &self,
+ rust_cold_cc,
+ span,
+ "rust-cold is experimental and subject to change"
+ );
+ }
+ "ptx-kernel" => {
+ gate_feature_post!(
+ &self,
+ abi_ptx,
+ span,
+ "PTX ABIs are experimental and subject to change"
+ );
+ }
+ "unadjusted" => {
+ gate_feature_post!(
+ &self,
+ abi_unadjusted,
+ span,
+ "unadjusted ABI is an implementation detail and perma-unstable"
+ );
+ }
+ "msp430-interrupt" => {
+ gate_feature_post!(
+ &self,
+ abi_msp430_interrupt,
+ span,
+ "msp430-interrupt ABI is experimental and subject to change"
+ );
+ }
+ "x86-interrupt" => {
+ gate_feature_post!(
+ &self,
+ abi_x86_interrupt,
+ span,
+ "x86-interrupt ABI is experimental and subject to change"
+ );
+ }
+ "amdgpu-kernel" => {
+ gate_feature_post!(
+ &self,
+ abi_amdgpu_kernel,
+ span,
+ "amdgpu-kernel ABI is experimental and subject to change"
+ );
+ }
+ "avr-interrupt" | "avr-non-blocking-interrupt" => {
+ gate_feature_post!(
+ &self,
+ abi_avr_interrupt,
+ span,
+ "avr-interrupt and avr-non-blocking-interrupt ABIs are experimental and subject to change"
+ );
+ }
+ "efiapi" => {
+ gate_feature_post!(
+ &self,
+ abi_efiapi,
+ span,
+ "efiapi ABI is experimental and subject to change"
+ );
+ }
+ "C-cmse-nonsecure-call" => {
+ gate_feature_post!(
+ &self,
+ abi_c_cmse_nonsecure_call,
+ span,
+ "C-cmse-nonsecure-call ABI is experimental and subject to change"
+ );
+ }
+ "C-unwind" => {
+ gate_feature_post!(
+ &self,
+ c_unwind,
+ span,
+ "C-unwind ABI is experimental and subject to change"
+ );
+ }
+ "stdcall-unwind" => {
+ gate_feature_post!(
+ &self,
+ c_unwind,
+ span,
+ "stdcall-unwind ABI is experimental and subject to change"
+ );
+ }
+ "system-unwind" => {
+ gate_feature_post!(
+ &self,
+ c_unwind,
+ span,
+ "system-unwind ABI is experimental and subject to change"
+ );
+ }
+ "thiscall-unwind" => {
+ gate_feature_post!(
+ &self,
+ c_unwind,
+ span,
+ "thiscall-unwind ABI is experimental and subject to change"
+ );
+ }
+ "cdecl-unwind" => {
+ gate_feature_post!(
+ &self,
+ c_unwind,
+ span,
+ "cdecl-unwind ABI is experimental and subject to change"
+ );
+ }
+ "fastcall-unwind" => {
+ gate_feature_post!(
+ &self,
+ c_unwind,
+ span,
+ "fastcall-unwind ABI is experimental and subject to change"
+ );
+ }
+ "vectorcall-unwind" => {
+ gate_feature_post!(
+ &self,
+ c_unwind,
+ span,
+ "vectorcall-unwind ABI is experimental and subject to change"
+ );
+ }
+ "aapcs-unwind" => {
+ gate_feature_post!(
+ &self,
+ c_unwind,
+ span,
+ "aapcs-unwind ABI is experimental and subject to change"
+ );
+ }
+ "win64-unwind" => {
+ gate_feature_post!(
+ &self,
+ c_unwind,
+ span,
+ "win64-unwind ABI is experimental and subject to change"
+ );
+ }
+ "sysv64-unwind" => {
+ gate_feature_post!(
+ &self,
+ c_unwind,
+ span,
+ "sysv64-unwind ABI is experimental and subject to change"
+ );
+ }
+ "wasm" => {
+ gate_feature_post!(
+ &self,
+ wasm_abi,
+ span,
+ "wasm ABI is experimental and subject to change"
+ );
+ }
+ abi => {
+ if self.sess.opts.pretty.map_or(true, |ppm| ppm.needs_hir()) {
+ self.sess.parse_sess.span_diagnostic.delay_span_bug(
+ span,
+ &format!("unrecognized ABI not caught in lowering: {}", abi),
+ );
+ }
+ }
+ }
+ }
+
+ fn check_extern(&self, ext: ast::Extern, constness: ast::Const) {
+ if let ast::Extern::Explicit(abi, _) = ext {
+ self.check_abi(abi, constness);
+ }
+ }
+
+ fn maybe_report_invalid_custom_discriminants(&self, variants: &[ast::Variant]) {
+ let has_fields = variants.iter().any(|variant| match variant.data {
+ VariantData::Tuple(..) | VariantData::Struct(..) => true,
+ VariantData::Unit(..) => false,
+ });
+
+ let discriminant_spans = variants
+ .iter()
+ .filter(|variant| match variant.data {
+ VariantData::Tuple(..) | VariantData::Struct(..) => false,
+ VariantData::Unit(..) => true,
+ })
+ .filter_map(|variant| variant.disr_expr.as_ref().map(|c| c.value.span))
+ .collect::<Vec<_>>();
+
+ if !discriminant_spans.is_empty() && has_fields {
+ let mut err = feature_err(
+ &self.sess.parse_sess,
+ sym::arbitrary_enum_discriminant,
+ discriminant_spans.clone(),
+ "custom discriminant values are not allowed in enums with tuple or struct variants",
+ );
+ for sp in discriminant_spans {
+ err.span_label(sp, "disallowed custom discriminant");
+ }
+ for variant in variants.iter() {
+ match &variant.data {
+ VariantData::Struct(..) => {
+ err.span_label(variant.span, "struct variant defined here");
+ }
+ VariantData::Tuple(..) => {
+ err.span_label(variant.span, "tuple variant defined here");
+ }
+ VariantData::Unit(..) => {}
+ }
+ }
+ err.emit();
+ }
+ }
+
+ fn check_gat(&self, generics: &ast::Generics, span: Span) {
+ if !generics.params.is_empty() {
+ gate_feature_post!(
+ &self,
+ generic_associated_types,
+ span,
+ "generic associated types are unstable"
+ );
+ }
+ if !generics.where_clause.predicates.is_empty() {
+ gate_feature_post!(
+ &self,
+ generic_associated_types,
+ span,
+ "where clauses on associated types are unstable"
+ );
+ }
+ }
+
+ /// Feature gate `impl Trait` inside `type Alias = $type_expr;`.
+ fn check_impl_trait(&self, ty: &ast::Ty) {
+ struct ImplTraitVisitor<'a> {
+ vis: &'a PostExpansionVisitor<'a>,
+ }
+ impl Visitor<'_> for ImplTraitVisitor<'_> {
+ fn visit_ty(&mut self, ty: &ast::Ty) {
+ if let ast::TyKind::ImplTrait(..) = ty.kind {
+ gate_feature_post!(
+ &self.vis,
+ type_alias_impl_trait,
+ ty.span,
+ "`impl Trait` in type aliases is unstable"
+ );
+ }
+ visit::walk_ty(self, ty);
+ }
+ }
+ ImplTraitVisitor { vis: self }.visit_ty(ty);
+ }
+}
+
+impl<'a> Visitor<'a> for PostExpansionVisitor<'a> {
+ fn visit_attribute(&mut self, attr: &ast::Attribute) {
+ let attr_info = attr.ident().and_then(|ident| BUILTIN_ATTRIBUTE_MAP.get(&ident.name));
+ // Check feature gates for built-in attributes.
+ if let Some(BuiltinAttribute {
+ gate: AttributeGate::Gated(_, name, descr, has_feature),
+ ..
+ }) = attr_info
+ {
+ gate_feature_fn!(self, has_feature, attr.span, *name, descr);
+ }
+ // Check unstable flavors of the `#[doc]` attribute.
+ if attr.has_name(sym::doc) {
+ for nested_meta in attr.meta_item_list().unwrap_or_default() {
+ macro_rules! gate_doc { ($($name:ident => $feature:ident)*) => {
+ $(if nested_meta.has_name(sym::$name) {
+ let msg = concat!("`#[doc(", stringify!($name), ")]` is experimental");
+ gate_feature_post!(self, $feature, attr.span, msg);
+ })*
+ }}
+
+ gate_doc!(
+ cfg => doc_cfg
+ cfg_hide => doc_cfg_hide
+ masked => doc_masked
+ notable_trait => doc_notable_trait
+ );
+
+ if nested_meta.has_name(sym::keyword) {
+ let msg = "`#[doc(keyword)]` is meant for internal use only";
+ gate_feature_post!(self, rustdoc_internals, attr.span, msg);
+ }
+
+ if nested_meta.has_name(sym::fake_variadic) {
+ let msg = "`#[doc(fake_variadic)]` is meant for internal use only";
+ gate_feature_post!(self, rustdoc_internals, attr.span, msg);
+ }
+ }
+ }
+
+ // Emit errors for non-staged-api crates.
+ if !self.features.staged_api {
+ if attr.has_name(sym::unstable)
+ || attr.has_name(sym::stable)
+ || attr.has_name(sym::rustc_const_unstable)
+ || attr.has_name(sym::rustc_const_stable)
+ {
+ struct_span_err!(
+ self.sess,
+ attr.span,
+ E0734,
+ "stability attributes may not be used outside of the standard library",
+ )
+ .emit();
+ }
+ }
+ }
+
+ fn visit_item(&mut self, i: &'a ast::Item) {
+ match i.kind {
+ ast::ItemKind::ForeignMod(ref foreign_module) => {
+ if let Some(abi) = foreign_module.abi {
+ self.check_abi(abi, ast::Const::No);
+ }
+ }
+
+ ast::ItemKind::Fn(..) => {
+ if self.sess.contains_name(&i.attrs, sym::start) {
+ gate_feature_post!(
+ &self,
+ start,
+ i.span,
+ "`#[start]` functions are experimental \
+ and their signature may change \
+ over time"
+ );
+ }
+ }
+
+ ast::ItemKind::Struct(..) => {
+ for attr in self.sess.filter_by_name(&i.attrs, sym::repr) {
+ for item in attr.meta_item_list().unwrap_or_else(Vec::new) {
+ if item.has_name(sym::simd) {
+ gate_feature_post!(
+ &self,
+ repr_simd,
+ attr.span,
+ "SIMD types are experimental and possibly buggy"
+ );
+ }
+ }
+ }
+ }
+
+ ast::ItemKind::Enum(ast::EnumDef { ref variants, .. }, ..) => {
+ for variant in variants {
+ match (&variant.data, &variant.disr_expr) {
+ (ast::VariantData::Unit(..), _) => {}
+ (_, Some(disr_expr)) => gate_feature_post!(
+ &self,
+ arbitrary_enum_discriminant,
+ disr_expr.value.span,
+ "discriminants on non-unit variants are experimental"
+ ),
+ _ => {}
+ }
+ }
+
+ let has_feature = self.features.arbitrary_enum_discriminant;
+ if !has_feature && !i.span.allows_unstable(sym::arbitrary_enum_discriminant) {
+ self.maybe_report_invalid_custom_discriminants(&variants);
+ }
+ }
+
+ ast::ItemKind::Impl(box ast::Impl { polarity, defaultness, ref of_trait, .. }) => {
+ if let ast::ImplPolarity::Negative(span) = polarity {
+ gate_feature_post!(
+ &self,
+ negative_impls,
+ span.to(of_trait.as_ref().map_or(span, |t| t.path.span)),
+ "negative trait bounds are not yet fully implemented; \
+ use marker types for now"
+ );
+ }
+
+ if let ast::Defaultness::Default(_) = defaultness {
+ gate_feature_post!(&self, specialization, i.span, "specialization is unstable");
+ }
+ }
+
+ ast::ItemKind::Trait(box ast::Trait { is_auto: ast::IsAuto::Yes, .. }) => {
+ gate_feature_post!(
+ &self,
+ auto_traits,
+ i.span,
+ "auto traits are experimental and possibly buggy"
+ );
+ }
+
+ ast::ItemKind::TraitAlias(..) => {
+ gate_feature_post!(&self, trait_alias, i.span, "trait aliases are experimental");
+ }
+
+ ast::ItemKind::MacroDef(ast::MacroDef { macro_rules: false, .. }) => {
+ let msg = "`macro` is experimental";
+ gate_feature_post!(&self, decl_macro, i.span, msg);
+ }
+
+ ast::ItemKind::TyAlias(box ast::TyAlias { ty: Some(ref ty), .. }) => {
+ self.check_impl_trait(&ty)
+ }
+
+ _ => {}
+ }
+
+ visit::walk_item(self, i);
+ }
+
+ fn visit_foreign_item(&mut self, i: &'a ast::ForeignItem) {
+ match i.kind {
+ ast::ForeignItemKind::Fn(..) | ast::ForeignItemKind::Static(..) => {
+ let link_name = self.sess.first_attr_value_str_by_name(&i.attrs, sym::link_name);
+ let links_to_llvm =
+ link_name.map_or(false, |val| val.as_str().starts_with("llvm."));
+ if links_to_llvm {
+ gate_feature_post!(
+ &self,
+ link_llvm_intrinsics,
+ i.span,
+ "linking to LLVM intrinsics is experimental"
+ );
+ }
+ }
+ ast::ForeignItemKind::TyAlias(..) => {
+ gate_feature_post!(&self, extern_types, i.span, "extern types are experimental");
+ }
+ ast::ForeignItemKind::MacCall(..) => {}
+ }
+
+ visit::walk_foreign_item(self, i)
+ }
+
+ fn visit_ty(&mut self, ty: &'a ast::Ty) {
+ match ty.kind {
+ ast::TyKind::BareFn(ref bare_fn_ty) => {
+ // Function pointers cannot be `const`
+ self.check_extern(bare_fn_ty.ext, ast::Const::No);
+ }
+ ast::TyKind::Never => {
+ gate_feature_post!(&self, never_type, ty.span, "the `!` type is experimental");
+ }
+ _ => {}
+ }
+ visit::walk_ty(self, ty)
+ }
+
+ fn visit_fn_ret_ty(&mut self, ret_ty: &'a ast::FnRetTy) {
+ if let ast::FnRetTy::Ty(ref output_ty) = *ret_ty {
+ if let ast::TyKind::Never = output_ty.kind {
+ // Do nothing.
+ } else {
+ self.visit_ty(output_ty)
+ }
+ }
+ }
+
+ fn visit_stmt(&mut self, stmt: &'a ast::Stmt) {
+ if let ast::StmtKind::Semi(expr) = &stmt.kind
+ && let ast::ExprKind::Assign(lhs, _, _) = &expr.kind
+ && let ast::ExprKind::Type(..) = lhs.kind
+ && self.sess.parse_sess.span_diagnostic.err_count() == 0
+ && !self.features.type_ascription
+ && !lhs.span.allows_unstable(sym::type_ascription)
+ {
+ // When we encounter a statement of the form `foo: Ty = val;`, this will emit a type
+ // ascription error, but the likely intention was to write a `let` statement. (#78907).
+ feature_err_issue(
+ &self.sess.parse_sess,
+ sym::type_ascription,
+ lhs.span,
+ GateIssue::Language,
+ "type ascription is experimental",
+ ).span_suggestion_verbose(
+ lhs.span.shrink_to_lo(),
+ "you might have meant to introduce a new binding",
+ "let ".to_string(),
+ Applicability::MachineApplicable,
+ ).emit();
+ }
+ visit::walk_stmt(self, stmt);
+ }
+
+ fn visit_expr(&mut self, e: &'a ast::Expr) {
+ match e.kind {
+ ast::ExprKind::Box(_) => {
+ gate_feature_post!(
+ &self,
+ box_syntax,
+ e.span,
+ "box expression syntax is experimental; you can call `Box::new` instead"
+ );
+ }
+ ast::ExprKind::Type(..) => {
+ // To avoid noise about type ascription in common syntax errors, only emit if it
+ // is the *only* error.
+ if self.sess.parse_sess.span_diagnostic.err_count() == 0 {
+ gate_feature_post!(
+ &self,
+ type_ascription,
+ e.span,
+ "type ascription is experimental"
+ );
+ }
+ }
+ ast::ExprKind::TryBlock(_) => {
+ gate_feature_post!(&self, try_blocks, e.span, "`try` expression is experimental");
+ }
+ ast::ExprKind::Block(_, Some(label)) => {
+ gate_feature_post!(
+ &self,
+ label_break_value,
+ label.ident.span,
+ "labels on blocks are unstable"
+ );
+ }
+ _ => {}
+ }
+ visit::walk_expr(self, e)
+ }
+
+ fn visit_pat(&mut self, pattern: &'a ast::Pat) {
+ match &pattern.kind {
+ PatKind::Slice(pats) => {
+ for pat in pats {
+ let inner_pat = match &pat.kind {
+ PatKind::Ident(.., Some(pat)) => pat,
+ _ => pat,
+ };
+ if let PatKind::Range(Some(_), None, Spanned { .. }) = inner_pat.kind {
+ gate_feature_post!(
+ &self,
+ half_open_range_patterns,
+ pat.span,
+ "`X..` patterns in slices are experimental"
+ );
+ }
+ }
+ }
+ PatKind::Box(..) => {
+ gate_feature_post!(
+ &self,
+ box_patterns,
+ pattern.span,
+ "box pattern syntax is experimental"
+ );
+ }
+ PatKind::Range(_, Some(_), Spanned { node: RangeEnd::Excluded, .. }) => {
+ gate_feature_post!(
+ &self,
+ exclusive_range_pattern,
+ pattern.span,
+ "exclusive range pattern syntax is experimental"
+ );
+ }
+ _ => {}
+ }
+ visit::walk_pat(self, pattern)
+ }
+
+ fn visit_fn(&mut self, fn_kind: FnKind<'a>, span: Span, _: NodeId) {
+ if let Some(header) = fn_kind.header() {
+ // Stability of const fn methods are covered in `visit_assoc_item` below.
+ self.check_extern(header.ext, header.constness);
+ }
+
+ if fn_kind.ctxt() != Some(FnCtxt::Foreign) && fn_kind.decl().c_variadic() {
+ gate_feature_post!(&self, c_variadic, span, "C-variadic functions are unstable");
+ }
+
+ visit::walk_fn(self, fn_kind, span)
+ }
+
+ fn visit_assoc_constraint(&mut self, constraint: &'a AssocConstraint) {
+ if let AssocConstraintKind::Bound { .. } = constraint.kind {
+ gate_feature_post!(
+ &self,
+ associated_type_bounds,
+ constraint.span,
+ "associated type bounds are unstable"
+ )
+ }
+ visit::walk_assoc_constraint(self, constraint)
+ }
+
+ fn visit_assoc_item(&mut self, i: &'a ast::AssocItem, ctxt: AssocCtxt) {
+ let is_fn = match i.kind {
+ ast::AssocItemKind::Fn(_) => true,
+ ast::AssocItemKind::TyAlias(box ast::TyAlias { ref generics, ref ty, .. }) => {
+ if let (Some(_), AssocCtxt::Trait) = (ty, ctxt) {
+ gate_feature_post!(
+ &self,
+ associated_type_defaults,
+ i.span,
+ "associated type defaults are unstable"
+ );
+ }
+ if let Some(ty) = ty {
+ self.check_impl_trait(ty);
+ }
+ self.check_gat(generics, i.span);
+ false
+ }
+ _ => false,
+ };
+ if let ast::Defaultness::Default(_) = i.kind.defaultness() {
+ // Limit `min_specialization` to only specializing functions.
+ gate_feature_fn!(
+ &self,
+ |x: &Features| x.specialization || (is_fn && x.min_specialization),
+ i.span,
+ sym::specialization,
+ "specialization is unstable"
+ );
+ }
+ visit::walk_assoc_item(self, i, ctxt)
+ }
+}
+
+pub fn check_crate(krate: &ast::Crate, sess: &Session) {
+ maybe_stage_features(sess, krate);
+ check_incompatible_features(sess);
+ let mut visitor = PostExpansionVisitor { sess, features: &sess.features_untracked() };
+
+ let spans = sess.parse_sess.gated_spans.spans.borrow();
+ macro_rules! gate_all {
+ ($gate:ident, $msg:literal, $help:literal) => {
+ if let Some(spans) = spans.get(&sym::$gate) {
+ for span in spans {
+ gate_feature_post!(&visitor, $gate, *span, $msg, $help);
+ }
+ }
+ };
+ ($gate:ident, $msg:literal) => {
+ if let Some(spans) = spans.get(&sym::$gate) {
+ for span in spans {
+ gate_feature_post!(&visitor, $gate, *span, $msg);
+ }
+ }
+ };
+ }
+ gate_all!(
+ if_let_guard,
+ "`if let` guards are experimental",
+ "you can write `if matches!(<expr>, <pattern>)` instead of `if let <pattern> = <expr>`"
+ );
+ gate_all!(let_chains, "`let` expressions in this position are unstable");
+ gate_all!(
+ async_closure,
+ "async closures are unstable",
+ "to use an async block, remove the `||`: `async {`"
+ );
+ gate_all!(
+ closure_lifetime_binder,
+ "`for<...>` binders for closures are experimental",
+ "consider removing `for<...>`"
+ );
+ gate_all!(more_qualified_paths, "usage of qualified paths in this context is experimental");
+ gate_all!(generators, "yield syntax is experimental");
+ gate_all!(raw_ref_op, "raw address of syntax is experimental");
+ gate_all!(const_trait_impl, "const trait impls are experimental");
+ gate_all!(half_open_range_patterns, "half-open range patterns are unstable");
+ gate_all!(inline_const, "inline-const is experimental");
+ gate_all!(inline_const_pat, "inline-const in pattern position is experimental");
+ gate_all!(associated_const_equality, "associated const equality is incomplete");
+ gate_all!(yeet_expr, "`do yeet` expression is experimental");
+
+ // All uses of `gate_all!` below this point were added in #65742,
+ // and subsequently disabled (with the non-early gating readded).
+ macro_rules! gate_all {
+ ($gate:ident, $msg:literal) => {
+ // FIXME(eddyb) do something more useful than always
+ // disabling these uses of early feature-gatings.
+ if false {
+ for span in spans.get(&sym::$gate).unwrap_or(&vec![]) {
+ gate_feature_post!(&visitor, $gate, *span, $msg);
+ }
+ }
+ };
+ }
+
+ gate_all!(trait_alias, "trait aliases are experimental");
+ gate_all!(associated_type_bounds, "associated type bounds are unstable");
+ gate_all!(decl_macro, "`macro` is experimental");
+ gate_all!(box_patterns, "box pattern syntax is experimental");
+ gate_all!(exclusive_range_pattern, "exclusive range pattern syntax is experimental");
+ gate_all!(try_blocks, "`try` blocks are unstable");
+ gate_all!(label_break_value, "labels on blocks are unstable");
+ gate_all!(box_syntax, "box expression syntax is experimental; you can call `Box::new` instead");
+ // To avoid noise about type ascription in common syntax errors,
+ // only emit if it is the *only* error. (Also check it last.)
+ if sess.parse_sess.span_diagnostic.err_count() == 0 {
+ gate_all!(type_ascription, "type ascription is experimental");
+ }
+
+ visit::walk_crate(&mut visitor, krate);
+}
+
+fn maybe_stage_features(sess: &Session, krate: &ast::Crate) {
+ // checks if `#![feature]` has been used to enable any lang feature
+ // does not check the same for lib features unless there's at least one
+ // declared lang feature
+ if !sess.opts.unstable_features.is_nightly_build() {
+ let lang_features = &sess.features_untracked().declared_lang_features;
+ if lang_features.len() == 0 {
+ return;
+ }
+ for attr in krate.attrs.iter().filter(|attr| attr.has_name(sym::feature)) {
+ let mut err = struct_span_err!(
+ sess.parse_sess.span_diagnostic,
+ attr.span,
+ E0554,
+ "`#![feature]` may not be used on the {} release channel",
+ option_env!("CFG_RELEASE_CHANNEL").unwrap_or("(unknown)")
+ );
+ let mut all_stable = true;
+ for ident in
+ attr.meta_item_list().into_iter().flatten().flat_map(|nested| nested.ident())
+ {
+ let name = ident.name;
+ let stable_since = lang_features
+ .iter()
+ .flat_map(|&(feature, _, since)| if feature == name { since } else { None })
+ .next();
+ if let Some(since) = stable_since {
+ err.help(&format!(
+ "the feature `{}` has been stable since {} and no longer requires \
+ an attribute to enable",
+ name, since
+ ));
+ } else {
+ all_stable = false;
+ }
+ }
+ if all_stable {
+ err.span_suggestion(
+ attr.span,
+ "remove the attribute",
+ "",
+ Applicability::MachineApplicable,
+ );
+ }
+ err.emit();
+ }
+ }
+}
+
+fn check_incompatible_features(sess: &Session) {
+ let features = sess.features_untracked();
+
+ let declared_features = features
+ .declared_lang_features
+ .iter()
+ .copied()
+ .map(|(name, span, _)| (name, span))
+ .chain(features.declared_lib_features.iter().copied());
+
+ for (f1, f2) in rustc_feature::INCOMPATIBLE_FEATURES
+ .iter()
+ .filter(|&&(f1, f2)| features.enabled(f1) && features.enabled(f2))
+ {
+ if let Some((f1_name, f1_span)) = declared_features.clone().find(|(name, _)| name == f1) {
+ if let Some((f2_name, f2_span)) = declared_features.clone().find(|(name, _)| name == f2)
+ {
+ let spans = vec![f1_span, f2_span];
+ sess.struct_span_err(
+ spans.clone(),
+ &format!(
+ "features `{}` and `{}` are incompatible, using them at the same time \
+ is not allowed",
+ f1_name, f2_name
+ ),
+ )
+ .help("remove one of these features")
+ .emit();
+ }
+ }
+ }
+}
diff --git a/compiler/rustc_ast_passes/src/lib.rs b/compiler/rustc_ast_passes/src/lib.rs
new file mode 100644
index 000000000..9d52c3288
--- /dev/null
+++ b/compiler/rustc_ast_passes/src/lib.rs
@@ -0,0 +1,18 @@
+//! The `rustc_ast_passes` crate contains passes which validate the AST in `syntax`
+//! parsed by `rustc_parse` and then lowered, after the passes in this crate,
+//! by `rustc_ast_lowering`.
+//!
+//! The crate also contains other misc AST visitors, e.g. `node_count` and `show_span`.
+
+#![allow(rustc::potential_query_instability)]
+#![feature(box_patterns)]
+#![feature(if_let_guard)]
+#![feature(iter_is_partitioned)]
+#![feature(let_chains)]
+#![feature(let_else)]
+#![recursion_limit = "256"]
+
+pub mod ast_validation;
+pub mod feature_gate;
+pub mod node_count;
+pub mod show_span;
diff --git a/compiler/rustc_ast_passes/src/node_count.rs b/compiler/rustc_ast_passes/src/node_count.rs
new file mode 100644
index 000000000..9c7369c83
--- /dev/null
+++ b/compiler/rustc_ast_passes/src/node_count.rs
@@ -0,0 +1,135 @@
+// Simply gives a rough count of the number of nodes in an AST.
+
+use rustc_ast::visit::*;
+use rustc_ast::*;
+use rustc_span::symbol::Ident;
+use rustc_span::Span;
+
+pub struct NodeCounter {
+ pub count: usize,
+}
+
+impl NodeCounter {
+ pub fn new() -> NodeCounter {
+ NodeCounter { count: 0 }
+ }
+}
+
+impl<'ast> Visitor<'ast> for NodeCounter {
+ fn visit_ident(&mut self, _ident: Ident) {
+ self.count += 1;
+ }
+ fn visit_foreign_item(&mut self, i: &ForeignItem) {
+ self.count += 1;
+ walk_foreign_item(self, i)
+ }
+ fn visit_item(&mut self, i: &Item) {
+ self.count += 1;
+ walk_item(self, i)
+ }
+ fn visit_local(&mut self, l: &Local) {
+ self.count += 1;
+ walk_local(self, l)
+ }
+ fn visit_block(&mut self, b: &Block) {
+ self.count += 1;
+ walk_block(self, b)
+ }
+ fn visit_stmt(&mut self, s: &Stmt) {
+ self.count += 1;
+ walk_stmt(self, s)
+ }
+ fn visit_arm(&mut self, a: &Arm) {
+ self.count += 1;
+ walk_arm(self, a)
+ }
+ fn visit_pat(&mut self, p: &Pat) {
+ self.count += 1;
+ walk_pat(self, p)
+ }
+ fn visit_expr(&mut self, ex: &Expr) {
+ self.count += 1;
+ walk_expr(self, ex)
+ }
+ fn visit_ty(&mut self, t: &Ty) {
+ self.count += 1;
+ walk_ty(self, t)
+ }
+ fn visit_generic_param(&mut self, param: &GenericParam) {
+ self.count += 1;
+ walk_generic_param(self, param)
+ }
+ fn visit_generics(&mut self, g: &Generics) {
+ self.count += 1;
+ walk_generics(self, g)
+ }
+ fn visit_fn(&mut self, fk: visit::FnKind<'_>, s: Span, _: NodeId) {
+ self.count += 1;
+ walk_fn(self, fk, s)
+ }
+ fn visit_assoc_item(&mut self, ti: &AssocItem, ctxt: AssocCtxt) {
+ self.count += 1;
+ walk_assoc_item(self, ti, ctxt);
+ }
+ fn visit_trait_ref(&mut self, t: &TraitRef) {
+ self.count += 1;
+ walk_trait_ref(self, t)
+ }
+ fn visit_param_bound(&mut self, bounds: &GenericBound, _ctxt: BoundKind) {
+ self.count += 1;
+ walk_param_bound(self, bounds)
+ }
+ fn visit_poly_trait_ref(&mut self, t: &PolyTraitRef, m: &TraitBoundModifier) {
+ self.count += 1;
+ walk_poly_trait_ref(self, t, m)
+ }
+ fn visit_variant_data(&mut self, s: &VariantData) {
+ self.count += 1;
+ walk_struct_def(self, s)
+ }
+ fn visit_field_def(&mut self, s: &FieldDef) {
+ self.count += 1;
+ walk_field_def(self, s)
+ }
+ fn visit_enum_def(
+ &mut self,
+ enum_definition: &EnumDef,
+ generics: &Generics,
+ item_id: NodeId,
+ _: Span,
+ ) {
+ self.count += 1;
+ walk_enum_def(self, enum_definition, generics, item_id)
+ }
+ fn visit_variant(&mut self, v: &Variant) {
+ self.count += 1;
+ walk_variant(self, v)
+ }
+ fn visit_lifetime(&mut self, lifetime: &Lifetime, _: visit::LifetimeCtxt) {
+ self.count += 1;
+ walk_lifetime(self, lifetime)
+ }
+ fn visit_mac_call(&mut self, mac: &MacCall) {
+ self.count += 1;
+ walk_mac(self, mac)
+ }
+ fn visit_path(&mut self, path: &Path, _id: NodeId) {
+ self.count += 1;
+ walk_path(self, path)
+ }
+ fn visit_use_tree(&mut self, use_tree: &UseTree, id: NodeId, _nested: bool) {
+ self.count += 1;
+ walk_use_tree(self, use_tree, id)
+ }
+ fn visit_generic_args(&mut self, path_span: Span, generic_args: &GenericArgs) {
+ self.count += 1;
+ walk_generic_args(self, path_span, generic_args)
+ }
+ fn visit_assoc_constraint(&mut self, constraint: &AssocConstraint) {
+ self.count += 1;
+ walk_assoc_constraint(self, constraint)
+ }
+ fn visit_attribute(&mut self, _attr: &Attribute) {
+ self.count += 1;
+ }
+}
diff --git a/compiler/rustc_ast_passes/src/show_span.rs b/compiler/rustc_ast_passes/src/show_span.rs
new file mode 100644
index 000000000..27637e311
--- /dev/null
+++ b/compiler/rustc_ast_passes/src/show_span.rs
@@ -0,0 +1,65 @@
+//! Span debugger
+//!
+//! This module shows spans for all expressions in the crate
+//! to help with compiler debugging.
+
+use std::str::FromStr;
+
+use rustc_ast as ast;
+use rustc_ast::visit;
+use rustc_ast::visit::Visitor;
+
+enum Mode {
+ Expression,
+ Pattern,
+ Type,
+}
+
+impl FromStr for Mode {
+ type Err = ();
+ fn from_str(s: &str) -> Result<Mode, ()> {
+ let mode = match s {
+ "expr" => Mode::Expression,
+ "pat" => Mode::Pattern,
+ "ty" => Mode::Type,
+ _ => return Err(()),
+ };
+ Ok(mode)
+ }
+}
+
+struct ShowSpanVisitor<'a> {
+ span_diagnostic: &'a rustc_errors::Handler,
+ mode: Mode,
+}
+
+impl<'a> Visitor<'a> for ShowSpanVisitor<'a> {
+ fn visit_expr(&mut self, e: &'a ast::Expr) {
+ if let Mode::Expression = self.mode {
+ self.span_diagnostic.span_warn(e.span, "expression");
+ }
+ visit::walk_expr(self, e);
+ }
+
+ fn visit_pat(&mut self, p: &'a ast::Pat) {
+ if let Mode::Pattern = self.mode {
+ self.span_diagnostic.span_warn(p.span, "pattern");
+ }
+ visit::walk_pat(self, p);
+ }
+
+ fn visit_ty(&mut self, t: &'a ast::Ty) {
+ if let Mode::Type = self.mode {
+ self.span_diagnostic.span_warn(t.span, "type");
+ }
+ visit::walk_ty(self, t);
+ }
+}
+
+pub fn run(span_diagnostic: &rustc_errors::Handler, mode: &str, krate: &ast::Crate) {
+ let Ok(mode) = mode.parse() else {
+ return;
+ };
+ let mut v = ShowSpanVisitor { span_diagnostic, mode };
+ visit::walk_crate(&mut v, krate);
+}
diff --git a/compiler/rustc_ast_pretty/Cargo.toml b/compiler/rustc_ast_pretty/Cargo.toml
new file mode 100644
index 000000000..5ad8714e9
--- /dev/null
+++ b/compiler/rustc_ast_pretty/Cargo.toml
@@ -0,0 +1,11 @@
+[package]
+name = "rustc_ast_pretty"
+version = "0.0.0"
+edition = "2021"
+
+[lib]
+doctest = false
+
+[dependencies]
+rustc_span = { path = "../rustc_span" }
+rustc_ast = { path = "../rustc_ast" }
diff --git a/compiler/rustc_ast_pretty/src/helpers.rs b/compiler/rustc_ast_pretty/src/helpers.rs
new file mode 100644
index 000000000..5ec71cddf
--- /dev/null
+++ b/compiler/rustc_ast_pretty/src/helpers.rs
@@ -0,0 +1,48 @@
+use crate::pp::Printer;
+use std::borrow::Cow;
+
+impl Printer {
+ pub fn word_space<W: Into<Cow<'static, str>>>(&mut self, w: W) {
+ self.word(w);
+ self.space();
+ }
+
+ pub fn popen(&mut self) {
+ self.word("(");
+ }
+
+ pub fn pclose(&mut self) {
+ self.word(")");
+ }
+
+ pub fn hardbreak_if_not_bol(&mut self) {
+ if !self.is_beginning_of_line() {
+ self.hardbreak()
+ }
+ }
+
+ pub fn space_if_not_bol(&mut self) {
+ if !self.is_beginning_of_line() {
+ self.space();
+ }
+ }
+
+ pub fn nbsp(&mut self) {
+ self.word(" ")
+ }
+
+ pub fn word_nbsp<S: Into<Cow<'static, str>>>(&mut self, w: S) {
+ self.word(w);
+ self.nbsp()
+ }
+
+ // Synthesizes a comment that was not textually present in the original
+ // source file.
+ pub fn synth_comment(&mut self, text: impl Into<Cow<'static, str>>) {
+ self.word("/*");
+ self.space();
+ self.word(text);
+ self.space();
+ self.word("*/")
+ }
+}
diff --git a/compiler/rustc_ast_pretty/src/lib.rs b/compiler/rustc_ast_pretty/src/lib.rs
new file mode 100644
index 000000000..79178830b
--- /dev/null
+++ b/compiler/rustc_ast_pretty/src/lib.rs
@@ -0,0 +1,8 @@
+#![feature(associated_type_bounds)]
+#![feature(box_patterns)]
+#![feature(with_negative_coherence)]
+#![recursion_limit = "256"]
+
+mod helpers;
+pub mod pp;
+pub mod pprust;
diff --git a/compiler/rustc_ast_pretty/src/pp.rs b/compiler/rustc_ast_pretty/src/pp.rs
new file mode 100644
index 000000000..c93022308
--- /dev/null
+++ b/compiler/rustc_ast_pretty/src/pp.rs
@@ -0,0 +1,451 @@
+//! This pretty-printer is a direct reimplementation of Philip Karlton's
+//! Mesa pretty-printer, as described in the appendix to
+//! Derek C. Oppen, "Pretty Printing" (1979),
+//! Stanford Computer Science Department STAN-CS-79-770,
+//! <http://i.stanford.edu/pub/cstr/reports/cs/tr/79/770/CS-TR-79-770.pdf>.
+//!
+//! The algorithm's aim is to break a stream into as few lines as possible
+//! while respecting the indentation-consistency requirements of the enclosing
+//! block, and avoiding breaking at silly places on block boundaries, for
+//! example, between "x" and ")" in "x)".
+//!
+//! I am implementing this algorithm because it comes with 20 pages of
+//! documentation explaining its theory, and because it addresses the set of
+//! concerns I've seen other pretty-printers fall down on. Weirdly. Even though
+//! it's 32 years old. What can I say?
+//!
+//! Despite some redundancies and quirks in the way it's implemented in that
+//! paper, I've opted to keep the implementation here as similar as I can,
+//! changing only what was blatantly wrong, a typo, or sufficiently
+//! non-idiomatic rust that it really stuck out.
+//!
+//! In particular you'll see a certain amount of churn related to INTEGER vs.
+//! CARDINAL in the Mesa implementation. Mesa apparently interconverts the two
+//! somewhat readily? In any case, I've used usize for indices-in-buffers and
+//! ints for character-sizes-and-indentation-offsets. This respects the need
+//! for ints to "go negative" while carrying a pending-calculation balance, and
+//! helps differentiate all the numbers flying around internally (slightly).
+//!
+//! I also inverted the indentation arithmetic used in the print stack, since
+//! the Mesa implementation (somewhat randomly) stores the offset on the print
+//! stack in terms of margin-col rather than col itself. I store col.
+//!
+//! I also implemented a small change in the String token, in that I store an
+//! explicit length for the string. For most tokens this is just the length of
+//! the accompanying string. But it's necessary to permit it to differ, for
+//! encoding things that are supposed to "go on their own line" -- certain
+//! classes of comment and blank-line -- where relying on adjacent
+//! hardbreak-like Break tokens with long blankness indication doesn't actually
+//! work. To see why, consider when there is a "thing that should be on its own
+//! line" between two long blocks, say functions. If you put a hardbreak after
+//! each function (or before each) and the breaking algorithm decides to break
+//! there anyways (because the functions themselves are long) you wind up with
+//! extra blank lines. If you don't put hardbreaks you can wind up with the
+//! "thing which should be on its own line" not getting its own line in the
+//! rare case of "really small functions" or such. This re-occurs with comments
+//! and explicit blank lines. So in those cases we use a string with a payload
+//! we want isolated to a line and an explicit length that's huge, surrounded
+//! by two zero-length breaks. The algorithm will try its best to fit it on a
+//! line (which it can't) and so naturally place the content on its own line to
+//! avoid combining it with other lines and making matters even worse.
+//!
+//! # Explanation
+//!
+//! In case you do not have the paper, here is an explanation of what's going
+//! on.
+//!
+//! There is a stream of input tokens flowing through this printer.
+//!
+//! The printer buffers up to 3N tokens inside itself, where N is linewidth.
+//! Yes, linewidth is chars and tokens are multi-char, but in the worst
+//! case every token worth buffering is 1 char long, so it's ok.
+//!
+//! Tokens are String, Break, and Begin/End to delimit blocks.
+//!
+//! Begin tokens can carry an offset, saying "how far to indent when you break
+//! inside here", as well as a flag indicating "consistent" or "inconsistent"
+//! breaking. Consistent breaking means that after the first break, no attempt
+//! will be made to flow subsequent breaks together onto lines. Inconsistent
+//! is the opposite. Inconsistent breaking example would be, say:
+//!
+//! ```ignore (illustrative)
+//! foo(hello, there, good, friends)
+//! ```
+//!
+//! breaking inconsistently to become
+//!
+//! ```ignore (illustrative)
+//! foo(hello, there,
+//! good, friends);
+//! ```
+//!
+//! whereas a consistent breaking would yield:
+//!
+//! ```ignore (illustrative)
+//! foo(hello,
+//! there,
+//! good,
+//! friends);
+//! ```
+//!
+//! That is, in the consistent-break blocks we value vertical alignment
+//! more than the ability to cram stuff onto a line. But in all cases if it
+//! can make a block a one-liner, it'll do so.
+//!
+//! Carrying on with high-level logic:
+//!
+//! The buffered tokens go through a ring-buffer, 'tokens'. The 'left' and
+//! 'right' indices denote the active portion of the ring buffer as well as
+//! describing hypothetical points-in-the-infinite-stream at most 3N tokens
+//! apart (i.e., "not wrapped to ring-buffer boundaries"). The paper will switch
+//! between using 'left' and 'right' terms to denote the wrapped-to-ring-buffer
+//! and point-in-infinite-stream senses freely.
+//!
+//! There is a parallel ring buffer, `size`, that holds the calculated size of
+//! each token. Why calculated? Because for Begin/End pairs, the "size"
+//! includes everything between the pair. That is, the "size" of Begin is
+//! actually the sum of the sizes of everything between Begin and the paired
+//! End that follows. Since that is arbitrarily far in the future, `size` is
+//! being rewritten regularly while the printer runs; in fact most of the
+//! machinery is here to work out `size` entries on the fly (and give up when
+//! they're so obviously over-long that "infinity" is a good enough
+//! approximation for purposes of line breaking).
+//!
+//! The "input side" of the printer is managed as an abstract process called
+//! SCAN, which uses `scan_stack`, to manage calculating `size`. SCAN is, in
+//! other words, the process of calculating 'size' entries.
+//!
+//! The "output side" of the printer is managed by an abstract process called
+//! PRINT, which uses `print_stack`, `margin` and `space` to figure out what to
+//! do with each token/size pair it consumes as it goes. It's trying to consume
+//! the entire buffered window, but can't output anything until the size is >=
+//! 0 (sizes are set to negative while they're pending calculation).
+//!
+//! So SCAN takes input and buffers tokens and pending calculations, while
+//! PRINT gobbles up completed calculations and tokens from the buffer. The
+//! theory is that the two can never get more than 3N tokens apart, because
+//! once there's "obviously" too much data to fit on a line, in a size
+//! calculation, SCAN will write "infinity" to the size and let PRINT consume
+//! it.
+//!
+//! In this implementation (following the paper, again) the SCAN process is the
+//! methods called `Printer::scan_*`, and the 'PRINT' process is the
+//! method called `Printer::print`.
+
+mod convenience;
+mod ring;
+
+use ring::RingBuffer;
+use std::borrow::Cow;
+use std::cmp;
+use std::collections::VecDeque;
+use std::iter;
+
+/// How to break. Described in more detail in the module docs.
+#[derive(Clone, Copy, PartialEq)]
+pub enum Breaks {
+ Consistent,
+ Inconsistent,
+}
+
+#[derive(Clone, Copy, PartialEq)]
+enum IndentStyle {
+ /// Vertically aligned under whatever column this block begins at.
+ ///
+ /// fn demo(arg1: usize,
+ /// arg2: usize) {}
+ Visual,
+ /// Indented relative to the indentation level of the previous line.
+ ///
+ /// fn demo(
+ /// arg1: usize,
+ /// arg2: usize,
+ /// ) {}
+ Block { offset: isize },
+}
+
+#[derive(Clone, Copy, Default, PartialEq)]
+pub struct BreakToken {
+ offset: isize,
+ blank_space: isize,
+ pre_break: Option<char>,
+}
+
+#[derive(Clone, Copy, PartialEq)]
+pub struct BeginToken {
+ indent: IndentStyle,
+ breaks: Breaks,
+}
+
+#[derive(Clone, PartialEq)]
+pub enum Token {
+ // In practice a string token contains either a `&'static str` or a
+ // `String`. `Cow` is overkill for this because we never modify the data,
+ // but it's more convenient than rolling our own more specialized type.
+ String(Cow<'static, str>),
+ Break(BreakToken),
+ Begin(BeginToken),
+ End,
+}
+
+#[derive(Copy, Clone)]
+enum PrintFrame {
+ Fits,
+ Broken { indent: usize, breaks: Breaks },
+}
+
+const SIZE_INFINITY: isize = 0xffff;
+
+/// Target line width.
+const MARGIN: isize = 78;
+/// Every line is allowed at least this much space, even if highly indented.
+const MIN_SPACE: isize = 60;
+
+pub struct Printer {
+ out: String,
+ /// Number of spaces left on line
+ space: isize,
+ /// Ring-buffer of tokens and calculated sizes
+ buf: RingBuffer<BufEntry>,
+ /// Running size of stream "...left"
+ left_total: isize,
+ /// Running size of stream "...right"
+ right_total: isize,
+ /// Pseudo-stack, really a ring too. Holds the
+ /// primary-ring-buffers index of the Begin that started the
+ /// current block, possibly with the most recent Break after that
+ /// Begin (if there is any) on top of it. Stuff is flushed off the
+ /// bottom as it becomes irrelevant due to the primary ring-buffer
+ /// advancing.
+ scan_stack: VecDeque<usize>,
+ /// Stack of blocks-in-progress being flushed by print
+ print_stack: Vec<PrintFrame>,
+ /// Level of indentation of current line
+ indent: usize,
+ /// Buffered indentation to avoid writing trailing whitespace
+ pending_indentation: isize,
+ /// The token most recently popped from the left boundary of the
+ /// ring-buffer for printing
+ last_printed: Option<Token>,
+}
+
+#[derive(Clone)]
+struct BufEntry {
+ token: Token,
+ size: isize,
+}
+
+impl Printer {
+ pub fn new() -> Self {
+ Printer {
+ out: String::new(),
+ space: MARGIN,
+ buf: RingBuffer::new(),
+ left_total: 0,
+ right_total: 0,
+ scan_stack: VecDeque::new(),
+ print_stack: Vec::new(),
+ indent: 0,
+ pending_indentation: 0,
+ last_printed: None,
+ }
+ }
+
+ pub fn last_token(&self) -> Option<&Token> {
+ self.last_token_still_buffered().or_else(|| self.last_printed.as_ref())
+ }
+
+ pub fn last_token_still_buffered(&self) -> Option<&Token> {
+ self.buf.last().map(|last| &last.token)
+ }
+
+ /// Be very careful with this!
+ pub fn replace_last_token_still_buffered(&mut self, token: Token) {
+ self.buf.last_mut().unwrap().token = token;
+ }
+
+ fn scan_eof(&mut self) {
+ if !self.scan_stack.is_empty() {
+ self.check_stack(0);
+ self.advance_left();
+ }
+ }
+
+ fn scan_begin(&mut self, token: BeginToken) {
+ if self.scan_stack.is_empty() {
+ self.left_total = 1;
+ self.right_total = 1;
+ self.buf.clear();
+ }
+ let right = self.buf.push(BufEntry { token: Token::Begin(token), size: -self.right_total });
+ self.scan_stack.push_back(right);
+ }
+
+ fn scan_end(&mut self) {
+ if self.scan_stack.is_empty() {
+ self.print_end();
+ } else {
+ let right = self.buf.push(BufEntry { token: Token::End, size: -1 });
+ self.scan_stack.push_back(right);
+ }
+ }
+
+ fn scan_break(&mut self, token: BreakToken) {
+ if self.scan_stack.is_empty() {
+ self.left_total = 1;
+ self.right_total = 1;
+ self.buf.clear();
+ } else {
+ self.check_stack(0);
+ }
+ let right = self.buf.push(BufEntry { token: Token::Break(token), size: -self.right_total });
+ self.scan_stack.push_back(right);
+ self.right_total += token.blank_space;
+ }
+
+ fn scan_string(&mut self, string: Cow<'static, str>) {
+ if self.scan_stack.is_empty() {
+ self.print_string(&string);
+ } else {
+ let len = string.len() as isize;
+ self.buf.push(BufEntry { token: Token::String(string), size: len });
+ self.right_total += len;
+ self.check_stream();
+ }
+ }
+
+ pub fn offset(&mut self, offset: isize) {
+ if let Some(BufEntry { token: Token::Break(token), .. }) = &mut self.buf.last_mut() {
+ token.offset += offset;
+ }
+ }
+
+ fn check_stream(&mut self) {
+ while self.right_total - self.left_total > self.space {
+ if *self.scan_stack.front().unwrap() == self.buf.index_of_first() {
+ self.scan_stack.pop_front().unwrap();
+ self.buf.first_mut().unwrap().size = SIZE_INFINITY;
+ }
+ self.advance_left();
+ if self.buf.is_empty() {
+ break;
+ }
+ }
+ }
+
+ fn advance_left(&mut self) {
+ while self.buf.first().unwrap().size >= 0 {
+ let left = self.buf.pop_first().unwrap();
+
+ match &left.token {
+ Token::String(string) => {
+ self.left_total += string.len() as isize;
+ self.print_string(string);
+ }
+ Token::Break(token) => {
+ self.left_total += token.blank_space;
+ self.print_break(*token, left.size);
+ }
+ Token::Begin(token) => self.print_begin(*token, left.size),
+ Token::End => self.print_end(),
+ }
+
+ self.last_printed = Some(left.token);
+
+ if self.buf.is_empty() {
+ break;
+ }
+ }
+ }
+
+ fn check_stack(&mut self, mut depth: usize) {
+ while let Some(&index) = self.scan_stack.back() {
+ let mut entry = &mut self.buf[index];
+ match entry.token {
+ Token::Begin(_) => {
+ if depth == 0 {
+ break;
+ }
+ self.scan_stack.pop_back().unwrap();
+ entry.size += self.right_total;
+ depth -= 1;
+ }
+ Token::End => {
+ // paper says + not =, but that makes no sense.
+ self.scan_stack.pop_back().unwrap();
+ entry.size = 1;
+ depth += 1;
+ }
+ _ => {
+ self.scan_stack.pop_back().unwrap();
+ entry.size += self.right_total;
+ if depth == 0 {
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ fn get_top(&self) -> PrintFrame {
+ *self
+ .print_stack
+ .last()
+ .unwrap_or(&PrintFrame::Broken { indent: 0, breaks: Breaks::Inconsistent })
+ }
+
+ fn print_begin(&mut self, token: BeginToken, size: isize) {
+ if size > self.space {
+ self.print_stack.push(PrintFrame::Broken { indent: self.indent, breaks: token.breaks });
+ self.indent = match token.indent {
+ IndentStyle::Block { offset } => {
+ usize::try_from(self.indent as isize + offset).unwrap()
+ }
+ IndentStyle::Visual => (MARGIN - self.space) as usize,
+ };
+ } else {
+ self.print_stack.push(PrintFrame::Fits);
+ }
+ }
+
+ fn print_end(&mut self) {
+ if let PrintFrame::Broken { indent, .. } = self.print_stack.pop().unwrap() {
+ self.indent = indent;
+ }
+ }
+
+ fn print_break(&mut self, token: BreakToken, size: isize) {
+ let fits = match self.get_top() {
+ PrintFrame::Fits => true,
+ PrintFrame::Broken { breaks: Breaks::Consistent, .. } => false,
+ PrintFrame::Broken { breaks: Breaks::Inconsistent, .. } => size <= self.space,
+ };
+ if fits {
+ self.pending_indentation += token.blank_space;
+ self.space -= token.blank_space;
+ } else {
+ if let Some(pre_break) = token.pre_break {
+ self.out.push(pre_break);
+ }
+ self.out.push('\n');
+ let indent = self.indent as isize + token.offset;
+ self.pending_indentation = indent;
+ self.space = cmp::max(MARGIN - indent, MIN_SPACE);
+ }
+ }
+
+ fn print_string(&mut self, string: &str) {
+ // Write the pending indent. A more concise way of doing this would be:
+ //
+ // write!(self.out, "{: >n$}", "", n = self.pending_indentation as usize)?;
+ //
+ // But that is significantly slower. This code is sufficiently hot, and indents can get
+ // sufficiently large, that the difference is significant on some workloads.
+ self.out.reserve(self.pending_indentation as usize);
+ self.out.extend(iter::repeat(' ').take(self.pending_indentation as usize));
+ self.pending_indentation = 0;
+
+ self.out.push_str(string);
+ self.space -= string.len() as isize;
+ }
+}
diff --git a/compiler/rustc_ast_pretty/src/pp/convenience.rs b/compiler/rustc_ast_pretty/src/pp/convenience.rs
new file mode 100644
index 000000000..93310dd45
--- /dev/null
+++ b/compiler/rustc_ast_pretty/src/pp/convenience.rs
@@ -0,0 +1,94 @@
+use crate::pp::{BeginToken, BreakToken, Breaks, IndentStyle, Printer, Token, SIZE_INFINITY};
+use std::borrow::Cow;
+
+impl Printer {
+ /// "raw box"
+ pub fn rbox(&mut self, indent: isize, breaks: Breaks) {
+ self.scan_begin(BeginToken { indent: IndentStyle::Block { offset: indent }, breaks })
+ }
+
+ /// Inconsistent breaking box
+ pub fn ibox(&mut self, indent: isize) {
+ self.rbox(indent, Breaks::Inconsistent)
+ }
+
+ /// Consistent breaking box
+ pub fn cbox(&mut self, indent: isize) {
+ self.rbox(indent, Breaks::Consistent)
+ }
+
+ pub fn visual_align(&mut self) {
+ self.scan_begin(BeginToken { indent: IndentStyle::Visual, breaks: Breaks::Consistent });
+ }
+
+ pub fn break_offset(&mut self, n: usize, off: isize) {
+ self.scan_break(BreakToken {
+ offset: off,
+ blank_space: n as isize,
+ ..BreakToken::default()
+ });
+ }
+
+ pub fn end(&mut self) {
+ self.scan_end()
+ }
+
+ pub fn eof(mut self) -> String {
+ self.scan_eof();
+ self.out
+ }
+
+ pub fn word<S: Into<Cow<'static, str>>>(&mut self, wrd: S) {
+ let string = wrd.into();
+ self.scan_string(string)
+ }
+
+ fn spaces(&mut self, n: usize) {
+ self.break_offset(n, 0)
+ }
+
+ pub fn zerobreak(&mut self) {
+ self.spaces(0)
+ }
+
+ pub fn space(&mut self) {
+ self.spaces(1)
+ }
+
+ pub fn hardbreak(&mut self) {
+ self.spaces(SIZE_INFINITY as usize)
+ }
+
+ pub fn is_beginning_of_line(&self) -> bool {
+ match self.last_token() {
+ Some(last_token) => last_token.is_hardbreak_tok(),
+ None => true,
+ }
+ }
+
+ pub fn hardbreak_tok_offset(off: isize) -> Token {
+ Token::Break(BreakToken {
+ offset: off,
+ blank_space: SIZE_INFINITY,
+ ..BreakToken::default()
+ })
+ }
+
+ pub fn trailing_comma(&mut self) {
+ self.scan_break(BreakToken { pre_break: Some(','), ..BreakToken::default() });
+ }
+
+ pub fn trailing_comma_or_space(&mut self) {
+ self.scan_break(BreakToken {
+ blank_space: 1,
+ pre_break: Some(','),
+ ..BreakToken::default()
+ });
+ }
+}
+
+impl Token {
+ pub fn is_hardbreak_tok(&self) -> bool {
+ *self == Printer::hardbreak_tok_offset(0)
+ }
+}
diff --git a/compiler/rustc_ast_pretty/src/pp/ring.rs b/compiler/rustc_ast_pretty/src/pp/ring.rs
new file mode 100644
index 000000000..8187394fe
--- /dev/null
+++ b/compiler/rustc_ast_pretty/src/pp/ring.rs
@@ -0,0 +1,77 @@
+use std::collections::VecDeque;
+use std::ops::{Index, IndexMut};
+
+/// A view onto a finite range of an infinitely long sequence of T.
+///
+/// The Ts are indexed 0..infinity. A RingBuffer begins as a view of elements
+/// 0..0 (i.e. nothing). The user of the RingBuffer advances its left and right
+/// position independently, although only in the positive direction, and only
+/// with left <= right at all times.
+///
+/// Holding a RingBuffer whose view is elements left..right gives the ability to
+/// use Index and IndexMut to access elements i in the infinitely long queue for
+/// which left <= i < right.
+pub struct RingBuffer<T> {
+ data: VecDeque<T>,
+ // Abstract index of data[0] in the infinitely sized queue.
+ offset: usize,
+}
+
+impl<T> RingBuffer<T> {
+ pub fn new() -> Self {
+ RingBuffer { data: VecDeque::new(), offset: 0 }
+ }
+
+ pub fn is_empty(&self) -> bool {
+ self.data.is_empty()
+ }
+
+ pub fn push(&mut self, value: T) -> usize {
+ let index = self.offset + self.data.len();
+ self.data.push_back(value);
+ index
+ }
+
+ pub fn clear(&mut self) {
+ self.data.clear();
+ }
+
+ pub fn index_of_first(&self) -> usize {
+ self.offset
+ }
+
+ pub fn first(&self) -> Option<&T> {
+ self.data.front()
+ }
+
+ pub fn first_mut(&mut self) -> Option<&mut T> {
+ self.data.front_mut()
+ }
+
+ pub fn pop_first(&mut self) -> Option<T> {
+ let first = self.data.pop_front()?;
+ self.offset += 1;
+ Some(first)
+ }
+
+ pub fn last(&self) -> Option<&T> {
+ self.data.back()
+ }
+
+ pub fn last_mut(&mut self) -> Option<&mut T> {
+ self.data.back_mut()
+ }
+}
+
+impl<T> Index<usize> for RingBuffer<T> {
+ type Output = T;
+ fn index(&self, index: usize) -> &Self::Output {
+ &self.data[index.checked_sub(self.offset).unwrap()]
+ }
+}
+
+impl<T> IndexMut<usize> for RingBuffer<T> {
+ fn index_mut(&mut self, index: usize) -> &mut Self::Output {
+ &mut self.data[index.checked_sub(self.offset).unwrap()]
+ }
+}
diff --git a/compiler/rustc_ast_pretty/src/pprust/mod.rs b/compiler/rustc_ast_pretty/src/pprust/mod.rs
new file mode 100644
index 000000000..ac9e7d06c
--- /dev/null
+++ b/compiler/rustc_ast_pretty/src/pprust/mod.rs
@@ -0,0 +1,86 @@
+#[cfg(test)]
+mod tests;
+
+pub mod state;
+pub use state::{print_crate, AnnNode, Comments, PpAnn, PrintState, State};
+
+use rustc_ast as ast;
+use rustc_ast::token::{Nonterminal, Token, TokenKind};
+use rustc_ast::tokenstream::{TokenStream, TokenTree};
+
+use std::borrow::Cow;
+
+pub fn nonterminal_to_string(nt: &Nonterminal) -> String {
+ State::new().nonterminal_to_string(nt)
+}
+
+/// Print the token kind precisely, without converting `$crate` into its respective crate name.
+pub fn token_kind_to_string(tok: &TokenKind) -> Cow<'static, str> {
+ State::new().token_kind_to_string(tok)
+}
+
+/// Print the token precisely, without converting `$crate` into its respective crate name.
+pub fn token_to_string(token: &Token) -> Cow<'static, str> {
+ State::new().token_to_string(token)
+}
+
+pub fn ty_to_string(ty: &ast::Ty) -> String {
+ State::new().ty_to_string(ty)
+}
+
+pub fn bounds_to_string(bounds: &[ast::GenericBound]) -> String {
+ State::new().bounds_to_string(bounds)
+}
+
+pub fn pat_to_string(pat: &ast::Pat) -> String {
+ State::new().pat_to_string(pat)
+}
+
+pub fn expr_to_string(e: &ast::Expr) -> String {
+ State::new().expr_to_string(e)
+}
+
+pub fn tt_to_string(tt: &TokenTree) -> String {
+ State::new().tt_to_string(tt)
+}
+
+pub fn tts_to_string(tokens: &TokenStream) -> String {
+ State::new().tts_to_string(tokens)
+}
+
+pub fn item_to_string(i: &ast::Item) -> String {
+ State::new().item_to_string(i)
+}
+
+pub fn path_to_string(p: &ast::Path) -> String {
+ State::new().path_to_string(p)
+}
+
+pub fn path_segment_to_string(p: &ast::PathSegment) -> String {
+ State::new().path_segment_to_string(p)
+}
+
+pub fn vis_to_string(v: &ast::Visibility) -> String {
+ State::new().vis_to_string(v)
+}
+
+pub fn meta_list_item_to_string(li: &ast::NestedMetaItem) -> String {
+ State::new().meta_list_item_to_string(li)
+}
+
+pub fn attribute_to_string(attr: &ast::Attribute) -> String {
+ State::new().attribute_to_string(attr)
+}
+
+pub fn to_string(f: impl FnOnce(&mut State<'_>)) -> String {
+ State::to_string(f)
+}
+
+pub fn crate_to_string_for_macros(krate: &ast::Crate) -> String {
+ State::to_string(|s| {
+ s.print_inner_attributes(&krate.attrs);
+ for item in &krate.items {
+ s.print_item(item);
+ }
+ })
+}
diff --git a/compiler/rustc_ast_pretty/src/pprust/state.rs b/compiler/rustc_ast_pretty/src/pprust/state.rs
new file mode 100644
index 000000000..5eb7bf634
--- /dev/null
+++ b/compiler/rustc_ast_pretty/src/pprust/state.rs
@@ -0,0 +1,1770 @@
+mod delimited;
+mod expr;
+mod item;
+
+use crate::pp::Breaks::{Consistent, Inconsistent};
+use crate::pp::{self, Breaks};
+
+use rustc_ast::ptr::P;
+use rustc_ast::token::{self, BinOpToken, CommentKind, Delimiter, Nonterminal, Token, TokenKind};
+use rustc_ast::tokenstream::{TokenStream, TokenTree};
+use rustc_ast::util::classify;
+use rustc_ast::util::comments::{gather_comments, Comment, CommentStyle};
+use rustc_ast::util::parser;
+use rustc_ast::{self as ast, BlockCheckMode, PatKind, RangeEnd, RangeSyntax};
+use rustc_ast::{attr, Term};
+use rustc_ast::{GenericArg, MacArgs, MacArgsEq};
+use rustc_ast::{GenericBound, SelfKind, TraitBoundModifier};
+use rustc_ast::{InlineAsmOperand, InlineAsmRegOrRegClass};
+use rustc_ast::{InlineAsmOptions, InlineAsmTemplatePiece};
+use rustc_span::edition::Edition;
+use rustc_span::source_map::{SourceMap, Spanned};
+use rustc_span::symbol::{kw, sym, Ident, IdentPrinter, Symbol};
+use rustc_span::{BytePos, FileName, Span};
+
+use std::borrow::Cow;
+
+pub use self::delimited::IterDelimited;
+
+pub enum MacHeader<'a> {
+ Path(&'a ast::Path),
+ Keyword(&'static str),
+}
+
+pub enum AnnNode<'a> {
+ Ident(&'a Ident),
+ Name(&'a Symbol),
+ Block(&'a ast::Block),
+ Item(&'a ast::Item),
+ SubItem(ast::NodeId),
+ Expr(&'a ast::Expr),
+ Pat(&'a ast::Pat),
+ Crate(&'a ast::Crate),
+}
+
+pub trait PpAnn {
+ fn pre(&self, _state: &mut State<'_>, _node: AnnNode<'_>) {}
+ fn post(&self, _state: &mut State<'_>, _node: AnnNode<'_>) {}
+}
+
+#[derive(Copy, Clone)]
+pub struct NoAnn;
+
+impl PpAnn for NoAnn {}
+
+pub struct Comments<'a> {
+ sm: &'a SourceMap,
+ comments: Vec<Comment>,
+ current: usize,
+}
+
+impl<'a> Comments<'a> {
+ pub fn new(sm: &'a SourceMap, filename: FileName, input: String) -> Comments<'a> {
+ let comments = gather_comments(sm, filename, input);
+ Comments { sm, comments, current: 0 }
+ }
+
+ pub fn next(&self) -> Option<Comment> {
+ self.comments.get(self.current).cloned()
+ }
+
+ pub fn trailing_comment(
+ &self,
+ span: rustc_span::Span,
+ next_pos: Option<BytePos>,
+ ) -> Option<Comment> {
+ if let Some(cmnt) = self.next() {
+ if cmnt.style != CommentStyle::Trailing {
+ return None;
+ }
+ let span_line = self.sm.lookup_char_pos(span.hi());
+ let comment_line = self.sm.lookup_char_pos(cmnt.pos);
+ let next = next_pos.unwrap_or_else(|| cmnt.pos + BytePos(1));
+ if span.hi() < cmnt.pos && cmnt.pos < next && span_line.line == comment_line.line {
+ return Some(cmnt);
+ }
+ }
+
+ None
+ }
+}
+
+pub struct State<'a> {
+ pub s: pp::Printer,
+ comments: Option<Comments<'a>>,
+ ann: &'a (dyn PpAnn + 'a),
+}
+
+pub(crate) const INDENT_UNIT: isize = 4;
+
+/// Requires you to pass an input filename and reader so that
+/// it can scan the input text for comments to copy forward.
+pub fn print_crate<'a>(
+ sm: &'a SourceMap,
+ krate: &ast::Crate,
+ filename: FileName,
+ input: String,
+ ann: &'a dyn PpAnn,
+ is_expanded: bool,
+ edition: Edition,
+) -> String {
+ let mut s =
+ State { s: pp::Printer::new(), comments: Some(Comments::new(sm, filename, input)), ann };
+
+ if is_expanded && !krate.attrs.iter().any(|attr| attr.has_name(sym::no_core)) {
+ // We need to print `#![no_std]` (and its feature gate) so that
+ // compiling pretty-printed source won't inject libstd again.
+ // However, we don't want these attributes in the AST because
+ // of the feature gate, so we fake them up here.
+
+ // `#![feature(prelude_import)]`
+ let pi_nested = attr::mk_nested_word_item(Ident::with_dummy_span(sym::prelude_import));
+ let list = attr::mk_list_item(Ident::with_dummy_span(sym::feature), vec![pi_nested]);
+ let fake_attr = attr::mk_attr_inner(list);
+ s.print_attribute(&fake_attr);
+
+ // Currently, in Rust 2018 we don't have `extern crate std;` at the crate
+ // root, so this is not needed, and actually breaks things.
+ if edition == Edition::Edition2015 {
+ // `#![no_std]`
+ let no_std_meta = attr::mk_word_item(Ident::with_dummy_span(sym::no_std));
+ let fake_attr = attr::mk_attr_inner(no_std_meta);
+ s.print_attribute(&fake_attr);
+ }
+ }
+
+ s.print_inner_attributes(&krate.attrs);
+ for item in &krate.items {
+ s.print_item(item);
+ }
+ s.print_remaining_comments();
+ s.ann.post(&mut s, AnnNode::Crate(krate));
+ s.s.eof()
+}
+
+/// This makes printed token streams look slightly nicer,
+/// and also addresses some specific regressions described in #63896 and #73345.
+fn tt_prepend_space(tt: &TokenTree, prev: &TokenTree) -> bool {
+ if let TokenTree::Token(token, _) = prev {
+ if matches!(token.kind, token::Dot | token::Dollar) {
+ return false;
+ }
+ if let token::DocComment(comment_kind, ..) = token.kind {
+ return comment_kind != CommentKind::Line;
+ }
+ }
+ match tt {
+ TokenTree::Token(token, _) => !matches!(token.kind, token::Comma | token::Not | token::Dot),
+ TokenTree::Delimited(_, Delimiter::Parenthesis, _) => {
+ !matches!(prev, TokenTree::Token(Token { kind: token::Ident(..), .. }, _))
+ }
+ TokenTree::Delimited(_, Delimiter::Bracket, _) => {
+ !matches!(prev, TokenTree::Token(Token { kind: token::Pound, .. }, _))
+ }
+ TokenTree::Delimited(..) => true,
+ }
+}
+
+fn binop_to_string(op: BinOpToken) -> &'static str {
+ match op {
+ token::Plus => "+",
+ token::Minus => "-",
+ token::Star => "*",
+ token::Slash => "/",
+ token::Percent => "%",
+ token::Caret => "^",
+ token::And => "&",
+ token::Or => "|",
+ token::Shl => "<<",
+ token::Shr => ">>",
+ }
+}
+
+fn doc_comment_to_string(
+ comment_kind: CommentKind,
+ attr_style: ast::AttrStyle,
+ data: Symbol,
+) -> String {
+ match (comment_kind, attr_style) {
+ (CommentKind::Line, ast::AttrStyle::Outer) => format!("///{}", data),
+ (CommentKind::Line, ast::AttrStyle::Inner) => format!("//!{}", data),
+ (CommentKind::Block, ast::AttrStyle::Outer) => format!("/**{}*/", data),
+ (CommentKind::Block, ast::AttrStyle::Inner) => format!("/*!{}*/", data),
+ }
+}
+
+pub fn literal_to_string(lit: token::Lit) -> String {
+ let token::Lit { kind, symbol, suffix } = lit;
+ let mut out = match kind {
+ token::Byte => format!("b'{}'", symbol),
+ token::Char => format!("'{}'", symbol),
+ token::Str => format!("\"{}\"", symbol),
+ token::StrRaw(n) => {
+ format!("r{delim}\"{string}\"{delim}", delim = "#".repeat(n as usize), string = symbol)
+ }
+ token::ByteStr => format!("b\"{}\"", symbol),
+ token::ByteStrRaw(n) => {
+ format!("br{delim}\"{string}\"{delim}", delim = "#".repeat(n as usize), string = symbol)
+ }
+ token::Integer | token::Float | token::Bool | token::Err => symbol.to_string(),
+ };
+
+ if let Some(suffix) = suffix {
+ out.push_str(suffix.as_str())
+ }
+
+ out
+}
+
+impl std::ops::Deref for State<'_> {
+ type Target = pp::Printer;
+ fn deref(&self) -> &Self::Target {
+ &self.s
+ }
+}
+
+impl std::ops::DerefMut for State<'_> {
+ fn deref_mut(&mut self) -> &mut Self::Target {
+ &mut self.s
+ }
+}
+
+pub trait PrintState<'a>: std::ops::Deref<Target = pp::Printer> + std::ops::DerefMut {
+ fn comments(&mut self) -> &mut Option<Comments<'a>>;
+ fn print_ident(&mut self, ident: Ident);
+ fn print_generic_args(&mut self, args: &ast::GenericArgs, colons_before_params: bool);
+
+ fn strsep<T, F>(
+ &mut self,
+ sep: &'static str,
+ space_before: bool,
+ b: Breaks,
+ elts: &[T],
+ mut op: F,
+ ) where
+ F: FnMut(&mut Self, &T),
+ {
+ self.rbox(0, b);
+ if let Some((first, rest)) = elts.split_first() {
+ op(self, first);
+ for elt in rest {
+ if space_before {
+ self.space();
+ }
+ self.word_space(sep);
+ op(self, elt);
+ }
+ }
+ self.end();
+ }
+
+ fn commasep<T, F>(&mut self, b: Breaks, elts: &[T], op: F)
+ where
+ F: FnMut(&mut Self, &T),
+ {
+ self.strsep(",", false, b, elts, op)
+ }
+
+ fn maybe_print_comment(&mut self, pos: BytePos) -> bool {
+ let mut has_comment = false;
+ while let Some(ref cmnt) = self.next_comment() {
+ if cmnt.pos < pos {
+ has_comment = true;
+ self.print_comment(cmnt);
+ } else {
+ break;
+ }
+ }
+ has_comment
+ }
+
+ fn print_comment(&mut self, cmnt: &Comment) {
+ match cmnt.style {
+ CommentStyle::Mixed => {
+ if !self.is_beginning_of_line() {
+ self.zerobreak();
+ }
+ if let Some((last, lines)) = cmnt.lines.split_last() {
+ self.ibox(0);
+
+ for line in lines {
+ self.word(line.clone());
+ self.hardbreak()
+ }
+
+ self.word(last.clone());
+ self.space();
+
+ self.end();
+ }
+ self.zerobreak()
+ }
+ CommentStyle::Isolated => {
+ self.hardbreak_if_not_bol();
+ for line in &cmnt.lines {
+ // Don't print empty lines because they will end up as trailing
+ // whitespace.
+ if !line.is_empty() {
+ self.word(line.clone());
+ }
+ self.hardbreak();
+ }
+ }
+ CommentStyle::Trailing => {
+ if !self.is_beginning_of_line() {
+ self.word(" ");
+ }
+ if cmnt.lines.len() == 1 {
+ self.word(cmnt.lines[0].clone());
+ self.hardbreak()
+ } else {
+ self.visual_align();
+ for line in &cmnt.lines {
+ if !line.is_empty() {
+ self.word(line.clone());
+ }
+ self.hardbreak();
+ }
+ self.end();
+ }
+ }
+ CommentStyle::BlankLine => {
+ // We need to do at least one, possibly two hardbreaks.
+ let twice = match self.last_token() {
+ Some(pp::Token::String(s)) => ";" == s,
+ Some(pp::Token::Begin(_)) => true,
+ Some(pp::Token::End) => true,
+ _ => false,
+ };
+ if twice {
+ self.hardbreak();
+ }
+ self.hardbreak();
+ }
+ }
+ if let Some(cmnts) = self.comments() {
+ cmnts.current += 1;
+ }
+ }
+
+ fn next_comment(&mut self) -> Option<Comment> {
+ self.comments().as_mut().and_then(|c| c.next())
+ }
+
+ fn maybe_print_trailing_comment(&mut self, span: rustc_span::Span, next_pos: Option<BytePos>) {
+ if let Some(cmnts) = self.comments() {
+ if let Some(cmnt) = cmnts.trailing_comment(span, next_pos) {
+ self.print_comment(&cmnt);
+ }
+ }
+ }
+
+ fn print_remaining_comments(&mut self) {
+ // If there aren't any remaining comments, then we need to manually
+ // make sure there is a line break at the end.
+ if self.next_comment().is_none() {
+ self.hardbreak();
+ }
+ while let Some(ref cmnt) = self.next_comment() {
+ self.print_comment(cmnt)
+ }
+ }
+
+ fn print_literal(&mut self, lit: &ast::Lit) {
+ self.maybe_print_comment(lit.span.lo());
+ self.word(lit.token.to_string())
+ }
+
+ fn print_string(&mut self, st: &str, style: ast::StrStyle) {
+ let st = match style {
+ ast::StrStyle::Cooked => format!("\"{}\"", st.escape_debug()),
+ ast::StrStyle::Raw(n) => {
+ format!("r{delim}\"{string}\"{delim}", delim = "#".repeat(n as usize), string = st)
+ }
+ };
+ self.word(st)
+ }
+
+ fn print_symbol(&mut self, sym: Symbol, style: ast::StrStyle) {
+ self.print_string(sym.as_str(), style);
+ }
+
+ fn print_inner_attributes(&mut self, attrs: &[ast::Attribute]) -> bool {
+ self.print_either_attributes(attrs, ast::AttrStyle::Inner, false, true)
+ }
+
+ fn print_inner_attributes_no_trailing_hardbreak(&mut self, attrs: &[ast::Attribute]) -> bool {
+ self.print_either_attributes(attrs, ast::AttrStyle::Inner, false, false)
+ }
+
+ fn print_outer_attributes(&mut self, attrs: &[ast::Attribute]) -> bool {
+ self.print_either_attributes(attrs, ast::AttrStyle::Outer, false, true)
+ }
+
+ fn print_inner_attributes_inline(&mut self, attrs: &[ast::Attribute]) -> bool {
+ self.print_either_attributes(attrs, ast::AttrStyle::Inner, true, true)
+ }
+
+ fn print_outer_attributes_inline(&mut self, attrs: &[ast::Attribute]) -> bool {
+ self.print_either_attributes(attrs, ast::AttrStyle::Outer, true, true)
+ }
+
+ fn print_either_attributes(
+ &mut self,
+ attrs: &[ast::Attribute],
+ kind: ast::AttrStyle,
+ is_inline: bool,
+ trailing_hardbreak: bool,
+ ) -> bool {
+ let mut printed = false;
+ for attr in attrs {
+ if attr.style == kind {
+ self.print_attribute_inline(attr, is_inline);
+ if is_inline {
+ self.nbsp();
+ }
+ printed = true;
+ }
+ }
+ if printed && trailing_hardbreak && !is_inline {
+ self.hardbreak_if_not_bol();
+ }
+ printed
+ }
+
+ fn print_attribute(&mut self, attr: &ast::Attribute) {
+ self.print_attribute_inline(attr, false)
+ }
+
+ fn print_attribute_inline(&mut self, attr: &ast::Attribute, is_inline: bool) {
+ if !is_inline {
+ self.hardbreak_if_not_bol();
+ }
+ self.maybe_print_comment(attr.span.lo());
+ match attr.kind {
+ ast::AttrKind::Normal(ref item, _) => {
+ match attr.style {
+ ast::AttrStyle::Inner => self.word("#!["),
+ ast::AttrStyle::Outer => self.word("#["),
+ }
+ self.print_attr_item(&item, attr.span);
+ self.word("]");
+ }
+ ast::AttrKind::DocComment(comment_kind, data) => {
+ self.word(doc_comment_to_string(comment_kind, attr.style, data));
+ self.hardbreak()
+ }
+ }
+ }
+
+ fn print_attr_item(&mut self, item: &ast::AttrItem, span: Span) {
+ self.ibox(0);
+ match &item.args {
+ MacArgs::Delimited(_, delim, tokens) => self.print_mac_common(
+ Some(MacHeader::Path(&item.path)),
+ false,
+ None,
+ Some(delim.to_token()),
+ tokens,
+ true,
+ span,
+ ),
+ MacArgs::Empty => {
+ self.print_path(&item.path, false, 0);
+ }
+ MacArgs::Eq(_, MacArgsEq::Ast(expr)) => {
+ self.print_path(&item.path, false, 0);
+ self.space();
+ self.word_space("=");
+ let token_str = self.expr_to_string(expr);
+ self.word(token_str);
+ }
+ MacArgs::Eq(_, MacArgsEq::Hir(lit)) => {
+ self.print_path(&item.path, false, 0);
+ self.space();
+ self.word_space("=");
+ let token_str = self.literal_to_string(lit);
+ self.word(token_str);
+ }
+ }
+ self.end();
+ }
+
+ fn print_meta_list_item(&mut self, item: &ast::NestedMetaItem) {
+ match item {
+ ast::NestedMetaItem::MetaItem(ref mi) => self.print_meta_item(mi),
+ ast::NestedMetaItem::Literal(ref lit) => self.print_literal(lit),
+ }
+ }
+
+ fn print_meta_item(&mut self, item: &ast::MetaItem) {
+ self.ibox(INDENT_UNIT);
+ match item.kind {
+ ast::MetaItemKind::Word => self.print_path(&item.path, false, 0),
+ ast::MetaItemKind::NameValue(ref value) => {
+ self.print_path(&item.path, false, 0);
+ self.space();
+ self.word_space("=");
+ self.print_literal(value);
+ }
+ ast::MetaItemKind::List(ref items) => {
+ self.print_path(&item.path, false, 0);
+ self.popen();
+ self.commasep(Consistent, &items, |s, i| s.print_meta_list_item(i));
+ self.pclose();
+ }
+ }
+ self.end();
+ }
+
+ /// This doesn't deserve to be called "pretty" printing, but it should be
+ /// meaning-preserving. A quick hack that might help would be to look at the
+ /// spans embedded in the TTs to decide where to put spaces and newlines.
+ /// But it'd be better to parse these according to the grammar of the
+ /// appropriate macro, transcribe back into the grammar we just parsed from,
+ /// and then pretty-print the resulting AST nodes (so, e.g., we print
+ /// expression arguments as expressions). It can be done! I think.
+ fn print_tt(&mut self, tt: &TokenTree, convert_dollar_crate: bool) {
+ match tt {
+ TokenTree::Token(token, _) => {
+ let token_str = self.token_to_string_ext(&token, convert_dollar_crate);
+ self.word(token_str);
+ if let token::DocComment(..) = token.kind {
+ self.hardbreak()
+ }
+ }
+ TokenTree::Delimited(dspan, delim, tts) => {
+ self.print_mac_common(
+ None,
+ false,
+ None,
+ Some(*delim),
+ tts,
+ convert_dollar_crate,
+ dspan.entire(),
+ );
+ }
+ }
+ }
+
+ fn print_tts(&mut self, tts: &TokenStream, convert_dollar_crate: bool) {
+ let mut iter = tts.trees().peekable();
+ while let Some(tt) = iter.next() {
+ self.print_tt(tt, convert_dollar_crate);
+ if let Some(next) = iter.peek() {
+ if tt_prepend_space(next, tt) {
+ self.space();
+ }
+ }
+ }
+ }
+
+ fn print_mac_common(
+ &mut self,
+ header: Option<MacHeader<'_>>,
+ has_bang: bool,
+ ident: Option<Ident>,
+ delim: Option<Delimiter>,
+ tts: &TokenStream,
+ convert_dollar_crate: bool,
+ span: Span,
+ ) {
+ if delim == Some(Delimiter::Brace) {
+ self.cbox(INDENT_UNIT);
+ }
+ match header {
+ Some(MacHeader::Path(path)) => self.print_path(path, false, 0),
+ Some(MacHeader::Keyword(kw)) => self.word(kw),
+ None => {}
+ }
+ if has_bang {
+ self.word("!");
+ }
+ if let Some(ident) = ident {
+ self.nbsp();
+ self.print_ident(ident);
+ }
+ match delim {
+ Some(Delimiter::Brace) => {
+ if header.is_some() || has_bang || ident.is_some() {
+ self.nbsp();
+ }
+ self.word("{");
+ if !tts.is_empty() {
+ self.space();
+ }
+ self.ibox(0);
+ self.print_tts(tts, convert_dollar_crate);
+ self.end();
+ let empty = tts.is_empty();
+ self.bclose(span, empty);
+ }
+ Some(delim) => {
+ let token_str = self.token_kind_to_string(&token::OpenDelim(delim));
+ self.word(token_str);
+ self.ibox(0);
+ self.print_tts(tts, convert_dollar_crate);
+ self.end();
+ let token_str = self.token_kind_to_string(&token::CloseDelim(delim));
+ self.word(token_str);
+ }
+ None => {
+ self.ibox(0);
+ self.print_tts(tts, convert_dollar_crate);
+ self.end();
+ }
+ }
+ }
+
+ fn print_mac_def(
+ &mut self,
+ macro_def: &ast::MacroDef,
+ ident: &Ident,
+ sp: Span,
+ print_visibility: impl FnOnce(&mut Self),
+ ) {
+ let (kw, has_bang) = if macro_def.macro_rules {
+ ("macro_rules", true)
+ } else {
+ print_visibility(self);
+ ("macro", false)
+ };
+ self.print_mac_common(
+ Some(MacHeader::Keyword(kw)),
+ has_bang,
+ Some(*ident),
+ macro_def.body.delim(),
+ &macro_def.body.inner_tokens(),
+ true,
+ sp,
+ );
+ if macro_def.body.need_semicolon() {
+ self.word(";");
+ }
+ }
+
+ fn print_path(&mut self, path: &ast::Path, colons_before_params: bool, depth: usize) {
+ self.maybe_print_comment(path.span.lo());
+
+ for (i, segment) in path.segments[..path.segments.len() - depth].iter().enumerate() {
+ if i > 0 {
+ self.word("::")
+ }
+ self.print_path_segment(segment, colons_before_params);
+ }
+ }
+
+ fn print_path_segment(&mut self, segment: &ast::PathSegment, colons_before_params: bool) {
+ if segment.ident.name != kw::PathRoot {
+ self.print_ident(segment.ident);
+ if let Some(ref args) = segment.args {
+ self.print_generic_args(args, colons_before_params);
+ }
+ }
+ }
+
+ fn head<S: Into<Cow<'static, str>>>(&mut self, w: S) {
+ let w = w.into();
+ // Outer-box is consistent.
+ self.cbox(INDENT_UNIT);
+ // Head-box is inconsistent.
+ self.ibox(0);
+ // Keyword that starts the head.
+ if !w.is_empty() {
+ self.word_nbsp(w);
+ }
+ }
+
+ fn bopen(&mut self) {
+ self.word("{");
+ self.end(); // Close the head-box.
+ }
+
+ fn bclose_maybe_open(&mut self, span: rustc_span::Span, empty: bool, close_box: bool) {
+ let has_comment = self.maybe_print_comment(span.hi());
+ if !empty || has_comment {
+ self.break_offset_if_not_bol(1, -(INDENT_UNIT as isize));
+ }
+ self.word("}");
+ if close_box {
+ self.end(); // Close the outer-box.
+ }
+ }
+
+ fn bclose(&mut self, span: rustc_span::Span, empty: bool) {
+ let close_box = true;
+ self.bclose_maybe_open(span, empty, close_box)
+ }
+
+ fn break_offset_if_not_bol(&mut self, n: usize, off: isize) {
+ if !self.is_beginning_of_line() {
+ self.break_offset(n, off)
+ } else if off != 0 {
+ if let Some(last_token) = self.last_token_still_buffered() {
+ if last_token.is_hardbreak_tok() {
+ // We do something pretty sketchy here: tuck the nonzero
+ // offset-adjustment we were going to deposit along with the
+ // break into the previous hardbreak.
+ self.replace_last_token_still_buffered(pp::Printer::hardbreak_tok_offset(off));
+ }
+ }
+ }
+ }
+
+ fn nonterminal_to_string(&self, nt: &Nonterminal) -> String {
+ match *nt {
+ token::NtExpr(ref e) => self.expr_to_string(e),
+ token::NtMeta(ref e) => self.attr_item_to_string(e),
+ token::NtTy(ref e) => self.ty_to_string(e),
+ token::NtPath(ref e) => self.path_to_string(e),
+ token::NtItem(ref e) => self.item_to_string(e),
+ token::NtBlock(ref e) => self.block_to_string(e),
+ token::NtStmt(ref e) => self.stmt_to_string(e),
+ token::NtPat(ref e) => self.pat_to_string(e),
+ token::NtIdent(e, is_raw) => IdentPrinter::for_ast_ident(e, is_raw).to_string(),
+ token::NtLifetime(e) => e.to_string(),
+ token::NtLiteral(ref e) => self.expr_to_string(e),
+ token::NtVis(ref e) => self.vis_to_string(e),
+ }
+ }
+
+ /// Print the token kind precisely, without converting `$crate` into its respective crate name.
+ fn token_kind_to_string(&self, tok: &TokenKind) -> Cow<'static, str> {
+ self.token_kind_to_string_ext(tok, None)
+ }
+
+ fn token_kind_to_string_ext(
+ &self,
+ tok: &TokenKind,
+ convert_dollar_crate: Option<Span>,
+ ) -> Cow<'static, str> {
+ match *tok {
+ token::Eq => "=".into(),
+ token::Lt => "<".into(),
+ token::Le => "<=".into(),
+ token::EqEq => "==".into(),
+ token::Ne => "!=".into(),
+ token::Ge => ">=".into(),
+ token::Gt => ">".into(),
+ token::Not => "!".into(),
+ token::Tilde => "~".into(),
+ token::OrOr => "||".into(),
+ token::AndAnd => "&&".into(),
+ token::BinOp(op) => binop_to_string(op).into(),
+ token::BinOpEq(op) => format!("{}=", binop_to_string(op)).into(),
+
+ /* Structural symbols */
+ token::At => "@".into(),
+ token::Dot => ".".into(),
+ token::DotDot => "..".into(),
+ token::DotDotDot => "...".into(),
+ token::DotDotEq => "..=".into(),
+ token::Comma => ",".into(),
+ token::Semi => ";".into(),
+ token::Colon => ":".into(),
+ token::ModSep => "::".into(),
+ token::RArrow => "->".into(),
+ token::LArrow => "<-".into(),
+ token::FatArrow => "=>".into(),
+ token::OpenDelim(Delimiter::Parenthesis) => "(".into(),
+ token::CloseDelim(Delimiter::Parenthesis) => ")".into(),
+ token::OpenDelim(Delimiter::Bracket) => "[".into(),
+ token::CloseDelim(Delimiter::Bracket) => "]".into(),
+ token::OpenDelim(Delimiter::Brace) => "{".into(),
+ token::CloseDelim(Delimiter::Brace) => "}".into(),
+ token::OpenDelim(Delimiter::Invisible) | token::CloseDelim(Delimiter::Invisible) => {
+ "".into()
+ }
+ token::Pound => "#".into(),
+ token::Dollar => "$".into(),
+ token::Question => "?".into(),
+ token::SingleQuote => "'".into(),
+
+ /* Literals */
+ token::Literal(lit) => literal_to_string(lit).into(),
+
+ /* Name components */
+ token::Ident(s, is_raw) => {
+ IdentPrinter::new(s, is_raw, convert_dollar_crate).to_string().into()
+ }
+ token::Lifetime(s) => s.to_string().into(),
+
+ /* Other */
+ token::DocComment(comment_kind, attr_style, data) => {
+ doc_comment_to_string(comment_kind, attr_style, data).into()
+ }
+ token::Eof => "<eof>".into(),
+
+ token::Interpolated(ref nt) => self.nonterminal_to_string(nt).into(),
+ }
+ }
+
+ /// Print the token precisely, without converting `$crate` into its respective crate name.
+ fn token_to_string(&self, token: &Token) -> Cow<'static, str> {
+ self.token_to_string_ext(token, false)
+ }
+
+ fn token_to_string_ext(&self, token: &Token, convert_dollar_crate: bool) -> Cow<'static, str> {
+ let convert_dollar_crate = convert_dollar_crate.then_some(token.span);
+ self.token_kind_to_string_ext(&token.kind, convert_dollar_crate)
+ }
+
+ fn ty_to_string(&self, ty: &ast::Ty) -> String {
+ Self::to_string(|s| s.print_type(ty))
+ }
+
+ fn bounds_to_string(&self, bounds: &[ast::GenericBound]) -> String {
+ Self::to_string(|s| s.print_type_bounds(bounds))
+ }
+
+ fn pat_to_string(&self, pat: &ast::Pat) -> String {
+ Self::to_string(|s| s.print_pat(pat))
+ }
+
+ fn expr_to_string(&self, e: &ast::Expr) -> String {
+ Self::to_string(|s| s.print_expr(e))
+ }
+
+ fn literal_to_string(&self, lit: &ast::Lit) -> String {
+ Self::to_string(|s| s.print_literal(lit))
+ }
+
+ fn tt_to_string(&self, tt: &TokenTree) -> String {
+ Self::to_string(|s| s.print_tt(tt, false))
+ }
+
+ fn tts_to_string(&self, tokens: &TokenStream) -> String {
+ Self::to_string(|s| s.print_tts(tokens, false))
+ }
+
+ fn stmt_to_string(&self, stmt: &ast::Stmt) -> String {
+ Self::to_string(|s| s.print_stmt(stmt))
+ }
+
+ fn item_to_string(&self, i: &ast::Item) -> String {
+ Self::to_string(|s| s.print_item(i))
+ }
+
+ fn assoc_item_to_string(&self, i: &ast::AssocItem) -> String {
+ Self::to_string(|s| s.print_assoc_item(i))
+ }
+
+ fn foreign_item_to_string(&self, i: &ast::ForeignItem) -> String {
+ Self::to_string(|s| s.print_foreign_item(i))
+ }
+
+ fn generic_params_to_string(&self, generic_params: &[ast::GenericParam]) -> String {
+ Self::to_string(|s| s.print_generic_params(generic_params))
+ }
+
+ fn path_to_string(&self, p: &ast::Path) -> String {
+ Self::to_string(|s| s.print_path(p, false, 0))
+ }
+
+ fn path_segment_to_string(&self, p: &ast::PathSegment) -> String {
+ Self::to_string(|s| s.print_path_segment(p, false))
+ }
+
+ fn vis_to_string(&self, v: &ast::Visibility) -> String {
+ Self::to_string(|s| s.print_visibility(v))
+ }
+
+ fn block_to_string(&self, blk: &ast::Block) -> String {
+ Self::to_string(|s| {
+ // Containing cbox, will be closed by `print_block` at `}`.
+ s.cbox(INDENT_UNIT);
+ // Head-ibox, will be closed by `print_block` after `{`.
+ s.ibox(0);
+ s.print_block(blk)
+ })
+ }
+
+ fn meta_list_item_to_string(&self, li: &ast::NestedMetaItem) -> String {
+ Self::to_string(|s| s.print_meta_list_item(li))
+ }
+
+ fn attr_item_to_string(&self, ai: &ast::AttrItem) -> String {
+ Self::to_string(|s| s.print_attr_item(ai, ai.path.span))
+ }
+
+ fn attribute_to_string(&self, attr: &ast::Attribute) -> String {
+ Self::to_string(|s| s.print_attribute(attr))
+ }
+
+ fn param_to_string(&self, arg: &ast::Param) -> String {
+ Self::to_string(|s| s.print_param(arg, false))
+ }
+
+ fn to_string(f: impl FnOnce(&mut State<'_>)) -> String {
+ let mut printer = State::new();
+ f(&mut printer);
+ printer.s.eof()
+ }
+}
+
+impl<'a> PrintState<'a> for State<'a> {
+ fn comments(&mut self) -> &mut Option<Comments<'a>> {
+ &mut self.comments
+ }
+
+ fn print_ident(&mut self, ident: Ident) {
+ self.word(IdentPrinter::for_ast_ident(ident, ident.is_raw_guess()).to_string());
+ self.ann.post(self, AnnNode::Ident(&ident))
+ }
+
+ fn print_generic_args(&mut self, args: &ast::GenericArgs, colons_before_params: bool) {
+ if colons_before_params {
+ self.word("::")
+ }
+
+ match *args {
+ ast::GenericArgs::AngleBracketed(ref data) => {
+ self.word("<");
+ self.commasep(Inconsistent, &data.args, |s, arg| match arg {
+ ast::AngleBracketedArg::Arg(a) => s.print_generic_arg(a),
+ ast::AngleBracketedArg::Constraint(c) => s.print_assoc_constraint(c),
+ });
+ self.word(">")
+ }
+
+ ast::GenericArgs::Parenthesized(ref data) => {
+ self.word("(");
+ self.commasep(Inconsistent, &data.inputs, |s, ty| s.print_type(ty));
+ self.word(")");
+ self.print_fn_ret_ty(&data.output);
+ }
+ }
+ }
+}
+
+impl<'a> State<'a> {
+ pub fn new() -> State<'a> {
+ State { s: pp::Printer::new(), comments: None, ann: &NoAnn }
+ }
+
+ pub(crate) fn commasep_cmnt<T, F, G>(
+ &mut self,
+ b: Breaks,
+ elts: &[T],
+ mut op: F,
+ mut get_span: G,
+ ) where
+ F: FnMut(&mut State<'_>, &T),
+ G: FnMut(&T) -> rustc_span::Span,
+ {
+ self.rbox(0, b);
+ let len = elts.len();
+ let mut i = 0;
+ for elt in elts {
+ self.maybe_print_comment(get_span(elt).hi());
+ op(self, elt);
+ i += 1;
+ if i < len {
+ self.word(",");
+ self.maybe_print_trailing_comment(get_span(elt), Some(get_span(&elts[i]).hi()));
+ self.space_if_not_bol();
+ }
+ }
+ self.end();
+ }
+
+ pub(crate) fn commasep_exprs(&mut self, b: Breaks, exprs: &[P<ast::Expr>]) {
+ self.commasep_cmnt(b, exprs, |s, e| s.print_expr(e), |e| e.span)
+ }
+
+ pub fn print_opt_lifetime(&mut self, lifetime: &Option<ast::Lifetime>) {
+ if let Some(lt) = *lifetime {
+ self.print_lifetime(lt);
+ self.nbsp();
+ }
+ }
+
+ pub fn print_assoc_constraint(&mut self, constraint: &ast::AssocConstraint) {
+ self.print_ident(constraint.ident);
+ constraint.gen_args.as_ref().map(|args| self.print_generic_args(args, false));
+ self.space();
+ match &constraint.kind {
+ ast::AssocConstraintKind::Equality { term } => {
+ self.word_space("=");
+ match term {
+ Term::Ty(ty) => self.print_type(ty),
+ Term::Const(c) => self.print_expr_anon_const(c, &[]),
+ }
+ }
+ ast::AssocConstraintKind::Bound { bounds } => {
+ if !bounds.is_empty() {
+ self.word_nbsp(":");
+ self.print_type_bounds(&bounds);
+ }
+ }
+ }
+ }
+
+ pub fn print_generic_arg(&mut self, generic_arg: &GenericArg) {
+ match generic_arg {
+ GenericArg::Lifetime(lt) => self.print_lifetime(*lt),
+ GenericArg::Type(ty) => self.print_type(ty),
+ GenericArg::Const(ct) => self.print_expr(&ct.value),
+ }
+ }
+
+ pub fn print_type(&mut self, ty: &ast::Ty) {
+ self.maybe_print_comment(ty.span.lo());
+ self.ibox(0);
+ match ty.kind {
+ ast::TyKind::Slice(ref ty) => {
+ self.word("[");
+ self.print_type(ty);
+ self.word("]");
+ }
+ ast::TyKind::Ptr(ref mt) => {
+ self.word("*");
+ self.print_mt(mt, true);
+ }
+ ast::TyKind::Rptr(ref lifetime, ref mt) => {
+ self.word("&");
+ self.print_opt_lifetime(lifetime);
+ self.print_mt(mt, false);
+ }
+ ast::TyKind::Never => {
+ self.word("!");
+ }
+ ast::TyKind::Tup(ref elts) => {
+ self.popen();
+ self.commasep(Inconsistent, &elts, |s, ty| s.print_type(ty));
+ if elts.len() == 1 {
+ self.word(",");
+ }
+ self.pclose();
+ }
+ ast::TyKind::Paren(ref typ) => {
+ self.popen();
+ self.print_type(typ);
+ self.pclose();
+ }
+ ast::TyKind::BareFn(ref f) => {
+ self.print_ty_fn(f.ext, f.unsafety, &f.decl, None, &f.generic_params);
+ }
+ ast::TyKind::Path(None, ref path) => {
+ self.print_path(path, false, 0);
+ }
+ ast::TyKind::Path(Some(ref qself), ref path) => self.print_qpath(path, qself, false),
+ ast::TyKind::TraitObject(ref bounds, syntax) => {
+ if syntax == ast::TraitObjectSyntax::Dyn {
+ self.word_nbsp("dyn");
+ }
+ self.print_type_bounds(bounds);
+ }
+ ast::TyKind::ImplTrait(_, ref bounds) => {
+ self.word_nbsp("impl");
+ self.print_type_bounds(bounds);
+ }
+ ast::TyKind::Array(ref ty, ref length) => {
+ self.word("[");
+ self.print_type(ty);
+ self.word("; ");
+ self.print_expr(&length.value);
+ self.word("]");
+ }
+ ast::TyKind::Typeof(ref e) => {
+ self.word("typeof(");
+ self.print_expr(&e.value);
+ self.word(")");
+ }
+ ast::TyKind::Infer => {
+ self.word("_");
+ }
+ ast::TyKind::Err => {
+ self.popen();
+ self.word("/*ERROR*/");
+ self.pclose();
+ }
+ ast::TyKind::ImplicitSelf => {
+ self.word("Self");
+ }
+ ast::TyKind::MacCall(ref m) => {
+ self.print_mac(m);
+ }
+ ast::TyKind::CVarArgs => {
+ self.word("...");
+ }
+ }
+ self.end();
+ }
+
+ fn print_trait_ref(&mut self, t: &ast::TraitRef) {
+ self.print_path(&t.path, false, 0)
+ }
+
+ fn print_formal_generic_params(&mut self, generic_params: &[ast::GenericParam]) {
+ if !generic_params.is_empty() {
+ self.word("for");
+ self.print_generic_params(generic_params);
+ self.nbsp();
+ }
+ }
+
+ fn print_poly_trait_ref(&mut self, t: &ast::PolyTraitRef) {
+ self.print_formal_generic_params(&t.bound_generic_params);
+ self.print_trait_ref(&t.trait_ref)
+ }
+
+ pub(crate) fn print_stmt(&mut self, st: &ast::Stmt) {
+ self.maybe_print_comment(st.span.lo());
+ match st.kind {
+ ast::StmtKind::Local(ref loc) => {
+ self.print_outer_attributes(&loc.attrs);
+ self.space_if_not_bol();
+ self.ibox(INDENT_UNIT);
+ self.word_nbsp("let");
+
+ self.ibox(INDENT_UNIT);
+ self.print_local_decl(loc);
+ self.end();
+ if let Some((init, els)) = loc.kind.init_else_opt() {
+ self.nbsp();
+ self.word_space("=");
+ self.print_expr(init);
+ if let Some(els) = els {
+ self.cbox(INDENT_UNIT);
+ self.ibox(INDENT_UNIT);
+ self.word(" else ");
+ self.print_block(els);
+ }
+ }
+ self.word(";");
+ self.end(); // `let` ibox
+ }
+ ast::StmtKind::Item(ref item) => self.print_item(item),
+ ast::StmtKind::Expr(ref expr) => {
+ self.space_if_not_bol();
+ self.print_expr_outer_attr_style(expr, false);
+ if classify::expr_requires_semi_to_be_stmt(expr) {
+ self.word(";");
+ }
+ }
+ ast::StmtKind::Semi(ref expr) => {
+ self.space_if_not_bol();
+ self.print_expr_outer_attr_style(expr, false);
+ self.word(";");
+ }
+ ast::StmtKind::Empty => {
+ self.space_if_not_bol();
+ self.word(";");
+ }
+ ast::StmtKind::MacCall(ref mac) => {
+ self.space_if_not_bol();
+ self.print_outer_attributes(&mac.attrs);
+ self.print_mac(&mac.mac);
+ if mac.style == ast::MacStmtStyle::Semicolon {
+ self.word(";");
+ }
+ }
+ }
+ self.maybe_print_trailing_comment(st.span, None)
+ }
+
+ pub(crate) fn print_block(&mut self, blk: &ast::Block) {
+ self.print_block_with_attrs(blk, &[])
+ }
+
+ pub(crate) fn print_block_unclosed_indent(&mut self, blk: &ast::Block) {
+ self.print_block_maybe_unclosed(blk, &[], false)
+ }
+
+ pub(crate) fn print_block_with_attrs(&mut self, blk: &ast::Block, attrs: &[ast::Attribute]) {
+ self.print_block_maybe_unclosed(blk, attrs, true)
+ }
+
+ pub(crate) fn print_block_maybe_unclosed(
+ &mut self,
+ blk: &ast::Block,
+ attrs: &[ast::Attribute],
+ close_box: bool,
+ ) {
+ match blk.rules {
+ BlockCheckMode::Unsafe(..) => self.word_space("unsafe"),
+ BlockCheckMode::Default => (),
+ }
+ self.maybe_print_comment(blk.span.lo());
+ self.ann.pre(self, AnnNode::Block(blk));
+ self.bopen();
+
+ let has_attrs = self.print_inner_attributes(attrs);
+
+ for (i, st) in blk.stmts.iter().enumerate() {
+ match st.kind {
+ ast::StmtKind::Expr(ref expr) if i == blk.stmts.len() - 1 => {
+ self.maybe_print_comment(st.span.lo());
+ self.space_if_not_bol();
+ self.print_expr_outer_attr_style(expr, false);
+ self.maybe_print_trailing_comment(expr.span, Some(blk.span.hi()));
+ }
+ _ => self.print_stmt(st),
+ }
+ }
+
+ let empty = !has_attrs && blk.stmts.is_empty();
+ self.bclose_maybe_open(blk.span, empty, close_box);
+ self.ann.post(self, AnnNode::Block(blk))
+ }
+
+ /// Print a `let pat = expr` expression.
+ pub(crate) fn print_let(&mut self, pat: &ast::Pat, expr: &ast::Expr) {
+ self.word("let ");
+ self.print_pat(pat);
+ self.space();
+ self.word_space("=");
+ let npals = || parser::needs_par_as_let_scrutinee(expr.precedence().order());
+ self.print_expr_cond_paren(expr, Self::cond_needs_par(expr) || npals())
+ }
+
+ pub(crate) fn print_mac(&mut self, m: &ast::MacCall) {
+ self.print_mac_common(
+ Some(MacHeader::Path(&m.path)),
+ true,
+ None,
+ m.args.delim(),
+ &m.args.inner_tokens(),
+ true,
+ m.span(),
+ );
+ }
+
+ fn print_inline_asm(&mut self, asm: &ast::InlineAsm) {
+ enum AsmArg<'a> {
+ Template(String),
+ Operand(&'a InlineAsmOperand),
+ ClobberAbi(Symbol),
+ Options(InlineAsmOptions),
+ }
+
+ let mut args = vec![AsmArg::Template(InlineAsmTemplatePiece::to_string(&asm.template))];
+ args.extend(asm.operands.iter().map(|(o, _)| AsmArg::Operand(o)));
+ for (abi, _) in &asm.clobber_abis {
+ args.push(AsmArg::ClobberAbi(*abi));
+ }
+ if !asm.options.is_empty() {
+ args.push(AsmArg::Options(asm.options));
+ }
+
+ self.popen();
+ self.commasep(Consistent, &args, |s, arg| match arg {
+ AsmArg::Template(template) => s.print_string(&template, ast::StrStyle::Cooked),
+ AsmArg::Operand(op) => {
+ let print_reg_or_class = |s: &mut Self, r: &InlineAsmRegOrRegClass| match r {
+ InlineAsmRegOrRegClass::Reg(r) => s.print_symbol(*r, ast::StrStyle::Cooked),
+ InlineAsmRegOrRegClass::RegClass(r) => s.word(r.to_string()),
+ };
+ match op {
+ InlineAsmOperand::In { reg, expr } => {
+ s.word("in");
+ s.popen();
+ print_reg_or_class(s, reg);
+ s.pclose();
+ s.space();
+ s.print_expr(expr);
+ }
+ InlineAsmOperand::Out { reg, late, expr } => {
+ s.word(if *late { "lateout" } else { "out" });
+ s.popen();
+ print_reg_or_class(s, reg);
+ s.pclose();
+ s.space();
+ match expr {
+ Some(expr) => s.print_expr(expr),
+ None => s.word("_"),
+ }
+ }
+ InlineAsmOperand::InOut { reg, late, expr } => {
+ s.word(if *late { "inlateout" } else { "inout" });
+ s.popen();
+ print_reg_or_class(s, reg);
+ s.pclose();
+ s.space();
+ s.print_expr(expr);
+ }
+ InlineAsmOperand::SplitInOut { reg, late, in_expr, out_expr } => {
+ s.word(if *late { "inlateout" } else { "inout" });
+ s.popen();
+ print_reg_or_class(s, reg);
+ s.pclose();
+ s.space();
+ s.print_expr(in_expr);
+ s.space();
+ s.word_space("=>");
+ match out_expr {
+ Some(out_expr) => s.print_expr(out_expr),
+ None => s.word("_"),
+ }
+ }
+ InlineAsmOperand::Const { anon_const } => {
+ s.word("const");
+ s.space();
+ s.print_expr(&anon_const.value);
+ }
+ InlineAsmOperand::Sym { sym } => {
+ s.word("sym");
+ s.space();
+ if let Some(qself) = &sym.qself {
+ s.print_qpath(&sym.path, qself, true);
+ } else {
+ s.print_path(&sym.path, true, 0);
+ }
+ }
+ }
+ }
+ AsmArg::ClobberAbi(abi) => {
+ s.word("clobber_abi");
+ s.popen();
+ s.print_symbol(*abi, ast::StrStyle::Cooked);
+ s.pclose();
+ }
+ AsmArg::Options(opts) => {
+ s.word("options");
+ s.popen();
+ let mut options = vec![];
+ if opts.contains(InlineAsmOptions::PURE) {
+ options.push("pure");
+ }
+ if opts.contains(InlineAsmOptions::NOMEM) {
+ options.push("nomem");
+ }
+ if opts.contains(InlineAsmOptions::READONLY) {
+ options.push("readonly");
+ }
+ if opts.contains(InlineAsmOptions::PRESERVES_FLAGS) {
+ options.push("preserves_flags");
+ }
+ if opts.contains(InlineAsmOptions::NORETURN) {
+ options.push("noreturn");
+ }
+ if opts.contains(InlineAsmOptions::NOSTACK) {
+ options.push("nostack");
+ }
+ if opts.contains(InlineAsmOptions::ATT_SYNTAX) {
+ options.push("att_syntax");
+ }
+ if opts.contains(InlineAsmOptions::RAW) {
+ options.push("raw");
+ }
+ if opts.contains(InlineAsmOptions::MAY_UNWIND) {
+ options.push("may_unwind");
+ }
+ s.commasep(Inconsistent, &options, |s, &opt| {
+ s.word(opt);
+ });
+ s.pclose();
+ }
+ });
+ self.pclose();
+ }
+
+ pub(crate) fn print_local_decl(&mut self, loc: &ast::Local) {
+ self.print_pat(&loc.pat);
+ if let Some(ref ty) = loc.ty {
+ self.word_space(":");
+ self.print_type(ty);
+ }
+ }
+
+ pub(crate) fn print_name(&mut self, name: Symbol) {
+ self.word(name.to_string());
+ self.ann.post(self, AnnNode::Name(&name))
+ }
+
+ fn print_qpath(&mut self, path: &ast::Path, qself: &ast::QSelf, colons_before_params: bool) {
+ self.word("<");
+ self.print_type(&qself.ty);
+ if qself.position > 0 {
+ self.space();
+ self.word_space("as");
+ let depth = path.segments.len() - qself.position;
+ self.print_path(path, false, depth);
+ }
+ self.word(">");
+ for item_segment in &path.segments[qself.position..] {
+ self.word("::");
+ self.print_ident(item_segment.ident);
+ if let Some(ref args) = item_segment.args {
+ self.print_generic_args(args, colons_before_params)
+ }
+ }
+ }
+
+ pub(crate) fn print_pat(&mut self, pat: &ast::Pat) {
+ self.maybe_print_comment(pat.span.lo());
+ self.ann.pre(self, AnnNode::Pat(pat));
+ /* Pat isn't normalized, but the beauty of it
+ is that it doesn't matter */
+ match pat.kind {
+ PatKind::Wild => self.word("_"),
+ PatKind::Ident(binding_mode, ident, ref sub) => {
+ match binding_mode {
+ ast::BindingMode::ByRef(mutbl) => {
+ self.word_nbsp("ref");
+ self.print_mutability(mutbl, false);
+ }
+ ast::BindingMode::ByValue(ast::Mutability::Not) => {}
+ ast::BindingMode::ByValue(ast::Mutability::Mut) => {
+ self.word_nbsp("mut");
+ }
+ }
+ self.print_ident(ident);
+ if let Some(ref p) = *sub {
+ self.space();
+ self.word_space("@");
+ self.print_pat(p);
+ }
+ }
+ PatKind::TupleStruct(ref qself, ref path, ref elts) => {
+ if let Some(qself) = qself {
+ self.print_qpath(path, qself, true);
+ } else {
+ self.print_path(path, true, 0);
+ }
+ self.popen();
+ self.commasep(Inconsistent, &elts, |s, p| s.print_pat(p));
+ self.pclose();
+ }
+ PatKind::Or(ref pats) => {
+ self.strsep("|", true, Inconsistent, &pats, |s, p| s.print_pat(p));
+ }
+ PatKind::Path(None, ref path) => {
+ self.print_path(path, true, 0);
+ }
+ PatKind::Path(Some(ref qself), ref path) => {
+ self.print_qpath(path, qself, false);
+ }
+ PatKind::Struct(ref qself, ref path, ref fields, etc) => {
+ if let Some(qself) = qself {
+ self.print_qpath(path, qself, true);
+ } else {
+ self.print_path(path, true, 0);
+ }
+ self.nbsp();
+ self.word("{");
+ let empty = fields.is_empty() && !etc;
+ if !empty {
+ self.space();
+ }
+ self.commasep_cmnt(
+ Consistent,
+ &fields,
+ |s, f| {
+ s.cbox(INDENT_UNIT);
+ if !f.is_shorthand {
+ s.print_ident(f.ident);
+ s.word_nbsp(":");
+ }
+ s.print_pat(&f.pat);
+ s.end();
+ },
+ |f| f.pat.span,
+ );
+ if etc {
+ if !fields.is_empty() {
+ self.word_space(",");
+ }
+ self.word("..");
+ }
+ if !empty {
+ self.space();
+ }
+ self.word("}");
+ }
+ PatKind::Tuple(ref elts) => {
+ self.popen();
+ self.commasep(Inconsistent, &elts, |s, p| s.print_pat(p));
+ if elts.len() == 1 {
+ self.word(",");
+ }
+ self.pclose();
+ }
+ PatKind::Box(ref inner) => {
+ self.word("box ");
+ self.print_pat(inner);
+ }
+ PatKind::Ref(ref inner, mutbl) => {
+ self.word("&");
+ if mutbl == ast::Mutability::Mut {
+ self.word("mut ");
+ }
+ if let PatKind::Ident(ast::BindingMode::ByValue(ast::Mutability::Mut), ..) =
+ inner.kind
+ {
+ self.popen();
+ self.print_pat(inner);
+ self.pclose();
+ } else {
+ self.print_pat(inner);
+ }
+ }
+ PatKind::Lit(ref e) => self.print_expr(&**e),
+ PatKind::Range(ref begin, ref end, Spanned { node: ref end_kind, .. }) => {
+ if let Some(e) = begin {
+ self.print_expr(e);
+ }
+ match *end_kind {
+ RangeEnd::Included(RangeSyntax::DotDotDot) => self.word("..."),
+ RangeEnd::Included(RangeSyntax::DotDotEq) => self.word("..="),
+ RangeEnd::Excluded => self.word(".."),
+ }
+ if let Some(e) = end {
+ self.print_expr(e);
+ }
+ }
+ PatKind::Slice(ref elts) => {
+ self.word("[");
+ self.commasep(Inconsistent, &elts, |s, p| s.print_pat(p));
+ self.word("]");
+ }
+ PatKind::Rest => self.word(".."),
+ PatKind::Paren(ref inner) => {
+ self.popen();
+ self.print_pat(inner);
+ self.pclose();
+ }
+ PatKind::MacCall(ref m) => self.print_mac(m),
+ }
+ self.ann.post(self, AnnNode::Pat(pat))
+ }
+
+ fn print_explicit_self(&mut self, explicit_self: &ast::ExplicitSelf) {
+ match explicit_self.node {
+ SelfKind::Value(m) => {
+ self.print_mutability(m, false);
+ self.word("self")
+ }
+ SelfKind::Region(ref lt, m) => {
+ self.word("&");
+ self.print_opt_lifetime(lt);
+ self.print_mutability(m, false);
+ self.word("self")
+ }
+ SelfKind::Explicit(ref typ, m) => {
+ self.print_mutability(m, false);
+ self.word("self");
+ self.word_space(":");
+ self.print_type(typ)
+ }
+ }
+ }
+
+ pub(crate) fn print_asyncness(&mut self, asyncness: ast::Async) {
+ if asyncness.is_async() {
+ self.word_nbsp("async");
+ }
+ }
+
+ pub fn print_type_bounds(&mut self, bounds: &[ast::GenericBound]) {
+ let mut first = true;
+ for bound in bounds {
+ if first {
+ first = false;
+ } else {
+ self.nbsp();
+ self.word_space("+");
+ }
+
+ match bound {
+ GenericBound::Trait(tref, modifier) => {
+ if modifier == &TraitBoundModifier::Maybe {
+ self.word("?");
+ }
+ self.print_poly_trait_ref(tref);
+ }
+ GenericBound::Outlives(lt) => self.print_lifetime(*lt),
+ }
+ }
+ }
+
+ pub(crate) fn print_lifetime(&mut self, lifetime: ast::Lifetime) {
+ self.print_name(lifetime.ident.name)
+ }
+
+ pub(crate) fn print_lifetime_bounds(&mut self, bounds: &ast::GenericBounds) {
+ for (i, bound) in bounds.iter().enumerate() {
+ if i != 0 {
+ self.word(" + ");
+ }
+ match bound {
+ ast::GenericBound::Outlives(lt) => self.print_lifetime(*lt),
+ _ => panic!(),
+ }
+ }
+ }
+
+ pub(crate) fn print_generic_params(&mut self, generic_params: &[ast::GenericParam]) {
+ if generic_params.is_empty() {
+ return;
+ }
+
+ self.word("<");
+
+ self.commasep(Inconsistent, &generic_params, |s, param| {
+ s.print_outer_attributes_inline(&param.attrs);
+
+ match param.kind {
+ ast::GenericParamKind::Lifetime => {
+ let lt = ast::Lifetime { id: param.id, ident: param.ident };
+ s.print_lifetime(lt);
+ if !param.bounds.is_empty() {
+ s.word_nbsp(":");
+ s.print_lifetime_bounds(&param.bounds)
+ }
+ }
+ ast::GenericParamKind::Type { ref default } => {
+ s.print_ident(param.ident);
+ if !param.bounds.is_empty() {
+ s.word_nbsp(":");
+ s.print_type_bounds(&param.bounds);
+ }
+ if let Some(ref default) = default {
+ s.space();
+ s.word_space("=");
+ s.print_type(default)
+ }
+ }
+ ast::GenericParamKind::Const { ref ty, kw_span: _, ref default } => {
+ s.word_space("const");
+ s.print_ident(param.ident);
+ s.space();
+ s.word_space(":");
+ s.print_type(ty);
+ if !param.bounds.is_empty() {
+ s.word_nbsp(":");
+ s.print_type_bounds(&param.bounds);
+ }
+ if let Some(ref default) = default {
+ s.space();
+ s.word_space("=");
+ s.print_expr(&default.value);
+ }
+ }
+ }
+ });
+
+ self.word(">");
+ }
+
+ pub fn print_mutability(&mut self, mutbl: ast::Mutability, print_const: bool) {
+ match mutbl {
+ ast::Mutability::Mut => self.word_nbsp("mut"),
+ ast::Mutability::Not => {
+ if print_const {
+ self.word_nbsp("const");
+ }
+ }
+ }
+ }
+
+ pub(crate) fn print_mt(&mut self, mt: &ast::MutTy, print_const: bool) {
+ self.print_mutability(mt.mutbl, print_const);
+ self.print_type(&mt.ty)
+ }
+
+ pub(crate) fn print_param(&mut self, input: &ast::Param, is_closure: bool) {
+ self.ibox(INDENT_UNIT);
+
+ self.print_outer_attributes_inline(&input.attrs);
+
+ match input.ty.kind {
+ ast::TyKind::Infer if is_closure => self.print_pat(&input.pat),
+ _ => {
+ if let Some(eself) = input.to_self() {
+ self.print_explicit_self(&eself);
+ } else {
+ let invalid = if let PatKind::Ident(_, ident, _) = input.pat.kind {
+ ident.name == kw::Empty
+ } else {
+ false
+ };
+ if !invalid {
+ self.print_pat(&input.pat);
+ self.word(":");
+ self.space();
+ }
+ self.print_type(&input.ty);
+ }
+ }
+ }
+ self.end();
+ }
+
+ pub(crate) fn print_fn_ret_ty(&mut self, fn_ret_ty: &ast::FnRetTy) {
+ if let ast::FnRetTy::Ty(ty) = fn_ret_ty {
+ self.space_if_not_bol();
+ self.ibox(INDENT_UNIT);
+ self.word_space("->");
+ self.print_type(ty);
+ self.end();
+ self.maybe_print_comment(ty.span.lo());
+ }
+ }
+
+ pub(crate) fn print_ty_fn(
+ &mut self,
+ ext: ast::Extern,
+ unsafety: ast::Unsafe,
+ decl: &ast::FnDecl,
+ name: Option<Ident>,
+ generic_params: &[ast::GenericParam],
+ ) {
+ self.ibox(INDENT_UNIT);
+ self.print_formal_generic_params(generic_params);
+ let generics = ast::Generics {
+ params: Vec::new(),
+ where_clause: ast::WhereClause {
+ has_where_token: false,
+ predicates: Vec::new(),
+ span: rustc_span::DUMMY_SP,
+ },
+ span: rustc_span::DUMMY_SP,
+ };
+ let header = ast::FnHeader { unsafety, ext, ..ast::FnHeader::default() };
+ self.print_fn(decl, header, name, &generics);
+ self.end();
+ }
+
+ pub(crate) fn print_fn_header_info(&mut self, header: ast::FnHeader) {
+ self.print_constness(header.constness);
+ self.print_asyncness(header.asyncness);
+ self.print_unsafety(header.unsafety);
+
+ match header.ext {
+ ast::Extern::None => {}
+ ast::Extern::Implicit(_) => {
+ self.word_nbsp("extern");
+ }
+ ast::Extern::Explicit(abi, _) => {
+ self.word_nbsp("extern");
+ self.print_literal(&abi.as_lit());
+ self.nbsp();
+ }
+ }
+
+ self.word("fn")
+ }
+
+ pub(crate) fn print_unsafety(&mut self, s: ast::Unsafe) {
+ match s {
+ ast::Unsafe::No => {}
+ ast::Unsafe::Yes(_) => self.word_nbsp("unsafe"),
+ }
+ }
+
+ pub(crate) fn print_constness(&mut self, s: ast::Const) {
+ match s {
+ ast::Const::No => {}
+ ast::Const::Yes(_) => self.word_nbsp("const"),
+ }
+ }
+
+ pub(crate) fn print_is_auto(&mut self, s: ast::IsAuto) {
+ match s {
+ ast::IsAuto::Yes => self.word_nbsp("auto"),
+ ast::IsAuto::No => {}
+ }
+ }
+}
diff --git a/compiler/rustc_ast_pretty/src/pprust/state/delimited.rs b/compiler/rustc_ast_pretty/src/pprust/state/delimited.rs
new file mode 100644
index 000000000..fe0640baa
--- /dev/null
+++ b/compiler/rustc_ast_pretty/src/pprust/state/delimited.rs
@@ -0,0 +1,41 @@
+use std::iter::Peekable;
+use std::mem;
+use std::ops::Deref;
+
+pub struct Delimited<I: Iterator> {
+ is_first: bool,
+ iter: Peekable<I>,
+}
+
+pub trait IterDelimited: Iterator + Sized {
+ fn delimited(self) -> Delimited<Self> {
+ Delimited { is_first: true, iter: self.peekable() }
+ }
+}
+
+impl<I: Iterator> IterDelimited for I {}
+
+pub struct IteratorItem<T> {
+ value: T,
+ pub is_first: bool,
+ pub is_last: bool,
+}
+
+impl<I: Iterator> Iterator for Delimited<I> {
+ type Item = IteratorItem<I::Item>;
+
+ fn next(&mut self) -> Option<Self::Item> {
+ let value = self.iter.next()?;
+ let is_first = mem::replace(&mut self.is_first, false);
+ let is_last = self.iter.peek().is_none();
+ Some(IteratorItem { value, is_first, is_last })
+ }
+}
+
+impl<T> Deref for IteratorItem<T> {
+ type Target = T;
+
+ fn deref(&self) -> &Self::Target {
+ &self.value
+ }
+}
diff --git a/compiler/rustc_ast_pretty/src/pprust/state/expr.rs b/compiler/rustc_ast_pretty/src/pprust/state/expr.rs
new file mode 100644
index 000000000..ead38caee
--- /dev/null
+++ b/compiler/rustc_ast_pretty/src/pprust/state/expr.rs
@@ -0,0 +1,621 @@
+use crate::pp::Breaks::Inconsistent;
+use crate::pprust::state::{AnnNode, IterDelimited, PrintState, State, INDENT_UNIT};
+
+use rustc_ast::ptr::P;
+use rustc_ast::util::parser::{self, AssocOp, Fixity};
+use rustc_ast::{self as ast, BlockCheckMode};
+
+impl<'a> State<'a> {
+ fn print_else(&mut self, els: Option<&ast::Expr>) {
+ if let Some(_else) = els {
+ match _else.kind {
+ // Another `else if` block.
+ ast::ExprKind::If(ref i, ref then, ref e) => {
+ self.cbox(INDENT_UNIT - 1);
+ self.ibox(0);
+ self.word(" else if ");
+ self.print_expr_as_cond(i);
+ self.space();
+ self.print_block(then);
+ self.print_else(e.as_deref())
+ }
+ // Final `else` block.
+ ast::ExprKind::Block(ref b, _) => {
+ self.cbox(INDENT_UNIT - 1);
+ self.ibox(0);
+ self.word(" else ");
+ self.print_block(b)
+ }
+ // Constraints would be great here!
+ _ => {
+ panic!("print_if saw if with weird alternative");
+ }
+ }
+ }
+ }
+
+ fn print_if(&mut self, test: &ast::Expr, blk: &ast::Block, elseopt: Option<&ast::Expr>) {
+ self.head("if");
+ self.print_expr_as_cond(test);
+ self.space();
+ self.print_block(blk);
+ self.print_else(elseopt)
+ }
+
+ fn print_call_post(&mut self, args: &[P<ast::Expr>]) {
+ self.popen();
+ self.commasep_exprs(Inconsistent, args);
+ self.pclose()
+ }
+
+ fn print_expr_maybe_paren(&mut self, expr: &ast::Expr, prec: i8) {
+ self.print_expr_cond_paren(expr, expr.precedence().order() < prec)
+ }
+
+ /// Prints an expr using syntax that's acceptable in a condition position, such as the `cond` in
+ /// `if cond { ... }`.
+ fn print_expr_as_cond(&mut self, expr: &ast::Expr) {
+ self.print_expr_cond_paren(expr, Self::cond_needs_par(expr))
+ }
+
+ // Does `expr` need parentheses when printed in a condition position?
+ //
+ // These cases need parens due to the parse error observed in #26461: `if return {}`
+ // parses as the erroneous construct `if (return {})`, not `if (return) {}`.
+ pub(super) fn cond_needs_par(expr: &ast::Expr) -> bool {
+ match expr.kind {
+ ast::ExprKind::Break(..)
+ | ast::ExprKind::Closure(..)
+ | ast::ExprKind::Ret(..)
+ | ast::ExprKind::Yeet(..) => true,
+ _ => parser::contains_exterior_struct_lit(expr),
+ }
+ }
+
+ /// Prints `expr` or `(expr)` when `needs_par` holds.
+ pub(super) fn print_expr_cond_paren(&mut self, expr: &ast::Expr, needs_par: bool) {
+ if needs_par {
+ self.popen();
+ }
+ self.print_expr(expr);
+ if needs_par {
+ self.pclose();
+ }
+ }
+
+ fn print_expr_vec(&mut self, exprs: &[P<ast::Expr>]) {
+ self.ibox(INDENT_UNIT);
+ self.word("[");
+ self.commasep_exprs(Inconsistent, exprs);
+ self.word("]");
+ self.end();
+ }
+
+ pub(super) fn print_expr_anon_const(
+ &mut self,
+ expr: &ast::AnonConst,
+ attrs: &[ast::Attribute],
+ ) {
+ self.ibox(INDENT_UNIT);
+ self.word("const");
+ self.nbsp();
+ if let ast::ExprKind::Block(block, None) = &expr.value.kind {
+ self.cbox(0);
+ self.ibox(0);
+ self.print_block_with_attrs(block, attrs);
+ } else {
+ self.print_expr(&expr.value);
+ }
+ self.end();
+ }
+
+ fn print_expr_repeat(&mut self, element: &ast::Expr, count: &ast::AnonConst) {
+ self.ibox(INDENT_UNIT);
+ self.word("[");
+ self.print_expr(element);
+ self.word_space(";");
+ self.print_expr(&count.value);
+ self.word("]");
+ self.end();
+ }
+
+ fn print_expr_struct(
+ &mut self,
+ qself: &Option<ast::QSelf>,
+ path: &ast::Path,
+ fields: &[ast::ExprField],
+ rest: &ast::StructRest,
+ ) {
+ if let Some(qself) = qself {
+ self.print_qpath(path, qself, true);
+ } else {
+ self.print_path(path, true, 0);
+ }
+ self.nbsp();
+ self.word("{");
+ let has_rest = match rest {
+ ast::StructRest::Base(_) | ast::StructRest::Rest(_) => true,
+ ast::StructRest::None => false,
+ };
+ if fields.is_empty() && !has_rest {
+ self.word("}");
+ return;
+ }
+ self.cbox(0);
+ for field in fields.iter().delimited() {
+ self.maybe_print_comment(field.span.hi());
+ self.print_outer_attributes(&field.attrs);
+ if field.is_first {
+ self.space_if_not_bol();
+ }
+ if !field.is_shorthand {
+ self.print_ident(field.ident);
+ self.word_nbsp(":");
+ }
+ self.print_expr(&field.expr);
+ if !field.is_last || has_rest {
+ self.word_space(",");
+ } else {
+ self.trailing_comma_or_space();
+ }
+ }
+ if has_rest {
+ if fields.is_empty() {
+ self.space();
+ }
+ self.word("..");
+ if let ast::StructRest::Base(expr) = rest {
+ self.print_expr(expr);
+ }
+ self.space();
+ }
+ self.offset(-INDENT_UNIT);
+ self.end();
+ self.word("}");
+ }
+
+ fn print_expr_tup(&mut self, exprs: &[P<ast::Expr>]) {
+ self.popen();
+ self.commasep_exprs(Inconsistent, exprs);
+ if exprs.len() == 1 {
+ self.word(",");
+ }
+ self.pclose()
+ }
+
+ fn print_expr_call(&mut self, func: &ast::Expr, args: &[P<ast::Expr>]) {
+ let prec = match func.kind {
+ ast::ExprKind::Field(..) => parser::PREC_FORCE_PAREN,
+ _ => parser::PREC_POSTFIX,
+ };
+
+ self.print_expr_maybe_paren(func, prec);
+ self.print_call_post(args)
+ }
+
+ fn print_expr_method_call(&mut self, segment: &ast::PathSegment, args: &[P<ast::Expr>]) {
+ let base_args = &args[1..];
+ self.print_expr_maybe_paren(&args[0], parser::PREC_POSTFIX);
+ self.word(".");
+ self.print_ident(segment.ident);
+ if let Some(ref args) = segment.args {
+ self.print_generic_args(args, true);
+ }
+ self.print_call_post(base_args)
+ }
+
+ fn print_expr_binary(&mut self, op: ast::BinOp, lhs: &ast::Expr, rhs: &ast::Expr) {
+ let assoc_op = AssocOp::from_ast_binop(op.node);
+ let prec = assoc_op.precedence() as i8;
+ let fixity = assoc_op.fixity();
+
+ let (left_prec, right_prec) = match fixity {
+ Fixity::Left => (prec, prec + 1),
+ Fixity::Right => (prec + 1, prec),
+ Fixity::None => (prec + 1, prec + 1),
+ };
+
+ let left_prec = match (&lhs.kind, op.node) {
+ // These cases need parens: `x as i32 < y` has the parser thinking that `i32 < y` is
+ // the beginning of a path type. It starts trying to parse `x as (i32 < y ...` instead
+ // of `(x as i32) < ...`. We need to convince it _not_ to do that.
+ (&ast::ExprKind::Cast { .. }, ast::BinOpKind::Lt | ast::BinOpKind::Shl) => {
+ parser::PREC_FORCE_PAREN
+ }
+ // We are given `(let _ = a) OP b`.
+ //
+ // - When `OP <= LAnd` we should print `let _ = a OP b` to avoid redundant parens
+ // as the parser will interpret this as `(let _ = a) OP b`.
+ //
+ // - Otherwise, e.g. when we have `(let a = b) < c` in AST,
+ // parens are required since the parser would interpret `let a = b < c` as
+ // `let a = (b < c)`. To achieve this, we force parens.
+ (&ast::ExprKind::Let { .. }, _) if !parser::needs_par_as_let_scrutinee(prec) => {
+ parser::PREC_FORCE_PAREN
+ }
+ _ => left_prec,
+ };
+
+ self.print_expr_maybe_paren(lhs, left_prec);
+ self.space();
+ self.word_space(op.node.to_string());
+ self.print_expr_maybe_paren(rhs, right_prec)
+ }
+
+ fn print_expr_unary(&mut self, op: ast::UnOp, expr: &ast::Expr) {
+ self.word(ast::UnOp::to_string(op));
+ self.print_expr_maybe_paren(expr, parser::PREC_PREFIX)
+ }
+
+ fn print_expr_addr_of(
+ &mut self,
+ kind: ast::BorrowKind,
+ mutability: ast::Mutability,
+ expr: &ast::Expr,
+ ) {
+ self.word("&");
+ match kind {
+ ast::BorrowKind::Ref => self.print_mutability(mutability, false),
+ ast::BorrowKind::Raw => {
+ self.word_nbsp("raw");
+ self.print_mutability(mutability, true);
+ }
+ }
+ self.print_expr_maybe_paren(expr, parser::PREC_PREFIX)
+ }
+
+ pub fn print_expr(&mut self, expr: &ast::Expr) {
+ self.print_expr_outer_attr_style(expr, true)
+ }
+
+ pub(super) fn print_expr_outer_attr_style(&mut self, expr: &ast::Expr, is_inline: bool) {
+ self.maybe_print_comment(expr.span.lo());
+
+ let attrs = &expr.attrs;
+ if is_inline {
+ self.print_outer_attributes_inline(attrs);
+ } else {
+ self.print_outer_attributes(attrs);
+ }
+
+ self.ibox(INDENT_UNIT);
+ self.ann.pre(self, AnnNode::Expr(expr));
+ match expr.kind {
+ ast::ExprKind::Box(ref expr) => {
+ self.word_space("box");
+ self.print_expr_maybe_paren(expr, parser::PREC_PREFIX);
+ }
+ ast::ExprKind::Array(ref exprs) => {
+ self.print_expr_vec(exprs);
+ }
+ ast::ExprKind::ConstBlock(ref anon_const) => {
+ self.print_expr_anon_const(anon_const, attrs);
+ }
+ ast::ExprKind::Repeat(ref element, ref count) => {
+ self.print_expr_repeat(element, count);
+ }
+ ast::ExprKind::Struct(ref se) => {
+ self.print_expr_struct(&se.qself, &se.path, &se.fields, &se.rest);
+ }
+ ast::ExprKind::Tup(ref exprs) => {
+ self.print_expr_tup(exprs);
+ }
+ ast::ExprKind::Call(ref func, ref args) => {
+ self.print_expr_call(func, &args);
+ }
+ ast::ExprKind::MethodCall(ref segment, ref args, _) => {
+ self.print_expr_method_call(segment, &args);
+ }
+ ast::ExprKind::Binary(op, ref lhs, ref rhs) => {
+ self.print_expr_binary(op, lhs, rhs);
+ }
+ ast::ExprKind::Unary(op, ref expr) => {
+ self.print_expr_unary(op, expr);
+ }
+ ast::ExprKind::AddrOf(k, m, ref expr) => {
+ self.print_expr_addr_of(k, m, expr);
+ }
+ ast::ExprKind::Lit(ref lit) => {
+ self.print_literal(lit);
+ }
+ ast::ExprKind::Cast(ref expr, ref ty) => {
+ let prec = AssocOp::As.precedence() as i8;
+ self.print_expr_maybe_paren(expr, prec);
+ self.space();
+ self.word_space("as");
+ self.print_type(ty);
+ }
+ ast::ExprKind::Type(ref expr, ref ty) => {
+ let prec = AssocOp::Colon.precedence() as i8;
+ self.print_expr_maybe_paren(expr, prec);
+ self.word_space(":");
+ self.print_type(ty);
+ }
+ ast::ExprKind::Let(ref pat, ref scrutinee, _) => {
+ self.print_let(pat, scrutinee);
+ }
+ ast::ExprKind::If(ref test, ref blk, ref elseopt) => {
+ self.print_if(test, blk, elseopt.as_deref())
+ }
+ ast::ExprKind::While(ref test, ref blk, opt_label) => {
+ if let Some(label) = opt_label {
+ self.print_ident(label.ident);
+ self.word_space(":");
+ }
+ self.cbox(0);
+ self.ibox(0);
+ self.word_nbsp("while");
+ self.print_expr_as_cond(test);
+ self.space();
+ self.print_block_with_attrs(blk, attrs);
+ }
+ ast::ExprKind::ForLoop(ref pat, ref iter, ref blk, opt_label) => {
+ if let Some(label) = opt_label {
+ self.print_ident(label.ident);
+ self.word_space(":");
+ }
+ self.cbox(0);
+ self.ibox(0);
+ self.word_nbsp("for");
+ self.print_pat(pat);
+ self.space();
+ self.word_space("in");
+ self.print_expr_as_cond(iter);
+ self.space();
+ self.print_block_with_attrs(blk, attrs);
+ }
+ ast::ExprKind::Loop(ref blk, opt_label) => {
+ if let Some(label) = opt_label {
+ self.print_ident(label.ident);
+ self.word_space(":");
+ }
+ self.cbox(0);
+ self.ibox(0);
+ self.word_nbsp("loop");
+ self.print_block_with_attrs(blk, attrs);
+ }
+ ast::ExprKind::Match(ref expr, ref arms) => {
+ self.cbox(0);
+ self.ibox(0);
+ self.word_nbsp("match");
+ self.print_expr_as_cond(expr);
+ self.space();
+ self.bopen();
+ self.print_inner_attributes_no_trailing_hardbreak(attrs);
+ for arm in arms {
+ self.print_arm(arm);
+ }
+ let empty = attrs.is_empty() && arms.is_empty();
+ self.bclose(expr.span, empty);
+ }
+ ast::ExprKind::Closure(
+ ref binder,
+ capture_clause,
+ asyncness,
+ movability,
+ ref decl,
+ ref body,
+ _,
+ ) => {
+ self.print_closure_binder(binder);
+ self.print_movability(movability);
+ self.print_asyncness(asyncness);
+ self.print_capture_clause(capture_clause);
+
+ self.print_fn_params_and_ret(decl, true);
+ self.space();
+ self.print_expr(body);
+ self.end(); // need to close a box
+
+ // a box will be closed by print_expr, but we didn't want an overall
+ // wrapper so we closed the corresponding opening. so create an
+ // empty box to satisfy the close.
+ self.ibox(0);
+ }
+ ast::ExprKind::Block(ref blk, opt_label) => {
+ if let Some(label) = opt_label {
+ self.print_ident(label.ident);
+ self.word_space(":");
+ }
+ // containing cbox, will be closed by print-block at }
+ self.cbox(0);
+ // head-box, will be closed by print-block after {
+ self.ibox(0);
+ self.print_block_with_attrs(blk, attrs);
+ }
+ ast::ExprKind::Async(capture_clause, _, ref blk) => {
+ self.word_nbsp("async");
+ self.print_capture_clause(capture_clause);
+ // cbox/ibox in analogy to the `ExprKind::Block` arm above
+ self.cbox(0);
+ self.ibox(0);
+ self.print_block_with_attrs(blk, attrs);
+ }
+ ast::ExprKind::Await(ref expr) => {
+ self.print_expr_maybe_paren(expr, parser::PREC_POSTFIX);
+ self.word(".await");
+ }
+ ast::ExprKind::Assign(ref lhs, ref rhs, _) => {
+ let prec = AssocOp::Assign.precedence() as i8;
+ self.print_expr_maybe_paren(lhs, prec + 1);
+ self.space();
+ self.word_space("=");
+ self.print_expr_maybe_paren(rhs, prec);
+ }
+ ast::ExprKind::AssignOp(op, ref lhs, ref rhs) => {
+ let prec = AssocOp::Assign.precedence() as i8;
+ self.print_expr_maybe_paren(lhs, prec + 1);
+ self.space();
+ self.word(op.node.to_string());
+ self.word_space("=");
+ self.print_expr_maybe_paren(rhs, prec);
+ }
+ ast::ExprKind::Field(ref expr, ident) => {
+ self.print_expr_maybe_paren(expr, parser::PREC_POSTFIX);
+ self.word(".");
+ self.print_ident(ident);
+ }
+ ast::ExprKind::Index(ref expr, ref index) => {
+ self.print_expr_maybe_paren(expr, parser::PREC_POSTFIX);
+ self.word("[");
+ self.print_expr(index);
+ self.word("]");
+ }
+ ast::ExprKind::Range(ref start, ref end, limits) => {
+ // Special case for `Range`. `AssocOp` claims that `Range` has higher precedence
+ // than `Assign`, but `x .. x = x` gives a parse error instead of `x .. (x = x)`.
+ // Here we use a fake precedence value so that any child with lower precedence than
+ // a "normal" binop gets parenthesized. (`LOr` is the lowest-precedence binop.)
+ let fake_prec = AssocOp::LOr.precedence() as i8;
+ if let Some(ref e) = *start {
+ self.print_expr_maybe_paren(e, fake_prec);
+ }
+ if limits == ast::RangeLimits::HalfOpen {
+ self.word("..");
+ } else {
+ self.word("..=");
+ }
+ if let Some(ref e) = *end {
+ self.print_expr_maybe_paren(e, fake_prec);
+ }
+ }
+ ast::ExprKind::Underscore => self.word("_"),
+ ast::ExprKind::Path(None, ref path) => self.print_path(path, true, 0),
+ ast::ExprKind::Path(Some(ref qself), ref path) => self.print_qpath(path, qself, true),
+ ast::ExprKind::Break(opt_label, ref opt_expr) => {
+ self.word("break");
+ if let Some(label) = opt_label {
+ self.space();
+ self.print_ident(label.ident);
+ }
+ if let Some(ref expr) = *opt_expr {
+ self.space();
+ self.print_expr_maybe_paren(expr, parser::PREC_JUMP);
+ }
+ }
+ ast::ExprKind::Continue(opt_label) => {
+ self.word("continue");
+ if let Some(label) = opt_label {
+ self.space();
+ self.print_ident(label.ident);
+ }
+ }
+ ast::ExprKind::Ret(ref result) => {
+ self.word("return");
+ if let Some(ref expr) = *result {
+ self.word(" ");
+ self.print_expr_maybe_paren(expr, parser::PREC_JUMP);
+ }
+ }
+ ast::ExprKind::Yeet(ref result) => {
+ self.word("do");
+ self.word(" ");
+ self.word("yeet");
+ if let Some(ref expr) = *result {
+ self.word(" ");
+ self.print_expr_maybe_paren(expr, parser::PREC_JUMP);
+ }
+ }
+ ast::ExprKind::InlineAsm(ref a) => {
+ self.word("asm!");
+ self.print_inline_asm(a);
+ }
+ ast::ExprKind::MacCall(ref m) => self.print_mac(m),
+ ast::ExprKind::Paren(ref e) => {
+ self.popen();
+ self.print_expr(e);
+ self.pclose();
+ }
+ ast::ExprKind::Yield(ref e) => {
+ self.word("yield");
+
+ if let Some(ref expr) = *e {
+ self.space();
+ self.print_expr_maybe_paren(expr, parser::PREC_JUMP);
+ }
+ }
+ ast::ExprKind::Try(ref e) => {
+ self.print_expr_maybe_paren(e, parser::PREC_POSTFIX);
+ self.word("?")
+ }
+ ast::ExprKind::TryBlock(ref blk) => {
+ self.cbox(0);
+ self.ibox(0);
+ self.word_nbsp("try");
+ self.print_block_with_attrs(blk, attrs)
+ }
+ ast::ExprKind::Err => {
+ self.popen();
+ self.word("/*ERROR*/");
+ self.pclose()
+ }
+ }
+ self.ann.post(self, AnnNode::Expr(expr));
+ self.end();
+ }
+
+ fn print_arm(&mut self, arm: &ast::Arm) {
+ // Note, I have no idea why this check is necessary, but here it is.
+ if arm.attrs.is_empty() {
+ self.space();
+ }
+ self.cbox(INDENT_UNIT);
+ self.ibox(0);
+ self.maybe_print_comment(arm.pat.span.lo());
+ self.print_outer_attributes(&arm.attrs);
+ self.print_pat(&arm.pat);
+ self.space();
+ if let Some(ref e) = arm.guard {
+ self.word_space("if");
+ self.print_expr(e);
+ self.space();
+ }
+ self.word_space("=>");
+
+ match arm.body.kind {
+ ast::ExprKind::Block(ref blk, opt_label) => {
+ if let Some(label) = opt_label {
+ self.print_ident(label.ident);
+ self.word_space(":");
+ }
+
+ // The block will close the pattern's ibox.
+ self.print_block_unclosed_indent(blk);
+
+ // If it is a user-provided unsafe block, print a comma after it.
+ if let BlockCheckMode::Unsafe(ast::UserProvided) = blk.rules {
+ self.word(",");
+ }
+ }
+ _ => {
+ self.end(); // Close the ibox for the pattern.
+ self.print_expr(&arm.body);
+ self.word(",");
+ }
+ }
+ self.end(); // Close enclosing cbox.
+ }
+
+ fn print_closure_binder(&mut self, binder: &ast::ClosureBinder) {
+ match binder {
+ ast::ClosureBinder::NotPresent => {}
+ ast::ClosureBinder::For { generic_params, .. } => {
+ self.print_formal_generic_params(&generic_params)
+ }
+ }
+ }
+
+ fn print_movability(&mut self, movability: ast::Movability) {
+ match movability {
+ ast::Movability::Static => self.word_space("static"),
+ ast::Movability::Movable => {}
+ }
+ }
+
+ fn print_capture_clause(&mut self, capture_clause: ast::CaptureBy) {
+ match capture_clause {
+ ast::CaptureBy::Value => self.word_space("move"),
+ ast::CaptureBy::Ref => {}
+ }
+ }
+}
diff --git a/compiler/rustc_ast_pretty/src/pprust/state/item.rs b/compiler/rustc_ast_pretty/src/pprust/state/item.rs
new file mode 100644
index 000000000..f1caf22f3
--- /dev/null
+++ b/compiler/rustc_ast_pretty/src/pprust/state/item.rs
@@ -0,0 +1,708 @@
+use crate::pp::Breaks::Inconsistent;
+use crate::pprust::state::delimited::IterDelimited;
+use crate::pprust::state::{AnnNode, PrintState, State, INDENT_UNIT};
+
+use rustc_ast as ast;
+use rustc_ast::GenericBound;
+use rustc_ast::ModKind;
+use rustc_span::symbol::Ident;
+
+fn visibility_qualified(vis: &ast::Visibility, s: &str) -> String {
+ format!("{}{}", State::to_string(|s| s.print_visibility(vis)), s)
+}
+
+impl<'a> State<'a> {
+ fn print_foreign_mod(&mut self, nmod: &ast::ForeignMod, attrs: &[ast::Attribute]) {
+ self.print_inner_attributes(attrs);
+ for item in &nmod.items {
+ self.print_foreign_item(item);
+ }
+ }
+
+ pub(crate) fn print_foreign_item(&mut self, item: &ast::ForeignItem) {
+ let ast::Item { id, span, ident, ref attrs, ref kind, ref vis, tokens: _ } = *item;
+ self.ann.pre(self, AnnNode::SubItem(id));
+ self.hardbreak_if_not_bol();
+ self.maybe_print_comment(span.lo());
+ self.print_outer_attributes(attrs);
+ match kind {
+ ast::ForeignItemKind::Fn(box ast::Fn { defaultness, sig, generics, body }) => {
+ self.print_fn_full(sig, ident, generics, vis, *defaultness, body.as_deref(), attrs);
+ }
+ ast::ForeignItemKind::Static(ty, mutbl, body) => {
+ let def = ast::Defaultness::Final;
+ self.print_item_const(ident, Some(*mutbl), ty, body.as_deref(), vis, def);
+ }
+ ast::ForeignItemKind::TyAlias(box ast::TyAlias {
+ defaultness,
+ generics,
+ where_clauses,
+ where_predicates_split,
+ bounds,
+ ty,
+ }) => {
+ self.print_associated_type(
+ ident,
+ generics,
+ *where_clauses,
+ *where_predicates_split,
+ bounds,
+ ty.as_deref(),
+ vis,
+ *defaultness,
+ );
+ }
+ ast::ForeignItemKind::MacCall(m) => {
+ self.print_mac(m);
+ if m.args.need_semicolon() {
+ self.word(";");
+ }
+ }
+ }
+ self.ann.post(self, AnnNode::SubItem(id))
+ }
+
+ fn print_item_const(
+ &mut self,
+ ident: Ident,
+ mutbl: Option<ast::Mutability>,
+ ty: &ast::Ty,
+ body: Option<&ast::Expr>,
+ vis: &ast::Visibility,
+ defaultness: ast::Defaultness,
+ ) {
+ self.head("");
+ self.print_visibility(vis);
+ self.print_defaultness(defaultness);
+ let leading = match mutbl {
+ None => "const",
+ Some(ast::Mutability::Not) => "static",
+ Some(ast::Mutability::Mut) => "static mut",
+ };
+ self.word_space(leading);
+ self.print_ident(ident);
+ self.word_space(":");
+ self.print_type(ty);
+ if body.is_some() {
+ self.space();
+ }
+ self.end(); // end the head-ibox
+ if let Some(body) = body {
+ self.word_space("=");
+ self.print_expr(body);
+ }
+ self.word(";");
+ self.end(); // end the outer cbox
+ }
+
+ fn print_associated_type(
+ &mut self,
+ ident: Ident,
+ generics: &ast::Generics,
+ where_clauses: (ast::TyAliasWhereClause, ast::TyAliasWhereClause),
+ where_predicates_split: usize,
+ bounds: &ast::GenericBounds,
+ ty: Option<&ast::Ty>,
+ vis: &ast::Visibility,
+ defaultness: ast::Defaultness,
+ ) {
+ let (before_predicates, after_predicates) =
+ generics.where_clause.predicates.split_at(where_predicates_split);
+ self.head("");
+ self.print_visibility(vis);
+ self.print_defaultness(defaultness);
+ self.word_space("type");
+ self.print_ident(ident);
+ self.print_generic_params(&generics.params);
+ if !bounds.is_empty() {
+ self.word_nbsp(":");
+ self.print_type_bounds(bounds);
+ }
+ self.print_where_clause_parts(where_clauses.0.0, before_predicates);
+ if let Some(ty) = ty {
+ self.space();
+ self.word_space("=");
+ self.print_type(ty);
+ }
+ self.print_where_clause_parts(where_clauses.1.0, after_predicates);
+ self.word(";");
+ self.end(); // end inner head-block
+ self.end(); // end outer head-block
+ }
+
+ /// Pretty-prints an item.
+ pub(crate) fn print_item(&mut self, item: &ast::Item) {
+ self.hardbreak_if_not_bol();
+ self.maybe_print_comment(item.span.lo());
+ self.print_outer_attributes(&item.attrs);
+ self.ann.pre(self, AnnNode::Item(item));
+ match item.kind {
+ ast::ItemKind::ExternCrate(orig_name) => {
+ self.head(visibility_qualified(&item.vis, "extern crate"));
+ if let Some(orig_name) = orig_name {
+ self.print_name(orig_name);
+ self.space();
+ self.word("as");
+ self.space();
+ }
+ self.print_ident(item.ident);
+ self.word(";");
+ self.end(); // end inner head-block
+ self.end(); // end outer head-block
+ }
+ ast::ItemKind::Use(ref tree) => {
+ self.print_visibility(&item.vis);
+ self.word_nbsp("use");
+ self.print_use_tree(tree);
+ self.word(";");
+ }
+ ast::ItemKind::Static(ref ty, mutbl, ref body) => {
+ let def = ast::Defaultness::Final;
+ self.print_item_const(item.ident, Some(mutbl), ty, body.as_deref(), &item.vis, def);
+ }
+ ast::ItemKind::Const(def, ref ty, ref body) => {
+ self.print_item_const(item.ident, None, ty, body.as_deref(), &item.vis, def);
+ }
+ ast::ItemKind::Fn(box ast::Fn { defaultness, ref sig, ref generics, ref body }) => {
+ let body = body.as_deref();
+ self.print_fn_full(
+ sig,
+ item.ident,
+ generics,
+ &item.vis,
+ defaultness,
+ body,
+ &item.attrs,
+ );
+ }
+ ast::ItemKind::Mod(unsafety, ref mod_kind) => {
+ self.head(Self::to_string(|s| {
+ s.print_visibility(&item.vis);
+ s.print_unsafety(unsafety);
+ s.word("mod");
+ }));
+ self.print_ident(item.ident);
+
+ match mod_kind {
+ ModKind::Loaded(items, ..) => {
+ self.nbsp();
+ self.bopen();
+ self.print_inner_attributes(&item.attrs);
+ for item in items {
+ self.print_item(item);
+ }
+ let empty = item.attrs.is_empty() && items.is_empty();
+ self.bclose(item.span, empty);
+ }
+ ModKind::Unloaded => {
+ self.word(";");
+ self.end(); // end inner head-block
+ self.end(); // end outer head-block
+ }
+ }
+ }
+ ast::ItemKind::ForeignMod(ref nmod) => {
+ self.head(Self::to_string(|s| {
+ s.print_unsafety(nmod.unsafety);
+ s.word("extern");
+ }));
+ if let Some(abi) = nmod.abi {
+ self.print_literal(&abi.as_lit());
+ self.nbsp();
+ }
+ self.bopen();
+ self.print_foreign_mod(nmod, &item.attrs);
+ let empty = item.attrs.is_empty() && nmod.items.is_empty();
+ self.bclose(item.span, empty);
+ }
+ ast::ItemKind::GlobalAsm(ref asm) => {
+ self.head(visibility_qualified(&item.vis, "global_asm!"));
+ self.print_inline_asm(asm);
+ self.end();
+ }
+ ast::ItemKind::TyAlias(box ast::TyAlias {
+ defaultness,
+ ref generics,
+ where_clauses,
+ where_predicates_split,
+ ref bounds,
+ ref ty,
+ }) => {
+ let ty = ty.as_deref();
+ self.print_associated_type(
+ item.ident,
+ generics,
+ where_clauses,
+ where_predicates_split,
+ bounds,
+ ty,
+ &item.vis,
+ defaultness,
+ );
+ }
+ ast::ItemKind::Enum(ref enum_definition, ref params) => {
+ self.print_enum_def(enum_definition, params, item.ident, item.span, &item.vis);
+ }
+ ast::ItemKind::Struct(ref struct_def, ref generics) => {
+ self.head(visibility_qualified(&item.vis, "struct"));
+ self.print_struct(struct_def, generics, item.ident, item.span, true);
+ }
+ ast::ItemKind::Union(ref struct_def, ref generics) => {
+ self.head(visibility_qualified(&item.vis, "union"));
+ self.print_struct(struct_def, generics, item.ident, item.span, true);
+ }
+ ast::ItemKind::Impl(box ast::Impl {
+ unsafety,
+ polarity,
+ defaultness,
+ constness,
+ ref generics,
+ ref of_trait,
+ ref self_ty,
+ ref items,
+ }) => {
+ self.head("");
+ self.print_visibility(&item.vis);
+ self.print_defaultness(defaultness);
+ self.print_unsafety(unsafety);
+ self.word("impl");
+
+ if generics.params.is_empty() {
+ self.nbsp();
+ } else {
+ self.print_generic_params(&generics.params);
+ self.space();
+ }
+
+ self.print_constness(constness);
+
+ if let ast::ImplPolarity::Negative(_) = polarity {
+ self.word("!");
+ }
+
+ if let Some(ref t) = *of_trait {
+ self.print_trait_ref(t);
+ self.space();
+ self.word_space("for");
+ }
+
+ self.print_type(self_ty);
+ self.print_where_clause(&generics.where_clause);
+
+ self.space();
+ self.bopen();
+ self.print_inner_attributes(&item.attrs);
+ for impl_item in items {
+ self.print_assoc_item(impl_item);
+ }
+ let empty = item.attrs.is_empty() && items.is_empty();
+ self.bclose(item.span, empty);
+ }
+ ast::ItemKind::Trait(box ast::Trait {
+ is_auto,
+ unsafety,
+ ref generics,
+ ref bounds,
+ ref items,
+ ..
+ }) => {
+ self.head("");
+ self.print_visibility(&item.vis);
+ self.print_unsafety(unsafety);
+ self.print_is_auto(is_auto);
+ self.word_nbsp("trait");
+ self.print_ident(item.ident);
+ self.print_generic_params(&generics.params);
+ let mut real_bounds = Vec::with_capacity(bounds.len());
+ for b in bounds.iter() {
+ if let GenericBound::Trait(ref ptr, ast::TraitBoundModifier::Maybe) = *b {
+ self.space();
+ self.word_space("for ?");
+ self.print_trait_ref(&ptr.trait_ref);
+ } else {
+ real_bounds.push(b.clone());
+ }
+ }
+ if !real_bounds.is_empty() {
+ self.word_nbsp(":");
+ self.print_type_bounds(&real_bounds);
+ }
+ self.print_where_clause(&generics.where_clause);
+ self.word(" ");
+ self.bopen();
+ self.print_inner_attributes(&item.attrs);
+ for trait_item in items {
+ self.print_assoc_item(trait_item);
+ }
+ let empty = item.attrs.is_empty() && items.is_empty();
+ self.bclose(item.span, empty);
+ }
+ ast::ItemKind::TraitAlias(ref generics, ref bounds) => {
+ self.head(visibility_qualified(&item.vis, "trait"));
+ self.print_ident(item.ident);
+ self.print_generic_params(&generics.params);
+ let mut real_bounds = Vec::with_capacity(bounds.len());
+ // FIXME(durka) this seems to be some quite outdated syntax
+ for b in bounds.iter() {
+ if let GenericBound::Trait(ref ptr, ast::TraitBoundModifier::Maybe) = *b {
+ self.space();
+ self.word_space("for ?");
+ self.print_trait_ref(&ptr.trait_ref);
+ } else {
+ real_bounds.push(b.clone());
+ }
+ }
+ self.nbsp();
+ if !real_bounds.is_empty() {
+ self.word_nbsp("=");
+ self.print_type_bounds(&real_bounds);
+ }
+ self.print_where_clause(&generics.where_clause);
+ self.word(";");
+ self.end(); // end inner head-block
+ self.end(); // end outer head-block
+ }
+ ast::ItemKind::MacCall(ref mac) => {
+ self.print_mac(mac);
+ if mac.args.need_semicolon() {
+ self.word(";");
+ }
+ }
+ ast::ItemKind::MacroDef(ref macro_def) => {
+ self.print_mac_def(macro_def, &item.ident, item.span, |state| {
+ state.print_visibility(&item.vis)
+ });
+ }
+ }
+ self.ann.post(self, AnnNode::Item(item))
+ }
+
+ fn print_enum_def(
+ &mut self,
+ enum_definition: &ast::EnumDef,
+ generics: &ast::Generics,
+ ident: Ident,
+ span: rustc_span::Span,
+ visibility: &ast::Visibility,
+ ) {
+ self.head(visibility_qualified(visibility, "enum"));
+ self.print_ident(ident);
+ self.print_generic_params(&generics.params);
+ self.print_where_clause(&generics.where_clause);
+ self.space();
+ self.print_variants(&enum_definition.variants, span)
+ }
+
+ fn print_variants(&mut self, variants: &[ast::Variant], span: rustc_span::Span) {
+ self.bopen();
+ for v in variants {
+ self.space_if_not_bol();
+ self.maybe_print_comment(v.span.lo());
+ self.print_outer_attributes(&v.attrs);
+ self.ibox(0);
+ self.print_variant(v);
+ self.word(",");
+ self.end();
+ self.maybe_print_trailing_comment(v.span, None);
+ }
+ let empty = variants.is_empty();
+ self.bclose(span, empty)
+ }
+
+ pub(crate) fn print_visibility(&mut self, vis: &ast::Visibility) {
+ match vis.kind {
+ ast::VisibilityKind::Public => self.word_nbsp("pub"),
+ ast::VisibilityKind::Restricted { ref path, .. } => {
+ let path = Self::to_string(|s| s.print_path(path, false, 0));
+ if path == "crate" || path == "self" || path == "super" {
+ self.word_nbsp(format!("pub({})", path))
+ } else {
+ self.word_nbsp(format!("pub(in {})", path))
+ }
+ }
+ ast::VisibilityKind::Inherited => {}
+ }
+ }
+
+ fn print_defaultness(&mut self, defaultness: ast::Defaultness) {
+ if let ast::Defaultness::Default(_) = defaultness {
+ self.word_nbsp("default");
+ }
+ }
+
+ fn print_record_struct_body(&mut self, fields: &[ast::FieldDef], span: rustc_span::Span) {
+ self.nbsp();
+ self.bopen();
+
+ let empty = fields.is_empty();
+ if !empty {
+ self.hardbreak_if_not_bol();
+
+ for field in fields {
+ self.hardbreak_if_not_bol();
+ self.maybe_print_comment(field.span.lo());
+ self.print_outer_attributes(&field.attrs);
+ self.print_visibility(&field.vis);
+ self.print_ident(field.ident.unwrap());
+ self.word_nbsp(":");
+ self.print_type(&field.ty);
+ self.word(",");
+ }
+ }
+
+ self.bclose(span, empty);
+ }
+
+ fn print_struct(
+ &mut self,
+ struct_def: &ast::VariantData,
+ generics: &ast::Generics,
+ ident: Ident,
+ span: rustc_span::Span,
+ print_finalizer: bool,
+ ) {
+ self.print_ident(ident);
+ self.print_generic_params(&generics.params);
+ match struct_def {
+ ast::VariantData::Tuple(..) | ast::VariantData::Unit(..) => {
+ if let ast::VariantData::Tuple(..) = struct_def {
+ self.popen();
+ self.commasep(Inconsistent, struct_def.fields(), |s, field| {
+ s.maybe_print_comment(field.span.lo());
+ s.print_outer_attributes(&field.attrs);
+ s.print_visibility(&field.vis);
+ s.print_type(&field.ty)
+ });
+ self.pclose();
+ }
+ self.print_where_clause(&generics.where_clause);
+ if print_finalizer {
+ self.word(";");
+ }
+ self.end();
+ self.end(); // Close the outer-box.
+ }
+ ast::VariantData::Struct(ref fields, ..) => {
+ self.print_where_clause(&generics.where_clause);
+ self.print_record_struct_body(fields, span);
+ }
+ }
+ }
+
+ pub(crate) fn print_variant(&mut self, v: &ast::Variant) {
+ self.head("");
+ self.print_visibility(&v.vis);
+ let generics = ast::Generics::default();
+ self.print_struct(&v.data, &generics, v.ident, v.span, false);
+ if let Some(ref d) = v.disr_expr {
+ self.space();
+ self.word_space("=");
+ self.print_expr(&d.value)
+ }
+ }
+
+ pub(crate) fn print_assoc_item(&mut self, item: &ast::AssocItem) {
+ let ast::Item { id, span, ident, ref attrs, ref kind, ref vis, tokens: _ } = *item;
+ self.ann.pre(self, AnnNode::SubItem(id));
+ self.hardbreak_if_not_bol();
+ self.maybe_print_comment(span.lo());
+ self.print_outer_attributes(attrs);
+ match kind {
+ ast::AssocItemKind::Fn(box ast::Fn { defaultness, sig, generics, body }) => {
+ self.print_fn_full(sig, ident, generics, vis, *defaultness, body.as_deref(), attrs);
+ }
+ ast::AssocItemKind::Const(def, ty, body) => {
+ self.print_item_const(ident, None, ty, body.as_deref(), vis, *def);
+ }
+ ast::AssocItemKind::TyAlias(box ast::TyAlias {
+ defaultness,
+ generics,
+ where_clauses,
+ where_predicates_split,
+ bounds,
+ ty,
+ }) => {
+ self.print_associated_type(
+ ident,
+ generics,
+ *where_clauses,
+ *where_predicates_split,
+ bounds,
+ ty.as_deref(),
+ vis,
+ *defaultness,
+ );
+ }
+ ast::AssocItemKind::MacCall(m) => {
+ self.print_mac(m);
+ if m.args.need_semicolon() {
+ self.word(";");
+ }
+ }
+ }
+ self.ann.post(self, AnnNode::SubItem(id))
+ }
+
+ fn print_fn_full(
+ &mut self,
+ sig: &ast::FnSig,
+ name: Ident,
+ generics: &ast::Generics,
+ vis: &ast::Visibility,
+ defaultness: ast::Defaultness,
+ body: Option<&ast::Block>,
+ attrs: &[ast::Attribute],
+ ) {
+ if body.is_some() {
+ self.head("");
+ }
+ self.print_visibility(vis);
+ self.print_defaultness(defaultness);
+ self.print_fn(&sig.decl, sig.header, Some(name), generics);
+ if let Some(body) = body {
+ self.nbsp();
+ self.print_block_with_attrs(body, attrs);
+ } else {
+ self.word(";");
+ }
+ }
+
+ pub(crate) fn print_fn(
+ &mut self,
+ decl: &ast::FnDecl,
+ header: ast::FnHeader,
+ name: Option<Ident>,
+ generics: &ast::Generics,
+ ) {
+ self.print_fn_header_info(header);
+ if let Some(name) = name {
+ self.nbsp();
+ self.print_ident(name);
+ }
+ self.print_generic_params(&generics.params);
+ self.print_fn_params_and_ret(decl, false);
+ self.print_where_clause(&generics.where_clause)
+ }
+
+ pub(crate) fn print_fn_params_and_ret(&mut self, decl: &ast::FnDecl, is_closure: bool) {
+ let (open, close) = if is_closure { ("|", "|") } else { ("(", ")") };
+ self.word(open);
+ self.commasep(Inconsistent, &decl.inputs, |s, param| s.print_param(param, is_closure));
+ self.word(close);
+ self.print_fn_ret_ty(&decl.output)
+ }
+
+ fn print_where_clause(&mut self, where_clause: &ast::WhereClause) {
+ self.print_where_clause_parts(where_clause.has_where_token, &where_clause.predicates);
+ }
+
+ pub(crate) fn print_where_clause_parts(
+ &mut self,
+ has_where_token: bool,
+ predicates: &[ast::WherePredicate],
+ ) {
+ if predicates.is_empty() && !has_where_token {
+ return;
+ }
+
+ self.space();
+ self.word_space("where");
+
+ for (i, predicate) in predicates.iter().enumerate() {
+ if i != 0 {
+ self.word_space(",");
+ }
+
+ self.print_where_predicate(predicate);
+ }
+ }
+
+ pub fn print_where_predicate(&mut self, predicate: &ast::WherePredicate) {
+ match predicate {
+ ast::WherePredicate::BoundPredicate(ast::WhereBoundPredicate {
+ bound_generic_params,
+ bounded_ty,
+ bounds,
+ ..
+ }) => {
+ self.print_formal_generic_params(bound_generic_params);
+ self.print_type(bounded_ty);
+ self.word(":");
+ if !bounds.is_empty() {
+ self.nbsp();
+ self.print_type_bounds(bounds);
+ }
+ }
+ ast::WherePredicate::RegionPredicate(ast::WhereRegionPredicate {
+ lifetime,
+ bounds,
+ ..
+ }) => {
+ self.print_lifetime(*lifetime);
+ self.word(":");
+ if !bounds.is_empty() {
+ self.nbsp();
+ self.print_lifetime_bounds(bounds);
+ }
+ }
+ ast::WherePredicate::EqPredicate(ast::WhereEqPredicate { lhs_ty, rhs_ty, .. }) => {
+ self.print_type(lhs_ty);
+ self.space();
+ self.word_space("=");
+ self.print_type(rhs_ty);
+ }
+ }
+ }
+
+ fn print_use_tree(&mut self, tree: &ast::UseTree) {
+ match tree.kind {
+ ast::UseTreeKind::Simple(rename, ..) => {
+ self.print_path(&tree.prefix, false, 0);
+ if let Some(rename) = rename {
+ self.nbsp();
+ self.word_nbsp("as");
+ self.print_ident(rename);
+ }
+ }
+ ast::UseTreeKind::Glob => {
+ if !tree.prefix.segments.is_empty() {
+ self.print_path(&tree.prefix, false, 0);
+ self.word("::");
+ }
+ self.word("*");
+ }
+ ast::UseTreeKind::Nested(ref items) => {
+ if !tree.prefix.segments.is_empty() {
+ self.print_path(&tree.prefix, false, 0);
+ self.word("::");
+ }
+ if items.is_empty() {
+ self.word("{}");
+ } else if items.len() == 1 {
+ self.print_use_tree(&items[0].0);
+ } else {
+ self.cbox(INDENT_UNIT);
+ self.word("{");
+ self.zerobreak();
+ self.ibox(0);
+ for use_tree in items.iter().delimited() {
+ self.print_use_tree(&use_tree.0);
+ if !use_tree.is_last {
+ self.word(",");
+ if let ast::UseTreeKind::Nested(_) = use_tree.0.kind {
+ self.hardbreak();
+ } else {
+ self.space();
+ }
+ }
+ }
+ self.end();
+ self.trailing_comma();
+ self.offset(-INDENT_UNIT);
+ self.word("}");
+ self.end();
+ }
+ }
+ }
+ }
+}
diff --git a/compiler/rustc_ast_pretty/src/pprust/tests.rs b/compiler/rustc_ast_pretty/src/pprust/tests.rs
new file mode 100644
index 000000000..6c8d42f33
--- /dev/null
+++ b/compiler/rustc_ast_pretty/src/pprust/tests.rs
@@ -0,0 +1,63 @@
+use super::*;
+
+use rustc_ast as ast;
+use rustc_span::create_default_session_globals_then;
+use rustc_span::symbol::Ident;
+
+fn fun_to_string(
+ decl: &ast::FnDecl,
+ header: ast::FnHeader,
+ name: Ident,
+ generics: &ast::Generics,
+) -> String {
+ to_string(|s| {
+ s.head("");
+ s.print_fn(decl, header, Some(name), generics);
+ s.end(); // Close the head box.
+ s.end(); // Close the outer box.
+ })
+}
+
+fn variant_to_string(var: &ast::Variant) -> String {
+ to_string(|s| s.print_variant(var))
+}
+
+#[test]
+fn test_fun_to_string() {
+ create_default_session_globals_then(|| {
+ let abba_ident = Ident::from_str("abba");
+
+ let decl =
+ ast::FnDecl { inputs: Vec::new(), output: ast::FnRetTy::Default(rustc_span::DUMMY_SP) };
+ let generics = ast::Generics::default();
+ assert_eq!(
+ fun_to_string(&decl, ast::FnHeader::default(), abba_ident, &generics),
+ "fn abba()"
+ );
+ })
+}
+
+#[test]
+fn test_variant_to_string() {
+ create_default_session_globals_then(|| {
+ let ident = Ident::from_str("principal_skinner");
+
+ let var = ast::Variant {
+ ident,
+ vis: ast::Visibility {
+ span: rustc_span::DUMMY_SP,
+ kind: ast::VisibilityKind::Inherited,
+ tokens: None,
+ },
+ attrs: ast::AttrVec::new(),
+ id: ast::DUMMY_NODE_ID,
+ data: ast::VariantData::Unit(ast::DUMMY_NODE_ID),
+ disr_expr: None,
+ span: rustc_span::DUMMY_SP,
+ is_placeholder: false,
+ };
+
+ let varstr = variant_to_string(&var);
+ assert_eq!(varstr, "principal_skinner");
+ })
+}