Skip to content

Commit

Permalink
Rollup merge of rust-lang#46827 - petrochenkov:assocrecov2, r=estebank
Browse files Browse the repository at this point in the history
syntax: Follow-up to the incorrect qpath recovery PR

cc rust-lang#46788

Add tests checking that "priority" of qpath recovery is higher than priority of unary and binary operators
Fix regressed parsing of paths with fn-like generic arguments
r? @estebank
  • Loading branch information
GuillaumeGomez authored Dec 21, 2017
2 parents ab7abfc + d333752 commit 2917ac6
Show file tree
Hide file tree
Showing 8 changed files with 165 additions and 70 deletions.
81 changes: 25 additions & 56 deletions src/libsyntax/ast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@ use syntax_pos::{Span, DUMMY_SP};
use codemap::{respan, Spanned};
use abi::Abi;
use ext::hygiene::{Mark, SyntaxContext};
use parse::parser::{RecoverQPath, PathStyle};
use print::pprust;
use ptr::P;
use rustc_data_structures::indexed_vec;
Expand Down Expand Up @@ -485,6 +484,30 @@ impl fmt::Debug for Pat {
}

impl Pat {
pub(super) fn to_ty(&self) -> Option<P<Ty>> {
let node = match &self.node {
PatKind::Wild => TyKind::Infer,
PatKind::Ident(BindingMode::ByValue(Mutability::Immutable), ident, None) =>
TyKind::Path(None, Path::from_ident(ident.span, ident.node)),
PatKind::Path(qself, path) => TyKind::Path(qself.clone(), path.clone()),
PatKind::Mac(mac) => TyKind::Mac(mac.clone()),
PatKind::Ref(pat, mutbl) =>
pat.to_ty().map(|ty| TyKind::Rptr(None, MutTy { ty, mutbl: *mutbl }))?,
PatKind::Slice(pats, None, _) if pats.len() == 1 =>
pats[0].to_ty().map(TyKind::Slice)?,
PatKind::Tuple(pats, None) => {
let mut tys = Vec::new();
for pat in pats {
tys.push(pat.to_ty()?);
}
TyKind::Tup(tys)
}
_ => return None,
};

Some(P(Ty { node, id: self.id, span: self.span }))
}

pub fn walk<F>(&self, it: &mut F) -> bool
where F: FnMut(&Pat) -> bool
{
Expand Down Expand Up @@ -520,38 +543,6 @@ impl Pat {
}
}

impl RecoverQPath for Pat {
fn to_ty(&self) -> Option<P<Ty>> {
let node = match &self.node {
PatKind::Wild => TyKind::Infer,
PatKind::Ident(BindingMode::ByValue(Mutability::Immutable), ident, None) =>
TyKind::Path(None, Path::from_ident(ident.span, ident.node)),
PatKind::Path(qself, path) => TyKind::Path(qself.clone(), path.clone()),
PatKind::Mac(mac) => TyKind::Mac(mac.clone()),
PatKind::Ref(pat, mutbl) =>
pat.to_ty().map(|ty| TyKind::Rptr(None, MutTy { ty, mutbl: *mutbl }))?,
PatKind::Slice(pats, None, _) if pats.len() == 1 =>
pats[0].to_ty().map(TyKind::Slice)?,
PatKind::Tuple(pats, None) => {
let mut tys = Vec::new();
for pat in pats {
tys.push(pat.to_ty()?);
}
TyKind::Tup(tys)
}
_ => return None,
};

Some(P(Ty { node, id: self.id, span: self.span }))
}
fn to_recovered(&self, qself: Option<QSelf>, path: Path) -> Self {
Self { span: path.span, node: PatKind::Path(qself, path), id: self.id }
}
fn to_string(&self) -> String {
pprust::pat_to_string(self)
}
}

/// A single field in a struct pattern
///
/// Patterns like the fields of Foo `{ x, ref y, ref mut z }`
Expand Down Expand Up @@ -919,10 +910,8 @@ impl Expr {
_ => None,
}
}
}

impl RecoverQPath for Expr {
fn to_ty(&self) -> Option<P<Ty>> {
pub(super) fn to_ty(&self) -> Option<P<Ty>> {
let node = match &self.node {
ExprKind::Path(qself, path) => TyKind::Path(qself.clone(), path.clone()),
ExprKind::Mac(mac) => TyKind::Mac(mac.clone()),
Expand Down Expand Up @@ -951,13 +940,6 @@ impl RecoverQPath for Expr {

Some(P(Ty { node, id: self.id, span: self.span }))
}
fn to_recovered(&self, qself: Option<QSelf>, path: Path) -> Self {
Self { span: path.span, node: ExprKind::Path(qself, path),
id: self.id, attrs: self.attrs.clone() }
}
fn to_string(&self) -> String {
pprust::expr_to_string(self)
}
}

impl fmt::Debug for Expr {
Expand Down Expand Up @@ -1469,19 +1451,6 @@ pub struct Ty {
pub span: Span,
}

impl RecoverQPath for Ty {
fn to_ty(&self) -> Option<P<Ty>> {
Some(P(self.clone()))
}
fn to_recovered(&self, qself: Option<QSelf>, path: Path) -> Self {
Self { span: path.span, node: TyKind::Path(qself, path), id: self.id }
}
fn to_string(&self) -> String {
pprust::ty_to_string(self)
}
const PATH_STYLE: PathStyle = PathStyle::Type;
}

impl fmt::Debug for Ty {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "type({})", pprust::ty_to_string(self))
Expand Down
62 changes: 51 additions & 11 deletions src/libsyntax/parse/parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -169,11 +169,49 @@ enum PrevTokenKind {
Other,
}

pub(crate) trait RecoverQPath: Sized {
trait RecoverQPath: Sized {
const PATH_STYLE: PathStyle = PathStyle::Expr;
fn to_ty(&self) -> Option<P<Ty>>;
fn to_recovered(&self, qself: Option<QSelf>, path: ast::Path) -> Self;
fn to_string(&self) -> String;
const PATH_STYLE: PathStyle = PathStyle::Expr;
}

impl RecoverQPath for Ty {
const PATH_STYLE: PathStyle = PathStyle::Type;
fn to_ty(&self) -> Option<P<Ty>> {
Some(P(self.clone()))
}
fn to_recovered(&self, qself: Option<QSelf>, path: ast::Path) -> Self {
Self { span: path.span, node: TyKind::Path(qself, path), id: self.id }
}
fn to_string(&self) -> String {
pprust::ty_to_string(self)
}
}

impl RecoverQPath for Pat {
fn to_ty(&self) -> Option<P<Ty>> {
self.to_ty()
}
fn to_recovered(&self, qself: Option<QSelf>, path: ast::Path) -> Self {
Self { span: path.span, node: PatKind::Path(qself, path), id: self.id }
}
fn to_string(&self) -> String {
pprust::pat_to_string(self)
}
}

impl RecoverQPath for Expr {
fn to_ty(&self) -> Option<P<Ty>> {
self.to_ty()
}
fn to_recovered(&self, qself: Option<QSelf>, path: ast::Path) -> Self {
Self { span: path.span, node: ExprKind::Path(qself, path),
id: self.id, attrs: self.attrs.clone() }
}
fn to_string(&self) -> String {
pprust::expr_to_string(self)
}
}

/* ident is handled by common.rs */
Expand Down Expand Up @@ -1432,7 +1470,7 @@ impl<'a> Parser<'a> {

// Parse a type
pub fn parse_ty(&mut self) -> PResult<'a, P<Ty>> {
self.parse_ty_common(true)
self.parse_ty_common(true, true)
}

/// Parse a type in restricted contexts where `+` is not permitted.
Expand All @@ -1441,10 +1479,11 @@ impl<'a> Parser<'a> {
/// Example 2: `value1 as TYPE + value2`
/// `+` is prohibited to avoid interactions with expression grammar.
fn parse_ty_no_plus(&mut self) -> PResult<'a, P<Ty>> {
self.parse_ty_common(false)
self.parse_ty_common(false, true)
}

fn parse_ty_common(&mut self, allow_plus: bool) -> PResult<'a, P<Ty>> {
fn parse_ty_common(&mut self, allow_plus: bool, allow_qpath_recovery: bool)
-> PResult<'a, P<Ty>> {
maybe_whole!(self, NtTy, |x| x);

let lo = self.span;
Expand Down Expand Up @@ -1577,7 +1616,7 @@ impl<'a> Parser<'a> {

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

Ok(P(ty))
}
Expand Down Expand Up @@ -1633,9 +1672,10 @@ impl<'a> Parser<'a> {
}

// Try to recover from associated item paths like `[T]::AssocItem`/`(T, U)::AssocItem`.
fn maybe_recover_from_bad_qpath<T: RecoverQPath>(&mut self, base: T) -> PResult<'a, T> {
fn maybe_recover_from_bad_qpath<T: RecoverQPath>(&mut self, base: T, allow_recovery: bool)
-> PResult<'a, T> {
// Do not add `::` to expected tokens.
if self.token != token::ModSep {
if !allow_recovery || self.token != token::ModSep {
return Ok(base);
}
let ty = match base.to_ty() {
Expand Down Expand Up @@ -1969,7 +2009,7 @@ impl<'a> Parser<'a> {
|p| p.parse_ty())?;
self.bump(); // `)`
let output = if self.eat(&token::RArrow) {
Some(self.parse_ty_no_plus()?)
Some(self.parse_ty_common(false, false)?)
} else {
None
};
Expand Down Expand Up @@ -2376,7 +2416,7 @@ impl<'a> Parser<'a> {
}

let expr = Expr { node: ex, span: lo.to(hi), id: ast::DUMMY_NODE_ID, attrs };
let expr = self.maybe_recover_from_bad_qpath(expr)?;
let expr = self.maybe_recover_from_bad_qpath(expr, true)?;

return Ok(P(expr));
}
Expand Down Expand Up @@ -3743,7 +3783,7 @@ impl<'a> Parser<'a> {
}

let pat = Pat { node: pat, span: lo.to(self.prev_span), id: ast::DUMMY_NODE_ID };
let pat = self.maybe_recover_from_bad_qpath(pat)?;
let pat = self.maybe_recover_from_bad_qpath(pat, true)?;

Ok(P(pat))
}
Expand Down
6 changes: 6 additions & 0 deletions src/test/ui/did_you_mean/bad-assoc-expr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,4 +21,10 @@ fn main() {

(u8, u8)::clone(&(0, 0));
//~^ ERROR missing angle brackets in associated item path

&(u8)::clone(&0);
//~^ ERROR missing angle brackets in associated item path

10 + (u8)::clone(&0);
//~^ ERROR missing angle brackets in associated item path
}
14 changes: 13 additions & 1 deletion src/test/ui/did_you_mean/bad-assoc-expr.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -22,5 +22,17 @@ error: missing angle brackets in associated item path
22 | (u8, u8)::clone(&(0, 0));
| ^^^^^^^^^^^^^^^ help: try: `<(u8, u8)>::clone`

error: aborting due to 4 previous errors
error: missing angle brackets in associated item path
--> $DIR/bad-assoc-expr.rs:25:6
|
25 | &(u8)::clone(&0);
| ^^^^^^^^^^^ help: try: `<(u8)>::clone`

error: missing angle brackets in associated item path
--> $DIR/bad-assoc-expr.rs:28:10
|
28 | 10 + (u8)::clone(&0);
| ^^^^^^^^^^^ help: try: `<(u8)>::clone`

error: aborting due to 6 previous errors

5 changes: 5 additions & 0 deletions src/test/ui/did_you_mean/bad-assoc-pat.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,4 +20,9 @@ fn main() {
//~^ ERROR missing angle brackets in associated item path
//~| ERROR no associated item named `AssocItem` found for type `_` in the current scope
}
match &0u8 {
&(u8,)::AssocItem => {}
//~^ ERROR missing angle brackets in associated item path
//~| ERROR no associated item named `AssocItem` found for type `(u8,)` in the current scope
}
}
14 changes: 13 additions & 1 deletion src/test/ui/did_you_mean/bad-assoc-pat.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,12 @@ error: missing angle brackets in associated item path
19 | _::AssocItem => {}
| ^^^^^^^^^^^^ help: try: `<_>::AssocItem`

error: missing angle brackets in associated item path
--> $DIR/bad-assoc-pat.rs:24:10
|
24 | &(u8,)::AssocItem => {}
| ^^^^^^^^^^^^^^^^ help: try: `<(u8,)>::AssocItem`

error[E0599]: no associated item named `AssocItem` found for type `[u8]` in the current scope
--> $DIR/bad-assoc-pat.rs:13:9
|
Expand All @@ -34,5 +40,11 @@ error[E0599]: no associated item named `AssocItem` found for type `_` in the cur
19 | _::AssocItem => {}
| ^^^^^^^^^^^^ associated item not found in `_`

error: aborting due to 6 previous errors
error[E0599]: no associated item named `AssocItem` found for type `(u8,)` in the current scope
--> $DIR/bad-assoc-pat.rs:24:10
|
24 | &(u8,)::AssocItem => {}
| ^^^^^^^^^^^^^^^^ associated item not found in `(u8,)`

error: aborting due to 8 previous errors

15 changes: 15 additions & 0 deletions src/test/ui/did_you_mean/bad-assoc-ty.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,4 +28,19 @@ type E = _::AssocTy;
//~^ ERROR missing angle brackets in associated item path
//~| ERROR the type placeholder `_` is not allowed within types on item signatures

type F = &'static (u8)::AssocTy;
//~^ ERROR missing angle brackets in associated item path
//~| ERROR ambiguous associated type

// Qualified paths cannot appear in bounds, so the recovery
// should apply to the whole sum and not `(Send)`.
type G = 'static + (Send)::AssocTy;
//~^ ERROR missing angle brackets in associated item path
//~| ERROR ambiguous associated type

// This is actually a legal path with fn-like generic arguments in the middle!
// Recovery should not apply in this context.
type H = Fn(u8) -> (u8)::Output;
//~^ ERROR ambiguous associated type

fn main() {}
38 changes: 37 additions & 1 deletion src/test/ui/did_you_mean/bad-assoc-ty.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,18 @@ error: missing angle brackets in associated item path
27 | type E = _::AssocTy;
| ^^^^^^^^^^ help: try: `<_>::AssocTy`

error: missing angle brackets in associated item path
--> $DIR/bad-assoc-ty.rs:31:19
|
31 | type F = &'static (u8)::AssocTy;
| ^^^^^^^^^^^^^ help: try: `<(u8)>::AssocTy`

error: missing angle brackets in associated item path
--> $DIR/bad-assoc-ty.rs:37:10
|
37 | type G = 'static + (Send)::AssocTy;
| ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `<'static + Send>::AssocTy`

error[E0223]: ambiguous associated type
--> $DIR/bad-assoc-ty.rs:11:10
|
Expand Down Expand Up @@ -66,5 +78,29 @@ error[E0121]: the type placeholder `_` is not allowed within types on item signa
27 | type E = _::AssocTy;
| ^ not allowed in type signatures

error: aborting due to 10 previous errors
error[E0223]: ambiguous associated type
--> $DIR/bad-assoc-ty.rs:31:19
|
31 | type F = &'static (u8)::AssocTy;
| ^^^^^^^^^^^^^ ambiguous associated type
|
= note: specify the type using the syntax `<u8 as Trait>::AssocTy`

error[E0223]: ambiguous associated type
--> $DIR/bad-assoc-ty.rs:37:10
|
37 | type G = 'static + (Send)::AssocTy;
| ^^^^^^^^^^^^^^^^^^^^^^^^^ ambiguous associated type
|
= note: specify the type using the syntax `<std::marker::Send + 'static as Trait>::AssocTy`

error[E0223]: ambiguous associated type
--> $DIR/bad-assoc-ty.rs:43:10
|
43 | type H = Fn(u8) -> (u8)::Output;
| ^^^^^^^^^^^^^^^^^^^^^^ ambiguous associated type
|
= note: specify the type using the syntax `<std::ops::Fn(u8) -> u8 + 'static as Trait>::Output`

error: aborting due to 15 previous errors

0 comments on commit 2917ac6

Please sign in to comment.