From 69363492337fa5280d8242f8b68de9a4b60a8b55 Mon Sep 17 00:00:00 2001 From: Ryan Levick Date: Thu, 10 Dec 2020 13:20:07 +0100 Subject: [PATCH] Add support for using qualified paths with structs in expression and pattern position. --- compiler/rustc_ast/src/ast.rs | 14 +- compiler/rustc_ast/src/mut_visit.rs | 9 +- compiler/rustc_ast/src/visit.rs | 13 +- compiler/rustc_ast_lowering/src/expr.rs | 20 +-- compiler/rustc_ast_lowering/src/pat.rs | 8 +- compiler/rustc_ast_passes/src/feature_gate.rs | 1 + compiler/rustc_ast_pretty/src/pprust/state.rs | 25 +++- compiler/rustc_expand/src/build.rs | 11 +- compiler/rustc_feature/src/active.rs | 3 + compiler/rustc_lint/src/unused.rs | 4 +- compiler/rustc_parse/src/parser/attr.rs | 1 - .../rustc_parse/src/parser/diagnostics.rs | 2 +- compiler/rustc_parse/src/parser/expr.rs | 28 ++-- compiler/rustc_parse/src/parser/pat.rs | 23 +--- compiler/rustc_parse/src/parser/stmt.rs | 2 +- compiler/rustc_resolve/src/late.rs | 10 +- compiler/rustc_span/src/symbol.rs | 1 + .../language-features/more-qualified-paths.md | 29 ++++ src/test/ui-fulldeps/pprust-expr-roundtrip.rs | 127 +++++++++--------- ...ssociated-type-destructuring-assignment.rs | 11 ++ .../associated-types/associated-type-macro.rs | 4 + .../associated-type-macro.stderr | 8 ++ .../associated-type-struct-construction.rs | 24 ++++ ...sociated-type-tuple-struct-construction.rs | 24 ++++ ...ated-type-tuple-struct-construction.stderr | 19 +++ .../feature-gate-more-qualified-paths.rs | 27 ++++ .../feature-gate-more-qualified-paths.stderr | 30 +++++ .../brace-after-qualified-path-in-match.rs | 7 - ...brace-after-qualified-path-in-match.stderr | 10 -- .../paren-after-qualified-path-in-match.rs | 7 - ...paren-after-qualified-path-in-match.stderr | 10 -- .../src/misc_early/unneeded_field_pattern.rs | 2 +- .../misc_early/unneeded_wildcard_pattern.rs | 2 +- .../clippy_lints/src/non_expressive_names.rs | 2 +- .../clippy_lints/src/unnested_or_patterns.rs | 16 ++- .../clippy/clippy_utils/src/ast_utils.rs | 17 ++- src/tools/rustfmt/src/expr.rs | 4 +- src/tools/rustfmt/src/patterns.rs | 6 +- 38 files changed, 374 insertions(+), 187 deletions(-) create mode 100644 src/doc/unstable-book/src/language-features/more-qualified-paths.md create mode 100644 src/test/ui/associated-types/associated-type-destructuring-assignment.rs create mode 100644 src/test/ui/associated-types/associated-type-macro.rs create mode 100644 src/test/ui/associated-types/associated-type-macro.stderr create mode 100644 src/test/ui/associated-types/associated-type-struct-construction.rs create mode 100644 src/test/ui/associated-types/associated-type-tuple-struct-construction.rs create mode 100644 src/test/ui/associated-types/associated-type-tuple-struct-construction.stderr create mode 100644 src/test/ui/feature-gates/feature-gate-more-qualified-paths.rs create mode 100644 src/test/ui/feature-gates/feature-gate-more-qualified-paths.stderr delete mode 100644 src/test/ui/parser/brace-after-qualified-path-in-match.rs delete mode 100644 src/test/ui/parser/brace-after-qualified-path-in-match.stderr delete mode 100644 src/test/ui/parser/paren-after-qualified-path-in-match.rs delete mode 100644 src/test/ui/parser/paren-after-qualified-path-in-match.stderr diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs index b3bac1d7ecdef..93d7a59768133 100644 --- a/compiler/rustc_ast/src/ast.rs +++ b/compiler/rustc_ast/src/ast.rs @@ -623,12 +623,13 @@ impl Pat { 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)), + 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)) - } + 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), @@ -701,10 +702,10 @@ pub enum PatKind { /// A struct or struct variant pattern (e.g., `Variant {x, y, ..}`). /// The `bool` is `true` in the presence of a `..`. - Struct(Path, Vec, /* recovered */ bool), + Struct(Option, Path, Vec, /* recovered */ bool), /// A tuple struct/variant pattern (`Variant(x, y, .., z)`). - TupleStruct(Path, Vec>), + TupleStruct(Option, Path, Vec>), /// An or-pattern `A | B | C`. /// Invariant: `pats.len() >= 2`. @@ -1247,6 +1248,7 @@ pub enum StructRest { #[derive(Clone, Encodable, Decodable, Debug)] pub struct StructExpr { + pub qself: Option, pub path: Path, pub fields: Vec, pub rest: StructRest, diff --git a/compiler/rustc_ast/src/mut_visit.rs b/compiler/rustc_ast/src/mut_visit.rs index 071d41ea2b2c7..0b6099fd330da 100644 --- a/compiler/rustc_ast/src/mut_visit.rs +++ b/compiler/rustc_ast/src/mut_visit.rs @@ -1139,7 +1139,8 @@ pub fn noop_visit_pat(pat: &mut P, vis: &mut T) { visit_opt(sub, |sub| vis.visit_pat(sub)); } PatKind::Lit(e) => vis.visit_expr(e), - PatKind::TupleStruct(path, elems) => { + PatKind::TupleStruct(qself, path, elems) => { + vis.visit_qself(qself); vis.visit_path(path); visit_vec(elems, |elem| vis.visit_pat(elem)); } @@ -1147,7 +1148,8 @@ pub fn noop_visit_pat(pat: &mut P, vis: &mut T) { vis.visit_qself(qself); vis.visit_path(path); } - PatKind::Struct(path, fields, _etc) => { + 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)); } @@ -1333,7 +1335,8 @@ pub fn noop_visit_expr( } ExprKind::MacCall(mac) => vis.visit_mac_call(mac), ExprKind::Struct(se) => { - let StructExpr { path, fields, rest } = se.deref_mut(); + 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 { diff --git a/compiler/rustc_ast/src/visit.rs b/compiler/rustc_ast/src/visit.rs index f1a99bc51c96d..1ebfcf367110f 100644 --- a/compiler/rustc_ast/src/visit.rs +++ b/compiler/rustc_ast/src/visit.rs @@ -497,7 +497,10 @@ pub fn walk_assoc_ty_constraint<'a, V: Visitor<'a>>( pub fn walk_pat<'a, V: Visitor<'a>>(visitor: &mut V, pattern: &'a Pat) { match pattern.kind { - PatKind::TupleStruct(ref path, ref elems) => { + 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); } @@ -507,7 +510,10 @@ pub fn walk_pat<'a, V: Visitor<'a>>(visitor: &mut V, pattern: &'a Pat) { } visitor.visit_path(path, pattern.id) } - PatKind::Struct(ref path, ref fields, _) => { + 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); } @@ -740,6 +746,9 @@ pub fn walk_expr<'a, V: Visitor<'a>>(visitor: &mut V, expression: &'a Expr) { 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 { diff --git a/compiler/rustc_ast_lowering/src/expr.rs b/compiler/rustc_ast_lowering/src/expr.rs index 866f2180bb6e3..b9dcd083c0b8c 100644 --- a/compiler/rustc_ast_lowering/src/expr.rs +++ b/compiler/rustc_ast_lowering/src/expr.rs @@ -237,7 +237,7 @@ impl<'hir> LoweringContext<'_, 'hir> { hir::ExprKind::Struct( self.arena.alloc(self.lower_qpath( e.id, - &None, + &se.qself, &se.path, ParamMode::Optional, ImplTraitContext::disallowed(), @@ -1041,10 +1041,12 @@ impl<'hir> LoweringContext<'_, 'hir> { /// 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 Path> { - // For tuple struct destructuring, it must be a non-qualified path (like in patterns). - if let ExprKind::Path(None, path) = &expr.kind { - // Does the path resolves to something disallowed in a tuple struct/variant pattern? + fn extract_tuple_struct_path<'a>( + &mut self, + expr: &'a Expr, + ) -> Option<(&'a Option, &'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() @@ -1052,7 +1054,7 @@ impl<'hir> LoweringContext<'_, 'hir> { return None; } } - return Some(path); + return Some((qself, path)); } None } @@ -1088,7 +1090,7 @@ impl<'hir> LoweringContext<'_, 'hir> { } // Tuple structs. ExprKind::Call(callee, args) => { - if let Some(path) = self.extract_tuple_struct_path(callee) { + if let Some((qself, path)) = self.extract_tuple_struct_path(callee) { let (pats, rest) = self.destructure_sequence( args, "tuple struct or variant", @@ -1097,7 +1099,7 @@ impl<'hir> LoweringContext<'_, 'hir> { ); let qpath = self.lower_qpath( callee.id, - &None, + qself, path, ParamMode::Optional, ImplTraitContext::disallowed(), @@ -1122,7 +1124,7 @@ impl<'hir> LoweringContext<'_, 'hir> { })); let qpath = self.lower_qpath( lhs.id, - &None, + &se.qself, &se.path, ParamMode::Optional, ImplTraitContext::disallowed(), diff --git a/compiler/rustc_ast_lowering/src/pat.rs b/compiler/rustc_ast_lowering/src/pat.rs index 2451409aac88e..66e623528f3bd 100644 --- a/compiler/rustc_ast_lowering/src/pat.rs +++ b/compiler/rustc_ast_lowering/src/pat.rs @@ -21,10 +21,10 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { break self.lower_pat_ident(pattern, binding_mode, ident, lower_sub); } PatKind::Lit(ref e) => break hir::PatKind::Lit(self.lower_expr(e)), - PatKind::TupleStruct(ref path, ref pats) => { + PatKind::TupleStruct(ref qself, ref path, ref pats) => { let qpath = self.lower_qpath( pattern.id, - &None, + qself, path, ParamMode::Optional, ImplTraitContext::disallowed(), @@ -47,10 +47,10 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { ); break hir::PatKind::Path(qpath); } - PatKind::Struct(ref path, ref fields, etc) => { + PatKind::Struct(ref qself, ref path, ref fields, etc) => { let qpath = self.lower_qpath( pattern.id, - &None, + qself, path, ParamMode::Optional, ImplTraitContext::disallowed(), diff --git a/compiler/rustc_ast_passes/src/feature_gate.rs b/compiler/rustc_ast_passes/src/feature_gate.rs index 664e138b39dc8..3f98944d850e7 100644 --- a/compiler/rustc_ast_passes/src/feature_gate.rs +++ b/compiler/rustc_ast_passes/src/feature_gate.rs @@ -705,6 +705,7 @@ pub fn check_crate(krate: &ast::Crate, sess: &Session) { "async closures are unstable", "to use an async block, remove the `||`: `async {`" ); + 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_bound_opt_out, "`?const` on trait bounds is experimental"); diff --git a/compiler/rustc_ast_pretty/src/pprust/state.rs b/compiler/rustc_ast_pretty/src/pprust/state.rs index b7bb896f31802..93facd255df5e 100644 --- a/compiler/rustc_ast_pretty/src/pprust/state.rs +++ b/compiler/rustc_ast_pretty/src/pprust/state.rs @@ -1713,11 +1713,16 @@ impl<'a> State<'a> { fn print_expr_struct( &mut self, + qself: &Option, path: &ast::Path, fields: &[ast::ExprField], rest: &ast::StructRest, ) { - self.print_path(path, true, 0); + if let Some(qself) = qself { + self.print_qpath(path, qself, true); + } else { + self.print_path(path, true, 0); + } self.s.word("{"); self.commasep_cmnt( Consistent, @@ -1874,7 +1879,7 @@ impl<'a> State<'a> { self.print_expr_repeat(element, count); } ast::ExprKind::Struct(ref se) => { - self.print_expr_struct(&se.path, &se.fields, &se.rest); + self.print_expr_struct(&se.qself, &se.path, &se.fields, &se.rest); } ast::ExprKind::Tup(ref exprs) => { self.print_expr_tup(exprs); @@ -2340,8 +2345,12 @@ impl<'a> State<'a> { self.print_pat(p); } } - PatKind::TupleStruct(ref path, ref elts) => { - self.print_path(path, true, 0); + 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(); @@ -2355,8 +2364,12 @@ impl<'a> State<'a> { PatKind::Path(Some(ref qself), ref path) => { self.print_qpath(path, qself, false); } - PatKind::Struct(ref path, ref fields, etc) => { - self.print_path(path, true, 0); + 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_space("{"); self.commasep_cmnt( diff --git a/compiler/rustc_expand/src/build.rs b/compiler/rustc_expand/src/build.rs index cb8b9398283ef..ef5b97a946909 100644 --- a/compiler/rustc_expand/src/build.rs +++ b/compiler/rustc_expand/src/build.rs @@ -275,7 +275,12 @@ impl<'a> ExtCtxt<'a> { ) -> P { self.expr( span, - ast::ExprKind::Struct(P(ast::StructExpr { path, fields, rest: ast::StructRest::None })), + ast::ExprKind::Struct(P(ast::StructExpr { + qself: None, + path, + fields, + rest: ast::StructRest::None, + })), ) } pub fn expr_struct_ident( @@ -405,7 +410,7 @@ impl<'a> ExtCtxt<'a> { path: ast::Path, subpats: Vec>, ) -> P { - self.pat(span, PatKind::TupleStruct(path, subpats)) + self.pat(span, PatKind::TupleStruct(None, path, subpats)) } pub fn pat_struct( &self, @@ -413,7 +418,7 @@ impl<'a> ExtCtxt<'a> { path: ast::Path, field_pats: Vec, ) -> P { - self.pat(span, PatKind::Struct(path, field_pats, false)) + self.pat(span, PatKind::Struct(None, path, field_pats, false)) } pub fn pat_tuple(&self, span: Span, pats: Vec>) -> P { self.pat(span, PatKind::Tuple(pats)) diff --git a/compiler/rustc_feature/src/active.rs b/compiler/rustc_feature/src/active.rs index 0359f503ef96c..56a320c8d3bce 100644 --- a/compiler/rustc_feature/src/active.rs +++ b/compiler/rustc_feature/src/active.rs @@ -663,6 +663,9 @@ declare_features! ( /// Allows unnamed fields of struct and union type (active, unnamed_fields, "1.53.0", Some(49804), None), + /// Allows qualified paths in struct expressions, struct patterns and tuple struct patterns. + (active, more_qualified_paths, "1.54.0", Some(80080), None), + // ------------------------------------------------------------------------- // feature-group-end: actual feature gates // ------------------------------------------------------------------------- diff --git a/compiler/rustc_lint/src/unused.rs b/compiler/rustc_lint/src/unused.rs index 67946dfb292a6..44c2a550c30e2 100644 --- a/compiler/rustc_lint/src/unused.rs +++ b/compiler/rustc_lint/src/unused.rs @@ -858,10 +858,10 @@ impl EarlyLintPass for UnusedParens { // The other cases do not contain sub-patterns. | Wild | Rest | Lit(..) | MacCall(..) | Range(..) | Ident(.., None) | Path(..) => {}, // These are list-like patterns; parens can always be removed. - TupleStruct(_, ps) | Tuple(ps) | Slice(ps) | Or(ps) => for p in ps { + TupleStruct(_, _, ps) | Tuple(ps) | Slice(ps) | Or(ps) => for p in ps { self.check_unused_parens_pat(cx, p, false, false); }, - Struct(_, fps, _) => for f in fps { + Struct(_, _, fps, _) => for f in fps { self.check_unused_parens_pat(cx, &f.pat, false, false); }, // Avoid linting on `i @ (p0 | .. | pn)` and `box (p0 | .. | pn)`, #64106. diff --git a/compiler/rustc_parse/src/parser/attr.rs b/compiler/rustc_parse/src/parser/attr.rs index ee6ff4dba396a..8b050389078a6 100644 --- a/compiler/rustc_parse/src/parser/attr.rs +++ b/compiler/rustc_parse/src/parser/attr.rs @@ -32,7 +32,6 @@ impl<'a> Parser<'a> { let mut just_parsed_doc_comment = false; let start_pos = self.token_cursor.num_next_calls; loop { - debug!("parse_outer_attributes: self.token={:?}", self.token); let attr = if self.check(&token::Pound) { let inner_error_reason = if just_parsed_doc_comment { "an inner attribute is not permitted following an outer doc comment" diff --git a/compiler/rustc_parse/src/parser/diagnostics.rs b/compiler/rustc_parse/src/parser/diagnostics.rs index 72fdc78c30cbc..b37caaebfb689 100644 --- a/compiler/rustc_parse/src/parser/diagnostics.rs +++ b/compiler/rustc_parse/src/parser/diagnostics.rs @@ -366,7 +366,7 @@ impl<'a> Parser<'a> { let mut snapshot = self.clone(); let path = Path { segments: vec![], span: self.prev_token.span.shrink_to_lo(), tokens: None }; - let struct_expr = snapshot.parse_struct_expr(path, AttrVec::new(), false); + let struct_expr = snapshot.parse_struct_expr(None, path, AttrVec::new(), false); let block_tail = self.parse_block_tail(lo, s, AttemptLocalParseRecovery::No); return Some(match (struct_expr, block_tail) { (Ok(expr), Err(mut err)) => { diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs index a764cf6bdb04e..c8789abc142d6 100644 --- a/compiler/rustc_parse/src/parser/expr.rs +++ b/compiler/rustc_parse/src/parser/expr.rs @@ -1108,9 +1108,6 @@ impl<'a> Parser<'a> { self.parse_closure_expr(attrs) } else if self.check(&token::OpenDelim(token::Bracket)) { self.parse_array_or_repeat_expr(attrs) - } else if self.eat_lt() { - let (qself, path) = self.parse_qpath(PathStyle::Expr)?; - Ok(self.mk_expr(lo.to(path.span), ExprKind::Path(Some(qself), path), attrs)) } else if self.check_path() { self.parse_path_start_expr(attrs) } else if self.check_keyword(kw::Move) || self.check_keyword(kw::Static) { @@ -1262,12 +1259,20 @@ impl<'a> Parser<'a> { } fn parse_path_start_expr(&mut self, attrs: AttrVec) -> PResult<'a, P> { - let path = self.parse_path(PathStyle::Expr)?; + let (qself, path) = if self.eat_lt() { + let (qself, path) = self.parse_qpath(PathStyle::Expr)?; + (Some(qself), path) + } else { + (None, self.parse_path(PathStyle::Expr)?) + }; let lo = path.span; // `!`, as an operator, is prefix, so we know this isn't that. let (hi, kind) = if self.eat(&token::Not) { // MACRO INVOCATION expression + if qself.is_some() { + self.struct_span_err(path.span, "macros cannot use qualified paths").emit(); + } let mac = MacCall { path, args: self.parse_mac_args()?, @@ -1275,13 +1280,16 @@ impl<'a> Parser<'a> { }; (self.prev_token.span, ExprKind::MacCall(mac)) } else if self.check(&token::OpenDelim(token::Brace)) { - if let Some(expr) = self.maybe_parse_struct_expr(&path, &attrs) { + if let Some(expr) = self.maybe_parse_struct_expr(qself.as_ref(), &path, &attrs) { + if qself.is_some() { + self.sess.gated_spans.gate(sym::more_qualified_paths, path.span); + } return expr; } else { - (path.span, ExprKind::Path(None, path)) + (path.span, ExprKind::Path(qself, path)) } } else { - (path.span, ExprKind::Path(None, path)) + (path.span, ExprKind::Path(qself, path)) }; let expr = self.mk_expr(lo.to(hi), kind, attrs); @@ -2247,6 +2255,7 @@ impl<'a> Parser<'a> { fn maybe_parse_struct_expr( &mut self, + qself: Option<&ast::QSelf>, path: &ast::Path, attrs: &AttrVec, ) -> Option>> { @@ -2255,7 +2264,7 @@ impl<'a> Parser<'a> { if let Err(err) = self.expect(&token::OpenDelim(token::Brace)) { return Some(Err(err)); } - let expr = self.parse_struct_expr(path.clone(), attrs.clone(), true); + let expr = self.parse_struct_expr(qself.cloned(), path.clone(), attrs.clone(), true); if let (Ok(expr), false) = (&expr, struct_allowed) { // This is a struct literal, but we don't can't accept them here. self.error_struct_lit_not_allowed_here(path.span, expr.span); @@ -2278,6 +2287,7 @@ impl<'a> Parser<'a> { /// Precondition: already parsed the '{'. pub(super) fn parse_struct_expr( &mut self, + qself: Option, pth: ast::Path, attrs: AttrVec, recover: bool, @@ -2375,7 +2385,7 @@ impl<'a> Parser<'a> { let expr = if recover_async { ExprKind::Err } else { - ExprKind::Struct(P(ast::StructExpr { path: pth, fields, rest: base })) + ExprKind::Struct(P(ast::StructExpr { qself, path: pth, fields, rest: base })) }; Ok(self.mk_expr(span, expr, attrs)) } diff --git a/compiler/rustc_parse/src/parser/pat.rs b/compiler/rustc_parse/src/parser/pat.rs index 0abefbd6a1219..418122202be1b 100644 --- a/compiler/rustc_parse/src/parser/pat.rs +++ b/compiler/rustc_parse/src/parser/pat.rs @@ -859,7 +859,8 @@ impl<'a> Parser<'a> { /// Parse a struct ("record") pattern (e.g. `Foo { ... }` or `Foo::Bar { ... }`). fn parse_pat_struct(&mut self, qself: Option, path: Path) -> PResult<'a, PatKind> { if qself.is_some() { - return self.error_qpath_before_pat(&path, "{"); + // Feature gate the use of qualified paths in patterns + self.sess.gated_spans.gate(sym::more_qualified_paths, path.span); } self.bump(); let (fields, etc) = self.parse_pat_fields().unwrap_or_else(|mut e| { @@ -869,27 +870,17 @@ impl<'a> Parser<'a> { (vec![], true) }); self.bump(); - Ok(PatKind::Struct(path, fields, etc)) + Ok(PatKind::Struct(qself, path, fields, etc)) } /// Parse tuple struct or tuple variant pattern (e.g. `Foo(...)` or `Foo::Bar(...)`). fn parse_pat_tuple_struct(&mut self, qself: Option, path: Path) -> PResult<'a, PatKind> { - if qself.is_some() { - return self.error_qpath_before_pat(&path, "("); - } let (fields, _) = self.parse_paren_comma_seq(|p| p.parse_pat_allow_top_alt(None, RecoverComma::No))?; - Ok(PatKind::TupleStruct(path, fields)) - } - - /// Error when there's a qualified path, e.g. `::Baz` - /// as the path of e.g., a tuple or record struct pattern. - fn error_qpath_before_pat(&mut self, path: &Path, token: &str) -> PResult<'a, PatKind> { - let msg = &format!("unexpected `{}` after qualified path", token); - let mut err = self.struct_span_err(self.token.span, msg); - err.span_label(self.token.span, msg); - err.span_label(path.span, "the qualified path"); - Err(err) + if qself.is_some() { + self.sess.gated_spans.gate(sym::more_qualified_paths, path.span); + } + Ok(PatKind::TupleStruct(qself, path, fields)) } /// Parses the fields of a struct-like pattern. diff --git a/compiler/rustc_parse/src/parser/stmt.rs b/compiler/rustc_parse/src/parser/stmt.rs index 4f0dcfeb5dae0..9ef3f61ec346b 100644 --- a/compiler/rustc_parse/src/parser/stmt.rs +++ b/compiler/rustc_parse/src/parser/stmt.rs @@ -117,7 +117,7 @@ impl<'a> Parser<'a> { } let expr = if this.eat(&token::OpenDelim(token::Brace)) { - this.parse_struct_expr(path, AttrVec::new(), true)? + this.parse_struct_expr(None, path, AttrVec::new(), true)? } else { let hi = this.prev_token.span; this.mk_expr(lo.to(hi), ExprKind::Path(None, path), AttrVec::new()) diff --git a/compiler/rustc_resolve/src/late.rs b/compiler/rustc_resolve/src/late.rs index 408d9b2392165..a21d8197bdbb3 100644 --- a/compiler/rustc_resolve/src/late.rs +++ b/compiler/rustc_resolve/src/late.rs @@ -1613,10 +1613,10 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { self.r.record_partial_res(pat.id, PartialRes::new(res)); self.r.record_pat_span(pat.id, pat.span); } - PatKind::TupleStruct(ref path, ref sub_patterns) => { + PatKind::TupleStruct(ref qself, ref path, ref sub_patterns) => { self.smart_resolve_path( pat.id, - None, + qself.as_ref(), path, PathSource::TupleStruct( pat.span, @@ -1627,8 +1627,8 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { PatKind::Path(ref qself, ref path) => { self.smart_resolve_path(pat.id, qself.as_ref(), path, PathSource::Pat); } - PatKind::Struct(ref path, ..) => { - self.smart_resolve_path(pat.id, None, path, PathSource::Struct); + PatKind::Struct(ref qself, ref path, ..) => { + self.smart_resolve_path(pat.id, qself.as_ref(), path, PathSource::Struct); } PatKind::Or(ref ps) => { // Add a new set of bindings to the stack. `Or` here records that when a @@ -2288,7 +2288,7 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { } ExprKind::Struct(ref se) => { - self.smart_resolve_path(expr.id, None, &se.path, PathSource::Struct); + self.smart_resolve_path(expr.id, se.qself.as_ref(), &se.path, PathSource::Struct); visit::walk_expr(self, expr); } diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 31b425f1a79a0..fb37c5e9c1eff 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -756,6 +756,7 @@ symbols! { modifiers, module, module_path, + more_qualified_paths, more_struct_aliases, movbe_target_feature, move_ref_pattern, diff --git a/src/doc/unstable-book/src/language-features/more-qualified-paths.md b/src/doc/unstable-book/src/language-features/more-qualified-paths.md new file mode 100644 index 0000000000000..857af577a6cfe --- /dev/null +++ b/src/doc/unstable-book/src/language-features/more-qualified-paths.md @@ -0,0 +1,29 @@ +# `more_qualified_paths` + +The `more_qualified_paths` feature can be used in order to enable the +use of qualified paths in patterns. + +## Example + +```rust +#![feature(more_qualified_paths)] + +fn main() { + // destructure through a qualified path + let ::Assoc { br } = StructStruct { br: 2 }; +} + +struct StructStruct { + br: i8, +} + +struct Foo; + +trait A { + type Assoc; +} + +impl A for Foo { + type Assoc = StructStruct; +} +``` diff --git a/src/test/ui-fulldeps/pprust-expr-roundtrip.rs b/src/test/ui-fulldeps/pprust-expr-roundtrip.rs index ac2d29c9caf92..091c834eccf5f 100644 --- a/src/test/ui-fulldeps/pprust-expr-roundtrip.rs +++ b/src/test/ui-fulldeps/pprust-expr-roundtrip.rs @@ -19,45 +19,35 @@ #![feature(rustc_private)] +extern crate rustc_ast; extern crate rustc_ast_pretty; extern crate rustc_data_structures; -extern crate rustc_ast; extern crate rustc_parse; extern crate rustc_session; extern crate rustc_span; +use rustc_ast::mut_visit::{self, visit_clobber, MutVisitor}; +use rustc_ast::ptr::P; +use rustc_ast::*; use rustc_ast_pretty::pprust; use rustc_data_structures::thin_vec::ThinVec; use rustc_parse::new_parser_from_source_str; use rustc_session::parse::ParseSess; -use rustc_span::source_map::{Spanned, DUMMY_SP, FileName}; use rustc_span::source_map::FilePathMapping; +use rustc_span::source_map::{FileName, Spanned, DUMMY_SP}; use rustc_span::symbol::Ident; -use rustc_ast::*; -use rustc_ast::mut_visit::{self, MutVisitor, visit_clobber}; -use rustc_ast::ptr::P; fn parse_expr(ps: &ParseSess, src: &str) -> Option> { let src_as_string = src.to_string(); - let mut p = new_parser_from_source_str( - ps, - FileName::Custom(src_as_string.clone()), - src_as_string, - ); + let mut p = + new_parser_from_source_str(ps, FileName::Custom(src_as_string.clone()), src_as_string); p.parse_expr().map_err(|mut e| e.cancel()).ok() } - // Helper functions for building exprs fn expr(kind: ExprKind) -> P { - P(Expr { - id: DUMMY_NODE_ID, - kind, - span: DUMMY_SP, - attrs: ThinVec::new(), - tokens: None - }) + P(Expr { id: DUMMY_NODE_ID, kind, span: DUMMY_SP, attrs: ThinVec::new(), tokens: None }) } fn make_x() -> P { @@ -83,11 +73,13 @@ fn iter_exprs(depth: usize, f: &mut dyn FnMut(P)) { 1 => iter_exprs(depth - 1, &mut |e| g(ExprKind::Call(e, vec![]))), 2 => { let seg = PathSegment::from_ident(Ident::from_str("x")); - iter_exprs(depth - 1, &mut |e| g(ExprKind::MethodCall( - seg.clone(), vec![e, make_x()], DUMMY_SP))); - iter_exprs(depth - 1, &mut |e| g(ExprKind::MethodCall( - seg.clone(), vec![make_x(), e], DUMMY_SP))); - }, + iter_exprs(depth - 1, &mut |e| { + g(ExprKind::MethodCall(seg.clone(), vec![e, make_x()], DUMMY_SP)) + }); + iter_exprs(depth - 1, &mut |e| { + g(ExprKind::MethodCall(seg.clone(), vec![make_x(), e], DUMMY_SP)) + }); + } 3..=8 => { let op = Spanned { span: DUMMY_SP, @@ -99,14 +91,14 @@ fn iter_exprs(depth: usize, f: &mut dyn FnMut(P)) { 7 => BinOpKind::Or, 8 => BinOpKind::Lt, _ => unreachable!(), - } + }, }; iter_exprs(depth - 1, &mut |e| g(ExprKind::Binary(op, e, make_x()))); iter_exprs(depth - 1, &mut |e| g(ExprKind::Binary(op, make_x(), e))); - }, + } 9 => { iter_exprs(depth - 1, &mut |e| g(ExprKind::Unary(UnOp::Deref, e))); - }, + } 10 => { let block = P(Block { stmts: Vec::new(), @@ -116,67 +108,66 @@ fn iter_exprs(depth: usize, f: &mut dyn FnMut(P)) { tokens: None, }); iter_exprs(depth - 1, &mut |e| g(ExprKind::If(e, block.clone(), None))); - }, + } 11 => { - let decl = P(FnDecl { - inputs: vec![], - output: FnRetTy::Default(DUMMY_SP), + let decl = P(FnDecl { inputs: vec![], output: FnRetTy::Default(DUMMY_SP) }); + iter_exprs(depth - 1, &mut |e| { + g(ExprKind::Closure( + CaptureBy::Value, + Async::No, + Movability::Movable, + decl.clone(), + e, + DUMMY_SP, + )) }); - iter_exprs(depth - 1, &mut |e| g( - ExprKind::Closure(CaptureBy::Value, - Async::No, - Movability::Movable, - decl.clone(), - e, - DUMMY_SP))); - }, + } 12 => { iter_exprs(depth - 1, &mut |e| g(ExprKind::Assign(e, make_x(), DUMMY_SP))); iter_exprs(depth - 1, &mut |e| g(ExprKind::Assign(make_x(), e, DUMMY_SP))); - }, + } 13 => { iter_exprs(depth - 1, &mut |e| g(ExprKind::Field(e, Ident::from_str("f")))); - }, + } 14 => { - iter_exprs(depth - 1, &mut |e| g(ExprKind::Range( - Some(e), Some(make_x()), RangeLimits::HalfOpen))); - iter_exprs(depth - 1, &mut |e| g(ExprKind::Range( - Some(make_x()), Some(e), RangeLimits::HalfOpen))); - }, + iter_exprs(depth - 1, &mut |e| { + g(ExprKind::Range(Some(e), Some(make_x()), RangeLimits::HalfOpen)) + }); + iter_exprs(depth - 1, &mut |e| { + g(ExprKind::Range(Some(make_x()), Some(e), RangeLimits::HalfOpen)) + }); + } 15 => { - iter_exprs( - depth - 1, - &mut |e| g(ExprKind::AddrOf(BorrowKind::Ref, Mutability::Not, e)), - ); - }, + iter_exprs(depth - 1, &mut |e| { + g(ExprKind::AddrOf(BorrowKind::Ref, Mutability::Not, e)) + }); + } 16 => { g(ExprKind::Ret(None)); iter_exprs(depth - 1, &mut |e| g(ExprKind::Ret(Some(e)))); - }, + } 17 => { let path = Path::from_ident(Ident::from_str("S")); g(ExprKind::Struct(P(StructExpr { - path, fields: vec![], rest: StructRest::Base(make_x()) + qself: None, + path, + fields: vec![], + rest: StructRest::Base(make_x()), }))); - }, + } 18 => { iter_exprs(depth - 1, &mut |e| g(ExprKind::Try(e))); - }, + } 19 => { - let pat = P(Pat { - id: DUMMY_NODE_ID, - kind: PatKind::Wild, - span: DUMMY_SP, - tokens: None, - }); + let pat = + P(Pat { id: DUMMY_NODE_ID, kind: PatKind::Wild, span: DUMMY_SP, tokens: None }); iter_exprs(depth - 1, &mut |e| g(ExprKind::Let(pat.clone(), e))) - }, + } _ => panic!("bad counter value in iter_exprs"), } } } - // Folders for manipulating the placement of `Paren` nodes. See below for why this is needed. /// `MutVisitor` that removes all `ExprKind::Paren` nodes. @@ -192,7 +183,6 @@ impl MutVisitor for RemoveParens { } } - /// `MutVisitor` that inserts `ExprKind::Paren` nodes around every `Expr`. struct AddParens; @@ -205,7 +195,7 @@ impl MutVisitor for AddParens { kind: ExprKind::Paren(e), span: DUMMY_SP, attrs: ThinVec::new(), - tokens: None + tokens: None, }) }); } @@ -238,9 +228,12 @@ fn run() { RemoveParens.visit_expr(&mut parsed); AddParens.visit_expr(&mut parsed); let text2 = pprust::expr_to_string(&parsed); - assert!(text1 == text2, - "exprs are not equal:\n e = {:?}\n parsed = {:?}", - text1, text2); + assert!( + text1 == text2, + "exprs are not equal:\n e = {:?}\n parsed = {:?}", + text1, + text2 + ); } }); } diff --git a/src/test/ui/associated-types/associated-type-destructuring-assignment.rs b/src/test/ui/associated-types/associated-type-destructuring-assignment.rs new file mode 100644 index 0000000000000..fea7c7a383fbe --- /dev/null +++ b/src/test/ui/associated-types/associated-type-destructuring-assignment.rs @@ -0,0 +1,11 @@ +// check-pass + +#![feature(destructuring_assignment)] +#![feature(more_qualified_paths)] + +enum E { V() } + +fn main() { + ::V() = E::V(); // OK, destructuring assignment + ::V {} = E::V(); // OK, destructuring assignment +} diff --git a/src/test/ui/associated-types/associated-type-macro.rs b/src/test/ui/associated-types/associated-type-macro.rs new file mode 100644 index 0000000000000..22b5bca40103d --- /dev/null +++ b/src/test/ui/associated-types/associated-type-macro.rs @@ -0,0 +1,4 @@ +fn main() { + #[cfg(FALSE)] + <() as module>::mac!(); //~ ERROR macros cannot use qualified paths +} diff --git a/src/test/ui/associated-types/associated-type-macro.stderr b/src/test/ui/associated-types/associated-type-macro.stderr new file mode 100644 index 0000000000000..6a4cf99c474e2 --- /dev/null +++ b/src/test/ui/associated-types/associated-type-macro.stderr @@ -0,0 +1,8 @@ +error: macros cannot use qualified paths + --> $DIR/associated-type-macro.rs:3:5 + | +LL | <() as module>::mac!(); + | ^^^^^^^^^^^^^^^^^^^ + +error: aborting due to previous error + diff --git a/src/test/ui/associated-types/associated-type-struct-construction.rs b/src/test/ui/associated-types/associated-type-struct-construction.rs new file mode 100644 index 0000000000000..f8f8048fb717f --- /dev/null +++ b/src/test/ui/associated-types/associated-type-struct-construction.rs @@ -0,0 +1,24 @@ +// Make sure that users can construct structs through associated types +// in both expressions and patterns + +#![feature(more_qualified_paths)] + +// check-pass +fn main() { + let ::Assoc { br } = ::Assoc { br: 2 }; + assert!(br == 2); +} + +struct StructStruct { + br: i8, +} + +struct Foo; + +trait A { + type Assoc; +} + +impl A for Foo { + type Assoc = StructStruct; +} diff --git a/src/test/ui/associated-types/associated-type-tuple-struct-construction.rs b/src/test/ui/associated-types/associated-type-tuple-struct-construction.rs new file mode 100644 index 0000000000000..d5809ecd55d85 --- /dev/null +++ b/src/test/ui/associated-types/associated-type-tuple-struct-construction.rs @@ -0,0 +1,24 @@ +// Users cannot yet construct structs through associated types +// in both expressions and patterns + +#![feature(more_qualified_paths)] + +fn main() { + let ::Assoc(n) = ::Assoc(2); + //~^ ERROR expected method or associated constant, found associated type + //~| ERROR expected method or associated constant, found associated type + assert!(n == 2); +} + +struct TupleStruct(i8); + +struct Foo; + + +trait A { + type Assoc; +} + +impl A for Foo { + type Assoc = TupleStruct; +} diff --git a/src/test/ui/associated-types/associated-type-tuple-struct-construction.stderr b/src/test/ui/associated-types/associated-type-tuple-struct-construction.stderr new file mode 100644 index 0000000000000..bca7deeb5128c --- /dev/null +++ b/src/test/ui/associated-types/associated-type-tuple-struct-construction.stderr @@ -0,0 +1,19 @@ +error[E0575]: expected method or associated constant, found associated type `A::Assoc` + --> $DIR/associated-type-tuple-struct-construction.rs:7:32 + | +LL | let ::Assoc(n) = ::Assoc(2); + | ^^^^^^^^^^^^^^^^^ + | + = note: can't use a type alias as a constructor + +error[E0575]: expected method or associated constant, found associated type `A::Assoc` + --> $DIR/associated-type-tuple-struct-construction.rs:7:9 + | +LL | let ::Assoc(n) = ::Assoc(2); + | ^^^^^^^^^^^^^^^^^ + | + = note: can't use a type alias as a constructor + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0575`. diff --git a/src/test/ui/feature-gates/feature-gate-more-qualified-paths.rs b/src/test/ui/feature-gates/feature-gate-more-qualified-paths.rs new file mode 100644 index 0000000000000..2e05acbfa1758 --- /dev/null +++ b/src/test/ui/feature-gates/feature-gate-more-qualified-paths.rs @@ -0,0 +1,27 @@ +fn main() { + // destructure through a qualified path + let ::Assoc { br } = StructStruct { br: 2 }; + //~^ ERROR usage of qualified paths in this context is experimental + let _ = ::Assoc { br: 2 }; + //~^ ERROR usage of qualified paths in this context is experimental + let ::V(..) = E::V(0); + //~^ ERROR usage of qualified paths in this context is experimental +} + +struct StructStruct { + br: i8, +} + +struct Foo; + +trait A { + type Assoc; +} + +impl A for Foo { + type Assoc = StructStruct; +} + +enum E { + V(u8) +} diff --git a/src/test/ui/feature-gates/feature-gate-more-qualified-paths.stderr b/src/test/ui/feature-gates/feature-gate-more-qualified-paths.stderr new file mode 100644 index 0000000000000..b49cc40800f03 --- /dev/null +++ b/src/test/ui/feature-gates/feature-gate-more-qualified-paths.stderr @@ -0,0 +1,30 @@ +error[E0658]: usage of qualified paths in this context is experimental + --> $DIR/feature-gate-more-qualified-paths.rs:3:9 + | +LL | let ::Assoc { br } = StructStruct { br: 2 }; + | ^^^^^^^^^^^^^^^^^ + | + = note: see issue #80080 for more information + = help: add `#![feature(more_qualified_paths)]` to the crate attributes to enable + +error[E0658]: usage of qualified paths in this context is experimental + --> $DIR/feature-gate-more-qualified-paths.rs:5:13 + | +LL | let _ = ::Assoc { br: 2 }; + | ^^^^^^^^^^^^^^^^^ + | + = note: see issue #80080 for more information + = help: add `#![feature(more_qualified_paths)]` to the crate attributes to enable + +error[E0658]: usage of qualified paths in this context is experimental + --> $DIR/feature-gate-more-qualified-paths.rs:7:9 + | +LL | let ::V(..) = E::V(0); + | ^^^^^^ + | + = note: see issue #80080 for more information + = help: add `#![feature(more_qualified_paths)]` to the crate attributes to enable + +error: aborting due to 3 previous errors + +For more information about this error, try `rustc --explain E0658`. diff --git a/src/test/ui/parser/brace-after-qualified-path-in-match.rs b/src/test/ui/parser/brace-after-qualified-path-in-match.rs deleted file mode 100644 index f41520861627a..0000000000000 --- a/src/test/ui/parser/brace-after-qualified-path-in-match.rs +++ /dev/null @@ -1,7 +0,0 @@ -fn main() { - match 10 { - ::Type{key: value} => (), - //~^ ERROR unexpected `{` after qualified path - _ => (), - } -} diff --git a/src/test/ui/parser/brace-after-qualified-path-in-match.stderr b/src/test/ui/parser/brace-after-qualified-path-in-match.stderr deleted file mode 100644 index d6fdf353f07af..0000000000000 --- a/src/test/ui/parser/brace-after-qualified-path-in-match.stderr +++ /dev/null @@ -1,10 +0,0 @@ -error: unexpected `{` after qualified path - --> $DIR/brace-after-qualified-path-in-match.rs:3:27 - | -LL | ::Type{key: value} => (), - | ------------------^ unexpected `{` after qualified path - | | - | the qualified path - -error: aborting due to previous error - diff --git a/src/test/ui/parser/paren-after-qualified-path-in-match.rs b/src/test/ui/parser/paren-after-qualified-path-in-match.rs deleted file mode 100644 index 68b1c2baf1001..0000000000000 --- a/src/test/ui/parser/paren-after-qualified-path-in-match.rs +++ /dev/null @@ -1,7 +0,0 @@ -fn main() { - match 10 { - ::Type(2) => (), - //~^ ERROR unexpected `(` after qualified path - _ => (), - } -} diff --git a/src/test/ui/parser/paren-after-qualified-path-in-match.stderr b/src/test/ui/parser/paren-after-qualified-path-in-match.stderr deleted file mode 100644 index af21f9195467a..0000000000000 --- a/src/test/ui/parser/paren-after-qualified-path-in-match.stderr +++ /dev/null @@ -1,10 +0,0 @@ -error: unexpected `(` after qualified path - --> $DIR/paren-after-qualified-path-in-match.rs:3:27 - | -LL | ::Type(2) => (), - | ------------------^ unexpected `(` after qualified path - | | - | the qualified path - -error: aborting due to previous error - diff --git a/src/tools/clippy/clippy_lints/src/misc_early/unneeded_field_pattern.rs b/src/tools/clippy/clippy_lints/src/misc_early/unneeded_field_pattern.rs index 329a0009a3e2c..2201cf56d52ab 100644 --- a/src/tools/clippy/clippy_lints/src/misc_early/unneeded_field_pattern.rs +++ b/src/tools/clippy/clippy_lints/src/misc_early/unneeded_field_pattern.rs @@ -5,7 +5,7 @@ use rustc_lint::{EarlyContext, LintContext}; use super::UNNEEDED_FIELD_PATTERN; pub(super) fn check(cx: &EarlyContext<'_>, pat: &Pat) { - if let PatKind::Struct(ref npat, ref pfields, _) = pat.kind { + if let PatKind::Struct(_, ref npat, ref pfields, _) = pat.kind { let mut wilds = 0; let type_name = npat .segments diff --git a/src/tools/clippy/clippy_lints/src/misc_early/unneeded_wildcard_pattern.rs b/src/tools/clippy/clippy_lints/src/misc_early/unneeded_wildcard_pattern.rs index 4dd032d78f1d5..df044538fe19d 100644 --- a/src/tools/clippy/clippy_lints/src/misc_early/unneeded_wildcard_pattern.rs +++ b/src/tools/clippy/clippy_lints/src/misc_early/unneeded_wildcard_pattern.rs @@ -7,7 +7,7 @@ use rustc_span::source_map::Span; use super::UNNEEDED_WILDCARD_PATTERN; pub(super) fn check(cx: &EarlyContext<'_>, pat: &Pat) { - if let PatKind::TupleStruct(_, ref patterns) | PatKind::Tuple(ref patterns) = pat.kind { + if let PatKind::TupleStruct(_, _, ref patterns) | PatKind::Tuple(ref patterns) = pat.kind { if let Some(rest_index) = patterns.iter().position(|pat| pat.is_rest()) { if let Some((left_index, left_pat)) = patterns[..rest_index] .iter() diff --git a/src/tools/clippy/clippy_lints/src/non_expressive_names.rs b/src/tools/clippy/clippy_lints/src/non_expressive_names.rs index 5292af5f07655..1a23e6afe283d 100644 --- a/src/tools/clippy/clippy_lints/src/non_expressive_names.rs +++ b/src/tools/clippy/clippy_lints/src/non_expressive_names.rs @@ -139,7 +139,7 @@ impl<'a, 'tcx, 'b> Visitor<'tcx> for SimilarNamesNameVisitor<'a, 'tcx, 'b> { self.check_ident(ident); } }, - PatKind::Struct(_, ref fields, _) => { + PatKind::Struct(_, _, ref fields, _) => { for field in fields { if !field.is_shorthand { self.visit_pat(&field.pat); diff --git a/src/tools/clippy/clippy_lints/src/unnested_or_patterns.rs b/src/tools/clippy/clippy_lints/src/unnested_or_patterns.rs index 3e985fa72b8fe..1b3c457b01adb 100644 --- a/src/tools/clippy/clippy_lints/src/unnested_or_patterns.rs +++ b/src/tools/clippy/clippy_lints/src/unnested_or_patterns.rs @@ -1,6 +1,6 @@ #![allow(clippy::wildcard_imports, clippy::enum_glob_use)] -use clippy_utils::ast_utils::{eq_field_pat, eq_id, eq_pat, eq_path}; +use clippy_utils::ast_utils::{eq_field_pat, eq_id, eq_pat, eq_path, eq_maybe_qself}; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::{meets_msrv, msrvs, over}; use rustc_ast::mut_visit::*; @@ -273,16 +273,16 @@ fn transform_with_focus_on_idx(alternatives: &mut Vec>, focus_idx: usize) |k| always_pat!(k, Tuple(ps) => ps), ), // Transform `S(pre, x, post) | ... | S(pre, y, post)` into `S(pre, x | y, post)`. - TupleStruct(path1, ps1) => extend_with_matching_product( + TupleStruct(qself1, path1, ps1) => extend_with_matching_product( ps1, start, alternatives, |k, ps1, idx| matches!( k, - TupleStruct(path2, ps2) if eq_path(path1, path2) && eq_pre_post(ps1, ps2, idx) + TupleStruct(qself2, path2, ps2) if eq_maybe_qself(qself1, qself2) && eq_path(path1, path2) && eq_pre_post(ps1, ps2, idx) ), - |k| always_pat!(k, TupleStruct(_, ps) => ps), + |k| always_pat!(k, TupleStruct(_, _, ps) => ps), ), // Transform a record pattern `S { fp_0, ..., fp_n }`. - Struct(path1, fps1, rest1) => extend_with_struct_pat(path1, fps1, *rest1, start, alternatives), + Struct(qself1, path1, fps1, rest1) => extend_with_struct_pat(qself1, path1, fps1, *rest1, start, alternatives), }; alternatives[focus_idx].kind = focus_kind; @@ -294,6 +294,7 @@ fn transform_with_focus_on_idx(alternatives: &mut Vec>, focus_idx: usize) /// So when we fixate on some `ident_k: pat_k`, we try to find `ident_k` in the other pattern /// and check that all `fp_i` where `i ∈ ((0...n) \ k)` between two patterns are equal. fn extend_with_struct_pat( + qself1: &Option, path1: &ast::Path, fps1: &mut Vec, rest1: bool, @@ -306,8 +307,9 @@ fn extend_with_struct_pat( start, alternatives, |k| { - matches!(k, Struct(path2, fps2, rest2) + matches!(k, Struct(qself2, path2, fps2, rest2) if rest1 == *rest2 // If one struct pattern has `..` so must the other. + && eq_maybe_qself(qself1, qself2) && eq_path(path1, path2) && fps1.len() == fps2.len() && fps1.iter().enumerate().all(|(idx_1, fp1)| { @@ -323,7 +325,7 @@ fn extend_with_struct_pat( })) }, // Extract `p2_k`. - |k| always_pat!(k, Struct(_, mut fps, _) => fps.swap_remove(pos_in_2.take().unwrap()).pat), + |k| always_pat!(k, Struct(_, _, mut fps, _) => fps.swap_remove(pos_in_2.take().unwrap()).pat), ); extend_with_tail_or(&mut fps1[idx].pat, tail_or) }) diff --git a/src/tools/clippy/clippy_utils/src/ast_utils.rs b/src/tools/clippy/clippy_utils/src/ast_utils.rs index 93e10c836cc7f..e6d84bc7560ba 100644 --- a/src/tools/clippy/clippy_utils/src/ast_utils.rs +++ b/src/tools/clippy/clippy_utils/src/ast_utils.rs @@ -47,9 +47,9 @@ pub fn eq_pat(l: &Pat, r: &Pat) -> bool { | (Ref(l, Mutability::Mut), Ref(r, Mutability::Mut)) => eq_pat(l, r), (Tuple(l), Tuple(r)) | (Slice(l), Slice(r)) => over(l, r, |l, r| eq_pat(l, r)), (Path(lq, lp), Path(rq, rp)) => both(lq, rq, |l, r| eq_qself(l, r)) && eq_path(lp, rp), - (TupleStruct(lp, lfs), TupleStruct(rp, rfs)) => eq_path(lp, rp) && over(lfs, rfs, |l, r| eq_pat(l, r)), - (Struct(lp, lfs, lr), Struct(rp, rfs, rr)) => { - lr == rr && eq_path(lp, rp) && unordered_over(lfs, rfs, |lf, rf| eq_field_pat(lf, rf)) + (TupleStruct(lqself, lp, lfs), TupleStruct(rqself, rp, rfs)) => eq_maybe_qself(lqself, rqself) && eq_path(lp, rp) && over(lfs, rfs, |l, r| eq_pat(l, r)), + (Struct(lqself, lp, lfs, lr), Struct(rqself, rp, rfs, rr)) => { + lr == rr && eq_maybe_qself(lqself, rqself) &&eq_path(lp, rp) && unordered_over(lfs, rfs, |lf, rf| eq_field_pat(lf, rf)) }, (Or(ls), Or(rs)) => unordered_over(ls, rs, |l, r| eq_pat(l, r)), (MacCall(l), MacCall(r)) => eq_mac_call(l, r), @@ -78,6 +78,14 @@ pub fn eq_qself(l: &QSelf, r: &QSelf) -> bool { l.position == r.position && eq_ty(&l.ty, &r.ty) } +pub fn eq_maybe_qself(l: &Option, r: &Option) -> bool { + match (l, r) { + (Some(l), Some(r)) => eq_qself(l, r), + (None, None) => true, + _ => false + } +} + pub fn eq_path(l: &Path, r: &Path) -> bool { over(&l.segments, &r.segments, |l, r| eq_path_seg(l, r)) } @@ -170,7 +178,8 @@ pub fn eq_expr(l: &Expr, r: &Expr) -> bool { (Path(lq, lp), Path(rq, rp)) => both(lq, rq, |l, r| eq_qself(l, r)) && eq_path(lp, rp), (MacCall(l), MacCall(r)) => eq_mac_call(l, r), (Struct(lse), Struct(rse)) => { - eq_path(&lse.path, &rse.path) + eq_maybe_qself(&lse.qself, &rse.qself) + && eq_path(&lse.path, &rse.path) && eq_struct_rest(&lse.rest, &rse.rest) && unordered_over(&lse.fields, &rse.fields, |l, r| eq_field(l, r)) }, diff --git a/src/tools/rustfmt/src/expr.rs b/src/tools/rustfmt/src/expr.rs index ced382c4915a1..bca9f77f959e3 100644 --- a/src/tools/rustfmt/src/expr.rs +++ b/src/tools/rustfmt/src/expr.rs @@ -107,7 +107,9 @@ pub(crate) fn format_expr( } ast::ExprKind::Unary(op, ref subexpr) => rewrite_unary_op(context, op, subexpr, shape), ast::ExprKind::Struct(ref struct_expr) => { - let ast::StructExpr { fields, path, rest } = &**struct_expr; + let ast::StructExpr { + fields, path, rest, .. + } = &**struct_expr; rewrite_struct_lit(context, path, fields, rest, &expr.attrs, expr.span, shape) } ast::ExprKind::Tup(ref items) => { diff --git a/src/tools/rustfmt/src/patterns.rs b/src/tools/rustfmt/src/patterns.rs index 6824fc661ba72..fa0ef260991d7 100644 --- a/src/tools/rustfmt/src/patterns.rs +++ b/src/tools/rustfmt/src/patterns.rs @@ -45,7 +45,7 @@ fn is_short_pattern_inner(pat: &ast::Pat) -> bool { | ast::PatKind::Path(..) | ast::PatKind::Range(..) => false, ast::PatKind::Tuple(ref subpats) => subpats.len() <= 1, - ast::PatKind::TupleStruct(ref path, ref subpats) => { + ast::PatKind::TupleStruct(_, ref path, ref subpats) => { path.segments.len() <= 1 && subpats.len() <= 1 } ast::PatKind::Box(ref p) | ast::PatKind::Ref(ref p, _) | ast::PatKind::Paren(ref p) => { @@ -226,7 +226,7 @@ impl Rewrite for Pat { PatKind::Path(ref q_self, ref path) => { rewrite_path(context, PathContext::Expr, q_self.as_ref(), path, shape) } - PatKind::TupleStruct(ref path, ref pat_vec) => { + PatKind::TupleStruct(_, ref path, ref pat_vec) => { let path_str = rewrite_path(context, PathContext::Expr, None, path, shape)?; rewrite_tuple_pat(pat_vec, Some(path_str), self.span, context, shape) } @@ -244,7 +244,7 @@ impl Rewrite for Pat { .collect(); Some(format!("[{}]", rw.join(", "))) } - PatKind::Struct(ref path, ref fields, ellipsis) => { + PatKind::Struct(_, ref path, ref fields, ellipsis) => { rewrite_struct_pat(path, fields, ellipsis, self.span, context, shape) } PatKind::MacCall(ref mac) => {