diff options
Diffstat (limited to '')
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, ¶m.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(¶m.pat); + visitor.visit_ty(¶m.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(®) { + 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, ¶m.attrs); + hir::Param { + hir_id, + pat: self.lower_pat(¶m.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(¶meter.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, + ¶m.kind, + ¶m.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(<)), + 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(®ion); + 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(¶m.ty, ImplTraitContext::Universal) + } else { + self.lower_ty_direct( + ¶m.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, ¶m.attrs); + hir::GenericParam { + hir_id, + name, + span: self.lower_span(param.span()), + pure_wrt_drop: self.tcx.sess.contains_name(¶m.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) = (¶m.kind, ¶m.bounds, ident.span); + let (ord_kind, ident) = match ¶m.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 ¶m.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(), + ¯o_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(¶m.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(¶m.bounds) + } + } + ast::GenericParamKind::Type { ref default } => { + s.print_ident(param.ident); + if !param.bounds.is_empty() { + s.word_nbsp(":"); + s.print_type_bounds(¶m.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(¶m.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"); + }) +} |