Add support for using qualified paths with structs in expression and pattern

position.
This commit is contained in:
Ryan Levick 2020-12-10 13:20:07 +01:00 committed by Ryan Levick
parent c5fbcd35a8
commit 6936349233
38 changed files with 374 additions and 187 deletions

View file

@ -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<PatField>, /* recovered */ bool),
Struct(Option<QSelf>, Path, Vec<PatField>, /* recovered */ bool),
/// A tuple struct/variant pattern (`Variant(x, y, .., z)`).
TupleStruct(Path, Vec<P<Pat>>),
TupleStruct(Option<QSelf>, Path, Vec<P<Pat>>),
/// 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<QSelf>,
pub path: Path,
pub fields: Vec<ExprField>,
pub rest: StructRest,

View file

@ -1139,7 +1139,8 @@ pub fn noop_visit_pat<T: MutVisitor>(pat: &mut P<Pat>, 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<T: MutVisitor>(pat: &mut P<Pat>, 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<T: MutVisitor>(
}
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 {

View file

@ -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 {

View file

@ -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<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()
@ -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(),

View file

@ -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(),

View file

@ -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");

View file

@ -1713,11 +1713,16 @@ impl<'a> State<'a> {
fn print_expr_struct(
&mut self,
qself: &Option<ast::QSelf>,
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(

View file

@ -275,7 +275,12 @@ impl<'a> ExtCtxt<'a> {
) -> P<ast::Expr> {
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<ast::Pat>>,
) -> P<ast::Pat> {
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<ast::PatField>,
) -> P<ast::Pat> {
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<ast::Pat>>) -> P<ast::Pat> {
self.pat(span, PatKind::Tuple(pats))

View file

@ -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
// -------------------------------------------------------------------------

View file

@ -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.

View file

@ -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"

View file

@ -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)) => {

View file

@ -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<Expr>> {
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<PResult<'a, P<Expr>>> {
@ -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<ast::QSelf>,
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))
}

View file

@ -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<QSelf>, 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<QSelf>, 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. `<Foo as Bar>::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.

View file

@ -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())

View file

@ -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);
}

View file

@ -756,6 +756,7 @@ symbols! {
modifiers,
module,
module_path,
more_qualified_paths,
more_struct_aliases,
movbe_target_feature,
move_ref_pattern,

View file

@ -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 <Foo as A>::Assoc { br } = StructStruct { br: 2 };
}
struct StructStruct {
br: i8,
}
struct Foo;
trait A {
type Assoc;
}
impl A for Foo {
type Assoc = StructStruct;
}
```

View file

@ -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<P<Expr>> {
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<Expr> {
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<Expr> {
@ -83,11 +73,13 @@ fn iter_exprs(depth: usize, f: &mut dyn FnMut(P<Expr>)) {
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<Expr>)) {
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<Expr>)) {
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
);
}
});
}

View file

@ -0,0 +1,11 @@
// check-pass
#![feature(destructuring_assignment)]
#![feature(more_qualified_paths)]
enum E { V() }
fn main() {
<E>::V() = E::V(); // OK, destructuring assignment
<E>::V {} = E::V(); // OK, destructuring assignment
}

View file

@ -0,0 +1,4 @@
fn main() {
#[cfg(FALSE)]
<() as module>::mac!(); //~ ERROR macros cannot use qualified paths
}

View file

@ -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

View file

@ -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 <Foo as A>::Assoc { br } = <Foo as A>::Assoc { br: 2 };
assert!(br == 2);
}
struct StructStruct {
br: i8,
}
struct Foo;
trait A {
type Assoc;
}
impl A for Foo {
type Assoc = StructStruct;
}

View file

@ -0,0 +1,24 @@
// Users cannot yet construct structs through associated types
// in both expressions and patterns
#![feature(more_qualified_paths)]
fn main() {
let <Foo as A>::Assoc(n) = <Foo as A>::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;
}

View file

@ -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 <Foo as A>::Assoc(n) = <Foo as A>::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 <Foo as A>::Assoc(n) = <Foo as A>::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`.

View file

@ -0,0 +1,27 @@
fn main() {
// destructure through a qualified path
let <Foo as A>::Assoc { br } = StructStruct { br: 2 };
//~^ ERROR usage of qualified paths in this context is experimental
let _ = <Foo as A>::Assoc { br: 2 };
//~^ ERROR usage of qualified paths in this context is experimental
let <E>::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)
}

View file

@ -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 <Foo as A>::Assoc { br } = StructStruct { br: 2 };
| ^^^^^^^^^^^^^^^^^
|
= note: see issue #80080 <https://github.com/rust-lang/rust/issues/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 _ = <Foo as A>::Assoc { br: 2 };
| ^^^^^^^^^^^^^^^^^
|
= note: see issue #80080 <https://github.com/rust-lang/rust/issues/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 <E>::V(..) = E::V(0);
| ^^^^^^
|
= note: see issue #80080 <https://github.com/rust-lang/rust/issues/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`.

View file

@ -1,7 +0,0 @@
fn main() {
match 10 {
<T as Trait>::Type{key: value} => (),
//~^ ERROR unexpected `{` after qualified path
_ => (),
}
}

View file

@ -1,10 +0,0 @@
error: unexpected `{` after qualified path
--> $DIR/brace-after-qualified-path-in-match.rs:3:27
|
LL | <T as Trait>::Type{key: value} => (),
| ------------------^ unexpected `{` after qualified path
| |
| the qualified path
error: aborting due to previous error

View file

@ -1,7 +0,0 @@
fn main() {
match 10 {
<T as Trait>::Type(2) => (),
//~^ ERROR unexpected `(` after qualified path
_ => (),
}
}

View file

@ -1,10 +0,0 @@
error: unexpected `(` after qualified path
--> $DIR/paren-after-qualified-path-in-match.rs:3:27
|
LL | <T as Trait>::Type(2) => (),
| ------------------^ unexpected `(` after qualified path
| |
| the qualified path
error: aborting due to previous error

View file

@ -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

View file

@ -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()

View file

@ -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);

View file

@ -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<P<Pat>>, 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<P<Pat>>, 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<ast::QSelf>,
path1: &ast::Path,
fps1: &mut Vec<ast::PatField>,
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)
})

View file

@ -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<QSelf>, r: &Option<QSelf>) -> 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))
},

View file

@ -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) => {

View file

@ -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) => {