Add support for using qualified paths with structs in expression and pattern
position.
This commit is contained in:
parent
c5fbcd35a8
commit
6936349233
38 changed files with 374 additions and 187 deletions
|
@ -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,
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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(),
|
||||
|
|
|
@ -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(),
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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,
|
||||
) {
|
||||
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) => {
|
||||
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) => {
|
||||
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(
|
||||
|
|
|
@ -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))
|
||||
|
|
|
@ -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
|
||||
// -------------------------------------------------------------------------
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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)) => {
|
||||
|
|
|
@ -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))
|
||||
}
|
||||
|
|
|
@ -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))
|
||||
if qself.is_some() {
|
||||
self.sess.gated_spans.gate(sym::more_qualified_paths, path.span);
|
||||
}
|
||||
|
||||
/// 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)
|
||||
Ok(PatKind::TupleStruct(qself, path, fields))
|
||||
}
|
||||
|
||||
/// Parses the fields of a struct-like pattern.
|
||||
|
|
|
@ -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())
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -756,6 +756,7 @@ symbols! {
|
|||
modifiers,
|
||||
module,
|
||||
module_path,
|
||||
more_qualified_paths,
|
||||
more_struct_aliases,
|
||||
movbe_target_feature,
|
||||
move_ref_pattern,
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
```
|
|
@ -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),
|
||||
});
|
||||
iter_exprs(depth - 1, &mut |e| g(
|
||||
ExprKind::Closure(CaptureBy::Value,
|
||||
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)));
|
||||
},
|
||||
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,
|
||||
assert!(
|
||||
text1 == text2,
|
||||
"exprs are not equal:\n e = {:?}\n parsed = {:?}",
|
||||
text1, text2);
|
||||
text1,
|
||||
text2
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
4
src/test/ui/associated-types/associated-type-macro.rs
Normal file
4
src/test/ui/associated-types/associated-type-macro.rs
Normal file
|
@ -0,0 +1,4 @@
|
|||
fn main() {
|
||||
#[cfg(FALSE)]
|
||||
<() as module>::mac!(); //~ ERROR macros cannot use qualified paths
|
||||
}
|
|
@ -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
|
||||
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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`.
|
|
@ -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)
|
||||
}
|
|
@ -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`.
|
|
@ -1,7 +0,0 @@
|
|||
fn main() {
|
||||
match 10 {
|
||||
<T as Trait>::Type{key: value} => (),
|
||||
//~^ ERROR unexpected `{` after qualified path
|
||||
_ => (),
|
||||
}
|
||||
}
|
|
@ -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
|
||||
|
|
@ -1,7 +0,0 @@
|
|||
fn main() {
|
||||
match 10 {
|
||||
<T as Trait>::Type(2) => (),
|
||||
//~^ ERROR unexpected `(` after qualified path
|
||||
_ => (),
|
||||
}
|
||||
}
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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)
|
||||
})
|
||||
|
|
|
@ -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))
|
||||
},
|
||||
|
|
|
@ -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) => {
|
||||
|
|
|
@ -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) => {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue