diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs index b105a97cdf98d..97bf845c3c178 100644 --- a/compiler/rustc_ast/src/ast.rs +++ b/compiler/rustc_ast/src/ast.rs @@ -2480,6 +2480,23 @@ impl Ty { final_ty } + pub fn extract_view(&self) -> Option<(Self, &[Ident])> { + match &self.kind { + TyKind::View(inner, fields) => Some((*inner.clone(), fields)), + + TyKind::Ref(lifetime, MutTy { ty, mutbl }) => { + let (inner, fields) = ty.extract_view()?; + let kind = + TyKind::Ref(*lifetime, MutTy { ty: Box::new(inner.clone()), mutbl: *mutbl }); + let ty = Ty { id: self.id, kind, span: self.span, tokens: self.tokens.clone() }; + + Some((ty, fields)) + } + + _ => None, + } + } + pub fn is_maybe_parenthesised_infer(&self) -> bool { match &self.kind { TyKind::Infer => true, @@ -2565,6 +2582,8 @@ pub enum TyKind { /// Usually not written directly in user code but indirectly via the macro /// `core::field::field_of!(...)`. FieldOf(Box, Option, Ident), + /// A view of a type. `T.{ field_1, field_2 }`. + View(Box, #[visitable(ignore)] ThinVec), /// Sometimes we need a dummy value when no error has occurred. Dummy, /// Placeholder for a kind that has failed to be defined. @@ -2660,6 +2679,20 @@ pub enum TraitObjectSyntax { None = 1, } +/// Represents how a reference is viewed. +// FIXME(scrabsha): move this to a new variant of `Mutability` when viewing of non-reference types +// is implemented. +#[derive(Clone, Encodable, Decodable, Debug, Walkable)] +pub enum ViewKind { + /// `&mut T`. All the fields can be observed. + Full, + /// `&mut T.{ foo, bar }`. Only `foo` and `bar` can be observed. + Partial { + #[visitable(ignore)] + fields: ThinVec, + }, +} + /// SAFETY: `TraitObjectSyntax` only has 3 data-less variants which means /// it can be represented with a `u2`. We use `repr(u8)` to guarantee the /// discriminants of the variants are no greater than `3`. @@ -2929,13 +2962,19 @@ pub struct Param { /// 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 struct SelfParam { + pub kind: SelfKind, + pub view: ViewKind, +} + #[derive(Clone, Encodable, Decodable, Debug)] pub enum SelfKind { /// `self`, `mut self` Value(Mutability), - /// `&'lt self`, `&'lt mut self` + /// `&'lt self`, `&'lt mut self`, `&'lt mut self.{ a, b }` Region(Option, Mutability), - /// `&'lt pin const self`, `&'lt pin mut self` + /// `&'lt pin const self`, `&'lt pin mut self`, `&'lt pin mut self.{ a, b }`. Pinned(Option, Mutability), /// `self: TYPE`, `mut self: TYPE` Explicit(Box, Mutability), @@ -2955,28 +2994,35 @@ impl SelfKind { } } -pub type ExplicitSelf = Spanned; +pub type ExplicitSelf = Spanned; impl Param { /// Attempts to cast parameter to `ExplicitSelf`. pub fn to_self(&self) -> Option { if let PatKind::Ident(BindingMode(ByRef::No, 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))), + let (ty, view) = match self.ty.extract_view() { + Some((this, fields)) => { + (this, ViewKind::Partial { fields: ThinVec::from(fields) }) + } + None => (*self.ty.clone(), ViewKind::Full), + }; + + let (kind, span) = match ty.kind { + TyKind::ImplicitSelf => (SelfKind::Value(mutbl), self.pat.span), TyKind::Ref(lt, MutTy { ref ty, mutbl }) if ty.kind.is_implicit_self() => { - Some(respan(self.pat.span, SelfKind::Region(lt, mutbl))) + (SelfKind::Region(lt, mutbl), self.pat.span) } TyKind::PinnedRef(lt, MutTy { ref ty, mutbl }) if ty.kind.is_implicit_self() => { - Some(respan(self.pat.span, SelfKind::Pinned(lt, mutbl))) + (SelfKind::Pinned(lt, mutbl), self.pat.span) } - _ => Some(respan( - self.pat.span.to(self.ty.span), - SelfKind::Explicit(self.ty.clone(), mutbl), - )), + _ => (SelfKind::Explicit(Box::new(ty), mutbl), self.ty.span), }; + + let param = respan(span, SelfParam { view, kind }); + return Some(param); } } None @@ -3000,7 +3046,7 @@ impl Param { span: eself_ident.span, tokens: None, }); - let (mutbl, ty) = match eself.node { + let (mutbl, mut ty) = match eself.node.kind { SelfKind::Explicit(ty, mutbl) => (mutbl, ty), SelfKind::Value(mutbl) => (mutbl, infer_ty), SelfKind::Region(lt, mutbl) => ( @@ -3022,6 +3068,14 @@ impl Param { }), ), }; + if let ViewKind::Partial { fields } = eself.node.view { + ty = Box::new(Ty { + id: DUMMY_NODE_ID, + kind: TyKind::View(ty, fields), + span, + tokens: None, + }); + } Param { attrs, pat: Box::new(Pat { diff --git a/compiler/rustc_ast/src/util/classify.rs b/compiler/rustc_ast/src/util/classify.rs index 43ef6897b79cf..dbb57bd9358bf 100644 --- a/compiler/rustc_ast/src/util/classify.rs +++ b/compiler/rustc_ast/src/util/classify.rs @@ -167,6 +167,8 @@ pub enum TrailingBrace<'a> { /// Trailing brace in any other expression, such as `a + B {}`. We will /// suggest wrapping the innermost expression in parentheses: `a + (B {})`. Expr(&'a ast::Expr), + /// Trailing brace in a type, like the one in `&Foo.{ bar }` + Type(&'a ast::Ty), } /// If an expression ends with `}`, returns the innermost expression ending in the `}` @@ -205,7 +207,7 @@ pub fn expr_trailing_brace(mut expr: &ast::Expr) -> Option> { | ConstBlock(_) => break Some(TrailingBrace::Expr(expr)), Cast(_, ty) => { - break type_trailing_braced_mac_call(ty).map(TrailingBrace::MacCall); + break type_trailing_brace(ty); } MacCall(mac) => { @@ -246,13 +248,17 @@ pub fn expr_trailing_brace(mut expr: &ast::Expr) -> Option> { } } -/// If the type's last token is `}`, it must be due to a braced macro call, such -/// as in `*const brace! { ... }`. Returns that trailing macro call. -fn type_trailing_braced_mac_call(mut ty: &ast::Ty) -> Option<&ast::MacCall> { +/// If a type ends with `}`, return the innermost node ending with the `}`. +/// +/// This may be caused by a macro call (`*const brace! { ... }`) or a view type +/// (`&Foo.{ bar }`). +fn type_trailing_brace(mut ty: &ast::Ty) -> Option> { loop { match &ty.kind { ast::TyKind::MacCall(mac) => { - break (mac.args.delim == Delimiter::Brace).then_some(mac); + break (mac.args.delim == Delimiter::Brace) + .then_some(mac.as_ref()) + .map(TrailingBrace::MacCall); } ast::TyKind::Ptr(mut_ty) @@ -261,6 +267,8 @@ fn type_trailing_braced_mac_call(mut ty: &ast::Ty) -> Option<&ast::MacCall> { ty = &mut_ty.ty; } + ast::TyKind::View(..) => break Some(TrailingBrace::Type(ty)), + ast::TyKind::UnsafeBinder(binder) => { ty = &binder.inner_ty; } diff --git a/compiler/rustc_ast/src/visit.rs b/compiler/rustc_ast/src/visit.rs index ee4b1d1354300..75f8aa76d1921 100644 --- a/compiler/rustc_ast/src/visit.rs +++ b/compiler/rustc_ast/src/visit.rs @@ -486,6 +486,7 @@ macro_rules! common_visitor_and_walkers { UnsafeBinderTy, UnsafeSource, UseTreeKind, + ViewKind, VisibilityKind, WhereBoundPredicate, WhereClause, diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs index 75dab290d6031..942d0127069fc 100644 --- a/compiler/rustc_ast_lowering/src/lib.rs +++ b/compiler/rustc_ast_lowering/src/lib.rs @@ -1594,6 +1594,10 @@ impl<'hir> LoweringContext<'_, 'hir> { ); hir::TyKind::Err(guar) } + TyKind::View(ty, _) => { + // FIXME(scrabsha): lower view types to HIR. + return self.lower_ty(ty, itctx); + } TyKind::Dummy => panic!("`TyKind::Dummy` should never be lowered"), }; diff --git a/compiler/rustc_ast_pretty/src/pprust/state.rs b/compiler/rustc_ast_pretty/src/pprust/state.rs index f46ce8fd76865..a76bbfa04b64f 100644 --- a/compiler/rustc_ast_pretty/src/pprust/state.rs +++ b/compiler/rustc_ast_pretty/src/pprust/state.rs @@ -17,7 +17,7 @@ use rustc_ast::util::comments::{Comment, CommentStyle}; use rustc_ast::{ self as ast, AttrArgs, BindingMode, BlockCheckMode, ByRef, DelimArgs, GenericArg, GenericBound, InlineAsmOperand, InlineAsmOptions, InlineAsmRegOrRegClass, InlineAsmTemplatePiece, PatKind, - RangeEnd, RangeSyntax, Safety, SelfKind, Term, attr, + RangeEnd, RangeSyntax, Safety, SelfKind, Term, ViewKind, attr, }; use rustc_span::edition::Edition; use rustc_span::source_map::SourceMap; @@ -1255,6 +1255,14 @@ impl<'a> State<'a> { } } + fn print_view(&mut self, fields: &[Ident]) { + self.word_space(".{"); + self.commasep(Breaks::Inconsistent, fields, |s, field| { + s.print_ident(*field); + }); + self.word_space(" }"); + } + pub fn print_assoc_item_constraint(&mut self, constraint: &ast::AssocItemConstraint) { self.print_ident(constraint.ident); if let Some(args) = constraint.gen_args.as_ref() { @@ -1437,6 +1445,10 @@ impl<'a> State<'a> { self.end(ib); self.pclose(); } + ast::TyKind::View(ty, fields) => { + self.print_type(ty); + self.print_view(fields); + } } self.end(ib); } @@ -2053,31 +2065,35 @@ impl<'a> State<'a> { } fn print_explicit_self(&mut self, explicit_self: &ast::ExplicitSelf) { - match &explicit_self.node { + match &explicit_self.node.kind { SelfKind::Value(m) => { self.print_mutability(*m, false); - self.word("self") + self.word("self"); } SelfKind::Region(lt, m) => { self.word("&"); self.print_opt_lifetime(lt); self.print_mutability(*m, false); - self.word("self") + self.word("self"); } SelfKind::Pinned(lt, m) => { self.word("&"); self.print_opt_lifetime(lt); self.word("pin "); self.print_mutability(*m, true); - self.word("self") + self.word("self"); } SelfKind::Explicit(typ, m) => { self.print_mutability(*m, false); self.word("self"); self.word_space(":"); - self.print_type(typ) + self.print_type(typ); } } + + if let ViewKind::Partial { fields } = &explicit_self.node.view { + self.print_view(fields); + } } fn print_coroutine_kind(&mut self, coroutine_kind: ast::CoroutineKind) { diff --git a/compiler/rustc_ast_pretty/src/pprust/tests.rs b/compiler/rustc_ast_pretty/src/pprust/tests.rs index 786de529c5b89..4ef42ae8e4c3b 100644 --- a/compiler/rustc_ast_pretty/src/pprust/tests.rs +++ b/compiler/rustc_ast_pretty/src/pprust/tests.rs @@ -1,6 +1,6 @@ use rustc_ast as ast; use rustc_span::{DUMMY_SP, Ident, create_default_session_globals_then}; -use thin_vec::ThinVec; +use thin_vec::{ThinVec, thin_vec}; use super::*; @@ -22,6 +22,12 @@ fn variant_to_string(var: &ast::Variant) -> String { to_string(|s| s.print_variant(var)) } +fn ty_to_string(ty: &ast::Ty) -> String { + to_string(|s| { + s.print_type(ty); + }) +} + #[test] fn test_fun_to_string() { create_default_session_globals_then(|| { @@ -60,3 +66,26 @@ fn test_variant_to_string() { assert_eq!(varstr, "principal_skinner"); }) } + +#[test] +fn test_field_view() { + create_default_session_globals_then(|| { + let ty = ast::Ty { + id: ast::DUMMY_NODE_ID, + kind: ast::TyKind::View( + Box::new(ast::Ty { + id: ast::DUMMY_NODE_ID, + kind: ast::TyKind::Dummy, + span: DUMMY_SP, + tokens: None, + }), + thin_vec![Ident::from_str("milhouse"), Ident::from_str("apu")], + ), + span: DUMMY_SP, + tokens: None, + }; + + let ty_str = ty_to_string(&ty); + assert_eq!(ty_str, "(/*DUMMY*/).{ milhouse, apu }"); + }); +} diff --git a/compiler/rustc_builtin_macros/src/deriving/generic/ty.rs b/compiler/rustc_builtin_macros/src/deriving/generic/ty.rs index e7972c5436e13..7e7ad92070787 100644 --- a/compiler/rustc_builtin_macros/src/deriving/generic/ty.rs +++ b/compiler/rustc_builtin_macros/src/deriving/generic/ty.rs @@ -2,7 +2,9 @@ //! when specifying impls to be derived. pub(crate) use Ty::*; -use rustc_ast::{self as ast, Expr, GenericArg, GenericParamKind, Generics, SelfKind, TyKind}; +use rustc_ast::{ + self as ast, Expr, GenericArg, GenericParamKind, Generics, SelfKind, TyKind, ViewKind, +}; use rustc_expand::base::ExtCtxt; use rustc_span::{DUMMY_SP, Ident, Span, Symbol, kw, respan}; use thin_vec::ThinVec; @@ -200,6 +202,8 @@ impl Bounds { pub(crate) fn get_explicit_self(cx: &ExtCtxt<'_>, span: Span) -> (Box, ast::ExplicitSelf) { // This constructs a fresh `self` path. let self_path = cx.expr_self(span); - let self_ty = respan(span, SelfKind::Region(None, ast::Mutability::Not)); + let kind = SelfKind::Region(None, ast::Mutability::Not); + let view = ViewKind::Full; + let self_ty = respan(span, ast::SelfParam { kind, view }); (self_path, self_ty) } diff --git a/compiler/rustc_parse/src/errors.rs b/compiler/rustc_parse/src/errors.rs index 4043b9bca61c5..cc2172b58f2f8 100644 --- a/compiler/rustc_parse/src/errors.rs +++ b/compiler/rustc_parse/src/errors.rs @@ -1058,11 +1058,9 @@ pub(crate) struct LabeledLoopInBreak { #[derive(Subdiagnostic)] pub(crate) enum WrapInParentheses { - #[multipart_suggestion( - "wrap the expression in parentheses", - applicability = "machine-applicable" - )] - Expression { + #[multipart_suggestion("wrap the {$kind} in parentheses", applicability = "machine-applicable")] + NonMacro { + kind: WrapInParenthesesNodeKind, #[suggestion_part(code = "(")] left: Span, #[suggestion_part(code = ")")] @@ -1080,6 +1078,22 @@ pub(crate) enum WrapInParentheses { }, } +pub(crate) enum WrapInParenthesesNodeKind { + Expression, + Type, +} + +impl IntoDiagArg for WrapInParenthesesNodeKind { + fn into_diag_arg(self, _path: &mut Option) -> DiagArgValue { + let str = match self { + WrapInParenthesesNodeKind::Expression => "expression", + WrapInParenthesesNodeKind::Type => "type", + }; + + DiagArgValue::Str(Cow::Borrowed(str)) + } +} + #[derive(Diagnostic)] #[diag("this is a block expression, not an array")] pub(crate) struct ArrayBracketsInsteadOfBraces { diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs index 35c271cb70204..20b7be8db3ca4 100644 --- a/compiler/rustc_parse/src/parser/expr.rs +++ b/compiler/rustc_parse/src/parser/expr.rs @@ -1900,7 +1900,8 @@ impl<'a> Parser<'a> { let lexpr = self.parse_expr_labeled(label, true)?; self.dcx().emit_err(errors::LabeledLoopInBreak { span: lexpr.span, - sub: errors::WrapInParentheses::Expression { + sub: errors::WrapInParentheses::NonMacro { + kind: errors::WrapInParenthesesNodeKind::Expression, left: lexpr.span.shrink_to_lo(), right: lexpr.span.shrink_to_hi(), }, diff --git a/compiler/rustc_parse/src/parser/item.rs b/compiler/rustc_parse/src/parser/item.rs index bd45bbb6a8582..e91205321a57a 100644 --- a/compiler/rustc_parse/src/parser/item.rs +++ b/compiler/rustc_parse/src/parser/item.rs @@ -3478,11 +3478,18 @@ impl<'a> Parser<'a> { let parse_self_possibly_typed = |this: &mut Self, m| { let eself_ident = expect_self_ident(this); let eself_hi = this.prev_token.span; - let eself = if this.eat(exp!(Colon)) { - SelfKind::Explicit(this.parse_ty()?, m) + let view = this.maybe_parse_view()?; + + // FIXME: we could recover for `self.{}: Ty` here. + let (kind, view) = if let Some((fields, _)) = view { + (SelfKind::Value(m), ViewKind::Partial { fields }) + } else if this.eat(exp!(Colon)) { + (SelfKind::Explicit(this.parse_ty()?, m), ViewKind::Full) } else { - SelfKind::Value(m) + (SelfKind::Value(m), ViewKind::Full) }; + + let eself = SelfParam { kind, view }; Ok((eself, eself_ident, eself_hi)) }; let expect_self_ident_not_typed = @@ -3514,15 +3521,15 @@ impl<'a> Parser<'a> { // Recover for the grammar `*self`, `*const self`, and `*mut self`. let recover_self_ptr = |this: &mut Self| { this.dcx().emit_err(errors::SelfArgumentPointer { span: this.token.span }); - - Ok((SelfKind::Value(Mutability::Not), expect_self_ident(this), this.prev_token.span)) + let param = SelfParam { kind: SelfKind::Value(Mutability::Not), view: ViewKind::Full }; + Ok((param, expect_self_ident(this), this.prev_token.span)) }; // Parse optional `self` parameter of a method. // Only a limited set of initial token sequences is considered `self` parameters; anything // else is parsed as a normal function parameter list, so some lookahead is required. let eself_lo = self.token.span; - let (eself, eself_ident, eself_hi) = match self.token.uninterpolate().kind { + let (eself_kind, eself_ident, eself_hi) = match self.token.uninterpolate().kind { token::And => { let has_lifetime = is_lifetime(self, 1); let skip_lifetime_count = has_lifetime as usize; @@ -3559,6 +3566,12 @@ impl<'a> Parser<'a> { }; let hi = self.token.span; let self_ident = expect_self_ident_not_typed(self, &eself, eself_lo.until(hi)); + let view = match self.maybe_parse_view()? { + Some((fields, _)) => ViewKind::Partial { fields }, + None => ViewKind::Full, + }; + let eself = SelfParam { kind: eself, view }; + (eself, self_ident, hi) } // `*self` @@ -3586,7 +3599,7 @@ impl<'a> Parser<'a> { _ => return Ok(None), }; - let eself = respan(eself_lo.to(eself_hi), eself); + let eself = respan(eself_lo.to(eself_hi), eself_kind); Ok(Some(Param::from_self(AttrVec::default(), eself, eself_ident))) } diff --git a/compiler/rustc_parse/src/parser/stmt.rs b/compiler/rustc_parse/src/parser/stmt.rs index 5bd2ca3139228..afc7652dd847c 100644 --- a/compiler/rustc_parse/src/parser/stmt.rs +++ b/compiler/rustc_parse/src/parser/stmt.rs @@ -429,7 +429,8 @@ impl<'a> Parser<'a> { self.dcx().emit_err(errors::InvalidExpressionInLetElse { span: init.span, operator: op.node.as_str(), - sugg: errors::WrapInParentheses::Expression { + sugg: errors::WrapInParentheses::NonMacro { + kind: errors::WrapInParenthesesNodeKind::Expression, left: init.span.shrink_to_lo(), right: init.span.shrink_to_hi(), }, @@ -450,11 +451,20 @@ impl<'a> Parser<'a> { ), TrailingBrace::Expr(expr) => ( expr.span, - errors::WrapInParentheses::Expression { + errors::WrapInParentheses::NonMacro { + kind: errors::WrapInParenthesesNodeKind::Expression, left: expr.span.shrink_to_lo(), right: expr.span.shrink_to_hi(), }, ), + TrailingBrace::Type(ty) => ( + ty.span, + errors::WrapInParentheses::NonMacro { + kind: errors::WrapInParenthesesNodeKind::Type, + left: ty.span.shrink_to_lo(), + right: ty.span.shrink_to_hi(), + }, + ), }; self.dcx().emit_err(errors::InvalidCurlyInLetElse { span: span.with_lo(span.hi() - BytePos(1)), diff --git a/compiler/rustc_parse/src/parser/ty.rs b/compiler/rustc_parse/src/parser/ty.rs index b5151cf20ab02..bcf605db0300d 100644 --- a/compiler/rustc_parse/src/parser/ty.rs +++ b/compiler/rustc_parse/src/parser/ty.rs @@ -441,6 +441,8 @@ impl<'a> Parser<'a> { let span = lo.to(self.prev_token.span); let mut ty = self.mk_ty(span, kind); + ty = self.parse_trailing_views(ty)?; + // Try to recover from use of `+` with incorrect priority. match allow_plus { AllowPlus::Yes => self.maybe_recover_from_bad_type_plus(&ty)?, @@ -768,31 +770,47 @@ impl<'a> Parser<'a> { self.bump_with((dyn_tok, dyn_tok_sp)); } let ty = self.parse_ty_no_plus()?; - if self.token == TokenKind::Dot && self.look_ahead(1, |t| t.kind == TokenKind::OpenBrace) { - // & [mut] . { } - // ^ - // we are here - let view_start_span = self.token.span; - self.bump(); - let fields = self - .parse_delim_comma_seq( - ExpTokenPair { tok: TokenKind::OpenBrace, token_type: TokenType::OpenBrace }, - ExpTokenPair { tok: TokenKind::CloseBrace, token_type: TokenType::CloseBrace }, - |p| p.parse_ident(), - )? - .0; - // FIXME(scrabsha): actually propagate field view in the AST. - let _ = fields; - let view_end_span = self.prev_token.span; - let span = view_start_span.to(view_end_span); - self.psess.gated_spans.gate(sym::view_types, span); - } Ok(match pinned { Pinnedness::Not => TyKind::Ref(opt_lifetime, MutTy { ty, mutbl }), Pinnedness::Pinned => TyKind::PinnedRef(opt_lifetime, MutTy { ty, mutbl }), }) } + fn parse_trailing_views(&mut self, mut ty: Box) -> PResult<'a, Box> { + while let Some((fields, span)) = self.maybe_parse_view()? { + let span = ty.span.to(span); + let kind = TyKind::View(ty, fields); + ty = self.mk_ty(span, kind); + } + Ok(ty) + } + + pub(crate) fn maybe_parse_view(&mut self) -> PResult<'a, Option<(ThinVec, Span)>> { + if self.token == TokenKind::Dot && self.look_ahead(1, |t| t.kind == TokenKind::OpenBrace) { + // . { } + // ^ + // we are here + let view_start = self.token.span; + self.bump(); + let fields = match self.parse_delim_comma_seq( + ExpTokenPair { tok: TokenKind::OpenBrace, token_type: TokenType::OpenBrace }, + ExpTokenPair { tok: TokenKind::CloseBrace, token_type: TokenType::CloseBrace }, + |p| p.parse_field_name(), + ) { + Ok((fields, _)) => fields, + Err(diag) => { + return Err(diag); + } + }; + let view_end = self.prev_token.span; + let view_span = view_start.to(view_end); + self.psess.gated_spans.gate(sym::view_types, view_span); + Ok(Some((fields, view_span))) + } else { + Ok(None) + } + } + /// Parse nothing, mutability or `pin` followed by "explicit" mutability. /// /// ```ebnf diff --git a/compiler/rustc_passes/src/input_stats.rs b/compiler/rustc_passes/src/input_stats.rs index e424cc09fb607..20537b9115ff2 100644 --- a/compiler/rustc_passes/src/input_stats.rs +++ b/compiler/rustc_passes/src/input_stats.rs @@ -690,6 +690,7 @@ impl<'v> ast_visit::Visitor<'v> for StatCollector<'v> { CVarArgs, Dummy, FieldOf, + View, Err ] ); diff --git a/src/tools/clippy/clippy_lints/src/redundant_static_lifetimes.rs b/src/tools/clippy/clippy_lints/src/redundant_static_lifetimes.rs index 35cfc37b133b2..fb5858c004423 100644 --- a/src/tools/clippy/clippy_lints/src/redundant_static_lifetimes.rs +++ b/src/tools/clippy/clippy_lints/src/redundant_static_lifetimes.rs @@ -62,7 +62,7 @@ impl RedundantStaticLifetimes { } }, // This is what we are looking for ! - TyKind::Ref(ref optional_lifetime, ref borrow_type) => { + TyKind::Ref(ref optional_lifetime, ref borrow_type, ) => { // Match the 'static lifetime if let Some(lifetime) = *optional_lifetime { match borrow_type.ty.kind { diff --git a/src/tools/clippy/clippy_utils/src/check_proc_macro.rs b/src/tools/clippy/clippy_utils/src/check_proc_macro.rs index 55c0372259c0f..4a54b0ecd9b96 100644 --- a/src/tools/clippy/clippy_utils/src/check_proc_macro.rs +++ b/src/tools/clippy/clippy_utils/src/check_proc_macro.rs @@ -434,7 +434,7 @@ fn ast_ty_search_pat(ty: &ast::Ty) -> (Pat, Pat) { match &ty.kind { TyKind::Slice(..) | TyKind::Array(..) => (Pat::Str("["), Pat::Str("]")), TyKind::Ptr(MutTy { ty, .. }) => (Pat::Str("*"), ast_ty_search_pat(ty).1), - TyKind::Ref(_, MutTy { ty, .. }) | TyKind::PinnedRef(_, MutTy { ty, .. }) => { + TyKind::Ref(_, MutTy { ty, .. }, ) | TyKind::PinnedRef(_, MutTy { ty, .. }, ) => { (Pat::Str("&"), ast_ty_search_pat(ty).1) }, TyKind::FnPtr(fn_ptr) => ( @@ -536,6 +536,7 @@ fn ast_ty_search_pat(ty: &ast::Ty) -> (Pat, Pat) { // experimental | TyKind::Pat(..) | TyKind::FieldOf(..) + | TyKind::View(..) // unused | TyKind::CVarArgs diff --git a/src/tools/rustfmt/src/items.rs b/src/tools/rustfmt/src/items.rs index 32f71703e019f..b06b0f2238857 100644 --- a/src/tools/rustfmt/src/items.rs +++ b/src/tools/rustfmt/src/items.rs @@ -31,7 +31,7 @@ use crate::shape::{Indent, Shape}; use crate::source_map::{LineRangeUtils, SpanUtils}; use crate::spanned::Spanned; use crate::stmt::Stmt; -use crate::types::opaque_ty; +use crate::types::{opaque_ty, rewrite_view}; use crate::utils::*; use crate::vertical::rewrite_with_alignment; use crate::visitor::FmtVisitor; @@ -2392,7 +2392,7 @@ fn rewrite_explicit_self( shape: Shape, has_multiple_attr_lines: bool, ) -> RewriteResult { - let self_str = match explicit_self.node { + let self_str = match explicit_self.node.kind { ast::SelfKind::Region(lt, m) => { let mut_str = format_mutability(m); let lifetime_str = rewrite_opt_lifetime(context, lt)?; @@ -2412,10 +2412,12 @@ fn rewrite_explicit_self( } ast::SelfKind::Value(mutability) => format!("{}self", format_mutability(mutability)), }; + let view = rewrite_view(&explicit_self.node.view); + let explicit_self_str = format!("{self_str}{view}"); Ok(combine_strs_with_missing_comments( context, param_attrs, - &self_str, + &explicit_self_str, span, shape, !has_multiple_attr_lines, diff --git a/src/tools/rustfmt/src/types.rs b/src/tools/rustfmt/src/types.rs index e574a9d278265..baae2bf062172 100644 --- a/src/tools/rustfmt/src/types.rs +++ b/src/tools/rustfmt/src/types.rs @@ -1,6 +1,7 @@ use std::ops::Deref; use rustc_ast::ast::{self, FnRetTy, Mutability, Term}; +use rustc_span::Ident; use rustc_span::{BytePos, Pos, Span, symbol::kw}; use tracing::debug; @@ -1064,6 +1065,12 @@ impl Rewrite for ast::Ty { result.push_str(&rewrite); Ok(result) } + ast::TyKind::View(ref inner, ref fields) => { + let mut result = String::new(); + result.push_str(&inner.rewrite_result(context, shape)?); + result.push_str(&rewrite_view_fields(fields)); + Ok(result) + } } } } @@ -1366,3 +1373,25 @@ pub(crate) fn rewrite_bound_params( Some(result) } } + +pub(crate) fn rewrite_view(v: &ast::ViewKind) -> String { + // FIXME: how should views be formatted? + // FIXME: handle comments. + match v { + ast::ViewKind::Full => String::new(), + ast::ViewKind::Partial { fields } => rewrite_view_fields(fields), + } +} + +fn rewrite_view_fields(fields: &[Ident]) -> String { + if fields.is_empty() { + String::from(".{}") + } else { + let fields = fields + .iter() + .map(|field| field.name.as_str()) + .collect::>() + .join(", "); + format!(".{{ {fields} }}") + } +} diff --git a/src/tools/rustfmt/tests/source/view-types.rs b/src/tools/rustfmt/tests/source/view-types.rs new file mode 100644 index 0000000000000..161b84ab3082d --- /dev/null +++ b/src/tools/rustfmt/tests/source/view-types.rs @@ -0,0 +1,13 @@ +// Check that `rustfmt` does not remove view type syntax. + +struct Foo { + bar: u8, +} + +impl Foo { + fn a(&mut self.{ bar }) {} + fn c(&mut self.{}) {} + + fn d(a: &mut Self.{ bar }) {} + fn d(a: &mut Self.{}) {} +} diff --git a/src/tools/rustfmt/tests/target/view-types.rs b/src/tools/rustfmt/tests/target/view-types.rs new file mode 100644 index 0000000000000..161b84ab3082d --- /dev/null +++ b/src/tools/rustfmt/tests/target/view-types.rs @@ -0,0 +1,13 @@ +// Check that `rustfmt` does not remove view type syntax. + +struct Foo { + bar: u8, +} + +impl Foo { + fn a(&mut self.{ bar }) {} + fn c(&mut self.{}) {} + + fn d(a: &mut Self.{ bar }) {} + fn d(a: &mut Self.{}) {} +} diff --git a/tests/ui/README.md b/tests/ui/README.md index 2fe1657e7ecf2..1fd522fb1ce22 100644 --- a/tests/ui/README.md +++ b/tests/ui/README.md @@ -1550,6 +1550,13 @@ Tests on `enum` variants. **FIXME**: Should be rehomed with `tests/ui/enum/`. +## `tests/ui/view-types` + +Anything related to view types. + +See +[Tracking Issue for view types](https://github.com/rust-lang/rust/issues/155938). + ## `tests/ui/wasm/` These tests target the `wasm32` architecture specifically. They are usually regression tests for WASM-specific bugs which were observed in the past. diff --git a/tests/ui/view-types/delim-check.rs b/tests/ui/view-types/delim-check.rs new file mode 100644 index 0000000000000..c7dce971eac70 --- /dev/null +++ b/tests/ui/view-types/delim-check.rs @@ -0,0 +1,14 @@ +#![feature(view_types)] +#![allow(irrefutable_let_patterns)] + +struct Foo { + bar: usize, +} + +fn main() { + let foo = Foo { bar: 42 }; + let a = &foo as &Foo.{ bar } else { + //~^ ERROR right curly brace `}` before `else` in a `let...else` statement not allowed + return; + }; +} diff --git a/tests/ui/view-types/delim-check.stderr b/tests/ui/view-types/delim-check.stderr new file mode 100644 index 0000000000000..d0663682d652f --- /dev/null +++ b/tests/ui/view-types/delim-check.stderr @@ -0,0 +1,13 @@ +error: right curly brace `}` before `else` in a `let...else` statement not allowed + --> $DIR/delim-check.rs:10:32 + | +LL | let a = &foo as &Foo.{ bar } else { + | ^ + | +help: wrap the type in parentheses + | +LL | let a = &foo as &(Foo.{ bar }) else { + | + + + +error: aborting due to 1 previous error + diff --git a/tests/ui/view-types/must-be-struct.rs b/tests/ui/view-types/must-be-struct.rs new file mode 100644 index 0000000000000..b5c3bc429bb66 --- /dev/null +++ b/tests/ui/view-types/must-be-struct.rs @@ -0,0 +1,16 @@ +//@ known-bug: unknown +//@ check-pass + +#![feature(view_types)] +#![allow(unused)] + +enum Foo { + Bar, + Baz, +} + +fn f(_: Foo.{}) {} +fn g(_: u8.{}) {} +fn h(_: char.{}) {} + +fn main() {} diff --git a/tests/ui/view-types/must-exist.rs b/tests/ui/view-types/must-exist.rs new file mode 100644 index 0000000000000..0dd26e2e18c37 --- /dev/null +++ b/tests/ui/view-types/must-exist.rs @@ -0,0 +1,14 @@ +//@ known-bug: unknown +//@ check-pass + +#![feature(view_types)] +#![allow(unused)] + +struct S { + foo: (), +} + +fn f(_: S.{ bar }) {} +fn g(_: S.{ foo, bar }) {} + +fn main() {} diff --git a/tests/ui/view-types/must-restrict.rs b/tests/ui/view-types/must-restrict.rs new file mode 100644 index 0000000000000..b5fcb4752739a --- /dev/null +++ b/tests/ui/view-types/must-restrict.rs @@ -0,0 +1,16 @@ +//@ known-bug: unknown +//@ check-pass + +#![feature(view_types)] +#![allow(unused)] + +struct S { + foo: (), + bar: (), +} + +fn f(_: S.{}.{ foo }) {} +fn g(_: S.{ foo }.{ bar }) {} +fn h(_: S.{ foo }.{}.{ foo }) {} + +fn main() {} diff --git a/tests/ui/view-types/syntax-errors.rs b/tests/ui/view-types/syntax-errors.rs new file mode 100644 index 0000000000000..d32eb4796157b --- /dev/null +++ b/tests/ui/view-types/syntax-errors.rs @@ -0,0 +1,23 @@ +#![feature(view_types)] +#![allow(unused)] + +struct Foo { + bar: usize, + baz: usize, +} + +impl Foo { + fn not_a_field(&mut self.{ _ }, _: &mut Foo.{ _ }) {} + //~^ ERROR expected identifier, found reserved identifier + //~| ERROR expected identifier, found reserved identifier + + fn keyword(&mut self.{ where }, _: &mut Foo.{ for }) {} + //~^ ERROR expected identifier, found keyword + //~| ERROR expected identifier, found keyword + + fn no_comma(&mut self.{ bar baz }, _: &mut Foo.{ bar baz }) {} + //~^ ERROR expected one of `,` or `}`, found `baz` + //~| ERROR expected one of `,` or `}`, found `baz` +} + +fn main() {} diff --git a/tests/ui/view-types/syntax-errors.stderr b/tests/ui/view-types/syntax-errors.stderr new file mode 100644 index 0000000000000..a0521b1cf07c0 --- /dev/null +++ b/tests/ui/view-types/syntax-errors.stderr @@ -0,0 +1,52 @@ +error: expected identifier, found reserved identifier `_` + --> $DIR/syntax-errors.rs:10:32 + | +LL | fn not_a_field(&mut self.{ _ }, _: &mut Foo.{ _ }) {} + | ^ expected identifier, found reserved identifier + +error: expected identifier, found reserved identifier `_` + --> $DIR/syntax-errors.rs:10:51 + | +LL | fn not_a_field(&mut self.{ _ }, _: &mut Foo.{ _ }) {} + | ^ expected identifier, found reserved identifier + +error: expected identifier, found keyword `where` + --> $DIR/syntax-errors.rs:14:28 + | +LL | fn keyword(&mut self.{ where }, _: &mut Foo.{ for }) {} + | ^^^^^ expected identifier, found keyword + | +help: escape `where` to use it as an identifier + | +LL | fn keyword(&mut self.{ r#where }, _: &mut Foo.{ for }) {} + | ++ + +error: expected identifier, found keyword `for` + --> $DIR/syntax-errors.rs:14:51 + | +LL | fn keyword(&mut self.{ where }, _: &mut Foo.{ for }) {} + | ^^^ expected identifier, found keyword + | +help: escape `for` to use it as an identifier + | +LL | fn keyword(&mut self.{ where }, _: &mut Foo.{ r#for }) {} + | ++ + +error: expected one of `,` or `}`, found `baz` + --> $DIR/syntax-errors.rs:18:33 + | +LL | fn no_comma(&mut self.{ bar baz }, _: &mut Foo.{ bar baz }) {} + | -^^^ expected one of `,` or `}` + | | + | help: missing `,` + +error: expected one of `,` or `}`, found `baz` + --> $DIR/syntax-errors.rs:18:58 + | +LL | fn no_comma(&mut self.{ bar baz }, _: &mut Foo.{ bar baz }) {} + | -^^^ expected one of `,` or `}` + | | + | help: missing `,` + +error: aborting due to 6 previous errors + diff --git a/tests/ui/view-types/tuple-structs.rs b/tests/ui/view-types/tuple-structs.rs new file mode 100644 index 0000000000000..a355ba56d50ea --- /dev/null +++ b/tests/ui/view-types/tuple-structs.rs @@ -0,0 +1,13 @@ +//@ run-pass + +#![feature(view_types)] +#![allow(unused)] + +struct Pair(usize, u32); + +impl Pair { + fn foo(&mut self.{ 0, 1 }) {} + fn bar(_pair: &mut Pair.{ 0, 1 }) {} +} + +fn main() {}