Skip to content

Commit dc39c31

Browse files
committed
Auto merge of #46788 - petrochenkov:assocrecov, r=estebank
syntax: recovery for incorrect associated item paths like `[T; N]::clone` cc #44970 Fixes #42187 r? @estebank
2 parents 3cc68ba + 70e5c37 commit dc39c31

File tree

9 files changed

+357
-20
lines changed

9 files changed

+357
-20
lines changed

src/libsyntax/ast.rs

+94
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ use syntax_pos::{Span, DUMMY_SP};
2020
use codemap::{respan, Spanned};
2121
use abi::Abi;
2222
use ext::hygiene::{Mark, SyntaxContext};
23+
use parse::parser::{RecoverQPath, PathStyle};
2324
use print::pprust;
2425
use ptr::P;
2526
use rustc_data_structures::indexed_vec;
@@ -519,6 +520,38 @@ impl Pat {
519520
}
520521
}
521522

523+
impl RecoverQPath for Pat {
524+
fn to_ty(&self) -> Option<P<Ty>> {
525+
let node = match &self.node {
526+
PatKind::Wild => TyKind::Infer,
527+
PatKind::Ident(BindingMode::ByValue(Mutability::Immutable), ident, None) =>
528+
TyKind::Path(None, Path::from_ident(ident.span, ident.node)),
529+
PatKind::Path(qself, path) => TyKind::Path(qself.clone(), path.clone()),
530+
PatKind::Mac(mac) => TyKind::Mac(mac.clone()),
531+
PatKind::Ref(pat, mutbl) =>
532+
pat.to_ty().map(|ty| TyKind::Rptr(None, MutTy { ty, mutbl: *mutbl }))?,
533+
PatKind::Slice(pats, None, _) if pats.len() == 1 =>
534+
pats[0].to_ty().map(TyKind::Slice)?,
535+
PatKind::Tuple(pats, None) => {
536+
let mut tys = Vec::new();
537+
for pat in pats {
538+
tys.push(pat.to_ty()?);
539+
}
540+
TyKind::Tup(tys)
541+
}
542+
_ => return None,
543+
};
544+
545+
Some(P(Ty { node, id: self.id, span: self.span }))
546+
}
547+
fn to_recovered(&self, qself: Option<QSelf>, path: Path) -> Self {
548+
Self { span: path.span, node: PatKind::Path(qself, path), id: self.id }
549+
}
550+
fn to_string(&self) -> String {
551+
pprust::pat_to_string(self)
552+
}
553+
}
554+
522555
/// A single field in a struct pattern
523556
///
524557
/// Patterns like the fields of Foo `{ x, ref y, ref mut z }`
@@ -877,6 +910,54 @@ impl Expr {
877910
true
878911
}
879912
}
913+
914+
fn to_bound(&self) -> Option<TyParamBound> {
915+
match &self.node {
916+
ExprKind::Path(None, path) =>
917+
Some(TraitTyParamBound(PolyTraitRef::new(Vec::new(), path.clone(), self.span),
918+
TraitBoundModifier::None)),
919+
_ => None,
920+
}
921+
}
922+
}
923+
924+
impl RecoverQPath for Expr {
925+
fn to_ty(&self) -> Option<P<Ty>> {
926+
let node = match &self.node {
927+
ExprKind::Path(qself, path) => TyKind::Path(qself.clone(), path.clone()),
928+
ExprKind::Mac(mac) => TyKind::Mac(mac.clone()),
929+
ExprKind::Paren(expr) => expr.to_ty().map(TyKind::Paren)?,
930+
ExprKind::AddrOf(mutbl, expr) =>
931+
expr.to_ty().map(|ty| TyKind::Rptr(None, MutTy { ty, mutbl: *mutbl }))?,
932+
ExprKind::Repeat(expr, expr_len) =>
933+
expr.to_ty().map(|ty| TyKind::Array(ty, expr_len.clone()))?,
934+
ExprKind::Array(exprs) if exprs.len() == 1 =>
935+
exprs[0].to_ty().map(TyKind::Slice)?,
936+
ExprKind::Tup(exprs) => {
937+
let mut tys = Vec::new();
938+
for expr in exprs {
939+
tys.push(expr.to_ty()?);
940+
}
941+
TyKind::Tup(tys)
942+
}
943+
ExprKind::Binary(binop, lhs, rhs) if binop.node == BinOpKind::Add =>
944+
if let (Some(lhs), Some(rhs)) = (lhs.to_bound(), rhs.to_bound()) {
945+
TyKind::TraitObject(vec![lhs, rhs], TraitObjectSyntax::None)
946+
} else {
947+
return None;
948+
}
949+
_ => return None,
950+
};
951+
952+
Some(P(Ty { node, id: self.id, span: self.span }))
953+
}
954+
fn to_recovered(&self, qself: Option<QSelf>, path: Path) -> Self {
955+
Self { span: path.span, node: ExprKind::Path(qself, path),
956+
id: self.id, attrs: self.attrs.clone() }
957+
}
958+
fn to_string(&self) -> String {
959+
pprust::expr_to_string(self)
960+
}
880961
}
881962

882963
impl fmt::Debug for Expr {
@@ -1388,6 +1469,19 @@ pub struct Ty {
13881469
pub span: Span,
13891470
}
13901471

1472+
impl RecoverQPath for Ty {
1473+
fn to_ty(&self) -> Option<P<Ty>> {
1474+
Some(P(self.clone()))
1475+
}
1476+
fn to_recovered(&self, qself: Option<QSelf>, path: Path) -> Self {
1477+
Self { span: path.span, node: TyKind::Path(qself, path), id: self.id }
1478+
}
1479+
fn to_string(&self) -> String {
1480+
pprust::ty_to_string(self)
1481+
}
1482+
const PATH_STYLE: PathStyle = PathStyle::Type;
1483+
}
1484+
13911485
impl fmt::Debug for Ty {
13921486
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
13931487
write!(f, "type({})", pprust::ty_to_string(self))

src/libsyntax/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222

2323
#![feature(unicode)]
2424
#![feature(rustc_diagnostic_macros)]
25+
#![feature(match_default_bindings)]
2526
#![feature(i128_type)]
2627

2728
// See librustc_cratesio_shim/Cargo.toml for a comment explaining this.

src/libsyntax/parse/parser.rs

+50-20
Original file line numberDiff line numberDiff line change
@@ -169,6 +169,13 @@ enum PrevTokenKind {
169169
Other,
170170
}
171171

172+
pub(crate) trait RecoverQPath: Sized {
173+
fn to_ty(&self) -> Option<P<Ty>>;
174+
fn to_recovered(&self, qself: Option<QSelf>, path: ast::Path) -> Self;
175+
fn to_string(&self) -> String;
176+
const PATH_STYLE: PathStyle = PathStyle::Expr;
177+
}
178+
172179
/* ident is handled by common.rs */
173180

174181
#[derive(Clone)]
@@ -1567,6 +1574,7 @@ impl<'a> Parser<'a> {
15671574

15681575
// Try to recover from use of `+` with incorrect priority.
15691576
self.maybe_recover_from_bad_type_plus(allow_plus, &ty)?;
1577+
let ty = self.maybe_recover_from_bad_qpath(ty)?;
15701578

15711579
Ok(P(ty))
15721580
}
@@ -1621,6 +1629,32 @@ impl<'a> Parser<'a> {
16211629
Ok(())
16221630
}
16231631

1632+
// Try to recover from associated item paths like `[T]::AssocItem`/`(T, U)::AssocItem`.
1633+
fn maybe_recover_from_bad_qpath<T: RecoverQPath>(&mut self, base: T) -> PResult<'a, T> {
1634+
// Do not add `::` to expected tokens.
1635+
if self.token != token::ModSep {
1636+
return Ok(base);
1637+
}
1638+
let ty = match base.to_ty() {
1639+
Some(ty) => ty,
1640+
None => return Ok(base),
1641+
};
1642+
1643+
self.bump(); // `::`
1644+
let mut segments = Vec::new();
1645+
self.parse_path_segments(&mut segments, T::PATH_STYLE, true)?;
1646+
1647+
let span = ty.span.to(self.prev_span);
1648+
let recovered =
1649+
base.to_recovered(Some(QSelf { ty, position: 0 }), ast::Path { segments, span });
1650+
1651+
self.diagnostic()
1652+
.struct_span_err(span, "missing angle brackets in associated item path")
1653+
.span_suggestion(span, "try", recovered.to_string()).emit();
1654+
1655+
Ok(recovered)
1656+
}
1657+
16241658
fn parse_borrowed_pointee(&mut self) -> PResult<'a, TyKind> {
16251659
let opt_lifetime = if self.check_lifetime() { Some(self.expect_lifetime()) } else { None };
16261660
let mutbl = self.parse_mutability();
@@ -2012,12 +2046,7 @@ impl<'a> Parser<'a> {
20122046
}
20132047

20142048
pub fn mk_expr(&mut self, span: Span, node: ExprKind, attrs: ThinVec<Attribute>) -> P<Expr> {
2015-
P(Expr {
2016-
id: ast::DUMMY_NODE_ID,
2017-
node,
2018-
span,
2019-
attrs: attrs.into(),
2020-
})
2049+
P(Expr { node, span, attrs, id: ast::DUMMY_NODE_ID })
20212050
}
20222051

20232052
pub fn mk_unary(&mut self, unop: ast::UnOp, expr: P<Expr>) -> ast::ExprKind {
@@ -2139,12 +2168,11 @@ impl<'a> Parser<'a> {
21392168
self.bump();
21402169

21412170
hi = self.prev_span;
2142-
let span = lo.to(hi);
2143-
return if es.len() == 1 && !trailing_comma {
2144-
Ok(self.mk_expr(span, ExprKind::Paren(es.into_iter().nth(0).unwrap()), attrs))
2171+
ex = if es.len() == 1 && !trailing_comma {
2172+
ExprKind::Paren(es.into_iter().nth(0).unwrap())
21452173
} else {
2146-
Ok(self.mk_expr(span, ExprKind::Tup(es), attrs))
2147-
}
2174+
ExprKind::Tup(es)
2175+
};
21482176
}
21492177
token::OpenDelim(token::Brace) => {
21502178
return self.parse_block_expr(lo, BlockCheckMode::Default, attrs);
@@ -2344,7 +2372,10 @@ impl<'a> Parser<'a> {
23442372
}
23452373
}
23462374

2347-
return Ok(self.mk_expr(lo.to(hi), ex, attrs));
2375+
let expr = Expr { node: ex, span: lo.to(hi), id: ast::DUMMY_NODE_ID, attrs };
2376+
let expr = self.maybe_recover_from_bad_qpath(expr)?;
2377+
2378+
return Ok(P(expr));
23482379
}
23492380

23502381
fn parse_struct_expr(&mut self, lo: Span, pth: ast::Path, mut attrs: ThinVec<Attribute>)
@@ -3405,7 +3436,7 @@ impl<'a> Parser<'a> {
34053436

34063437
if self.check(&token::Comma) ||
34073438
self.check(&token::CloseDelim(token::Bracket)) {
3408-
slice = Some(P(ast::Pat {
3439+
slice = Some(P(Pat {
34093440
id: ast::DUMMY_NODE_ID,
34103441
node: PatKind::Wild,
34113442
span: self.span,
@@ -3492,14 +3523,14 @@ impl<'a> Parser<'a> {
34923523
(false, false) => BindingMode::ByValue(Mutability::Immutable),
34933524
};
34943525
let fieldpath = codemap::Spanned{span:self.prev_span, node:fieldname};
3495-
let fieldpat = P(ast::Pat{
3526+
let fieldpat = P(Pat {
34963527
id: ast::DUMMY_NODE_ID,
34973528
node: PatKind::Ident(bind_type, fieldpath, None),
34983529
span: boxed_span.to(hi),
34993530
});
35003531

35013532
let subpat = if is_box {
3502-
P(ast::Pat{
3533+
P(Pat {
35033534
id: ast::DUMMY_NODE_ID,
35043535
node: PatKind::Box(fieldpat),
35053536
span: lo.to(hi),
@@ -3708,11 +3739,10 @@ impl<'a> Parser<'a> {
37083739
}
37093740
}
37103741

3711-
Ok(P(ast::Pat {
3712-
id: ast::DUMMY_NODE_ID,
3713-
node: pat,
3714-
span: lo.to(self.prev_span),
3715-
}))
3742+
let pat = Pat { node: pat, span: lo.to(self.prev_span), id: ast::DUMMY_NODE_ID };
3743+
let pat = self.maybe_recover_from_bad_qpath(pat)?;
3744+
3745+
Ok(P(pat))
37163746
}
37173747

37183748
/// Parse ident or ident @ pat
+24
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
fn main() {
12+
let a = [1, 2, 3, 4];
13+
[i32; 4]::clone(&a);
14+
//~^ ERROR missing angle brackets in associated item path
15+
16+
[i32]::as_ref(&a);
17+
//~^ ERROR missing angle brackets in associated item path
18+
19+
(u8)::clone(&0);
20+
//~^ ERROR missing angle brackets in associated item path
21+
22+
(u8, u8)::clone(&(0, 0));
23+
//~^ ERROR missing angle brackets in associated item path
24+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
error: missing angle brackets in associated item path
2+
--> $DIR/bad-assoc-expr.rs:13:5
3+
|
4+
13 | [i32; 4]::clone(&a);
5+
| ^^^^^^^^^^^^^^^ help: try: `<[i32; 4]>::clone`
6+
7+
error: missing angle brackets in associated item path
8+
--> $DIR/bad-assoc-expr.rs:16:5
9+
|
10+
16 | [i32]::as_ref(&a);
11+
| ^^^^^^^^^^^^^ help: try: `<[i32]>::as_ref`
12+
13+
error: missing angle brackets in associated item path
14+
--> $DIR/bad-assoc-expr.rs:19:5
15+
|
16+
19 | (u8)::clone(&0);
17+
| ^^^^^^^^^^^ help: try: `<(u8)>::clone`
18+
19+
error: missing angle brackets in associated item path
20+
--> $DIR/bad-assoc-expr.rs:22:5
21+
|
22+
22 | (u8, u8)::clone(&(0, 0));
23+
| ^^^^^^^^^^^^^^^ help: try: `<(u8, u8)>::clone`
24+
25+
error: aborting due to 4 previous errors
26+
+23
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
fn main() {
12+
match 0u8 {
13+
[u8]::AssocItem => {}
14+
//~^ ERROR missing angle brackets in associated item path
15+
//~| ERROR no associated item named `AssocItem` found for type `[u8]` in the current scope
16+
(u8, u8)::AssocItem => {}
17+
//~^ ERROR missing angle brackets in associated item path
18+
//~| ERROR no associated item named `AssocItem` found for type `(u8, u8)` in the current sco
19+
_::AssocItem => {}
20+
//~^ ERROR missing angle brackets in associated item path
21+
//~| ERROR no associated item named `AssocItem` found for type `_` in the current scope
22+
}
23+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
error: missing angle brackets in associated item path
2+
--> $DIR/bad-assoc-pat.rs:13:9
3+
|
4+
13 | [u8]::AssocItem => {}
5+
| ^^^^^^^^^^^^^^^ help: try: `<[u8]>::AssocItem`
6+
7+
error: missing angle brackets in associated item path
8+
--> $DIR/bad-assoc-pat.rs:16:9
9+
|
10+
16 | (u8, u8)::AssocItem => {}
11+
| ^^^^^^^^^^^^^^^^^^^ help: try: `<(u8, u8)>::AssocItem`
12+
13+
error: missing angle brackets in associated item path
14+
--> $DIR/bad-assoc-pat.rs:19:9
15+
|
16+
19 | _::AssocItem => {}
17+
| ^^^^^^^^^^^^ help: try: `<_>::AssocItem`
18+
19+
error[E0599]: no associated item named `AssocItem` found for type `[u8]` in the current scope
20+
--> $DIR/bad-assoc-pat.rs:13:9
21+
|
22+
13 | [u8]::AssocItem => {}
23+
| ^^^^^^^^^^^^^^^ associated item not found in `[u8]`
24+
25+
error[E0599]: no associated item named `AssocItem` found for type `(u8, u8)` in the current scope
26+
--> $DIR/bad-assoc-pat.rs:16:9
27+
|
28+
16 | (u8, u8)::AssocItem => {}
29+
| ^^^^^^^^^^^^^^^^^^^ associated item not found in `(u8, u8)`
30+
31+
error[E0599]: no associated item named `AssocItem` found for type `_` in the current scope
32+
--> $DIR/bad-assoc-pat.rs:19:9
33+
|
34+
19 | _::AssocItem => {}
35+
| ^^^^^^^^^^^^ associated item not found in `_`
36+
37+
error: aborting due to 6 previous errors
38+

0 commit comments

Comments
 (0)