Skip to content

Commit

Permalink
Recover on const impl<> X for Y
Browse files Browse the repository at this point in the history
  • Loading branch information
coolreader18 committed Dec 12, 2020
1 parent b4def89 commit 1e27b65
Show file tree
Hide file tree
Showing 5 changed files with 99 additions and 3 deletions.
41 changes: 38 additions & 3 deletions compiler/rustc_parse/src/parser/item.rs
Expand Up @@ -247,9 +247,14 @@ impl<'a> Parser<'a> {
(ident, ItemKind::Static(ty, m, expr))
} else if let Const::Yes(const_span) = self.parse_constness() {
// CONST ITEM
self.recover_const_mut(const_span);
let (ident, ty, expr) = self.parse_item_global(None)?;
(ident, ItemKind::Const(def(), ty, expr))
if self.token.is_keyword(kw::Impl) {
// recover from `const impl`, suggest `impl const`
self.recover_const_impl(const_span, attrs, def())?
} else {
self.recover_const_mut(const_span);
let (ident, ty, expr) = self.parse_item_global(None)?;
(ident, ItemKind::Const(def(), ty, expr))
}
} else if self.check_keyword(kw::Trait) || self.check_auto_or_unsafe_trait_item() {
// TRAIT ITEM
self.parse_item_trait(attrs, lo)?
Expand Down Expand Up @@ -988,6 +993,36 @@ impl<'a> Parser<'a> {
}
}

/// Recover on `const impl` with `const` already eaten.
fn recover_const_impl(
&mut self,
const_span: Span,
attrs: &mut Vec<Attribute>,
defaultness: Defaultness,
) -> PResult<'a, ItemInfo> {
let impl_span = self.token.span;
let mut err = self.expected_ident_found();
let mut impl_info = self.parse_item_impl(attrs, defaultness)?;
match impl_info.1 {
// only try to recover if this is implementing a trait for a type
ItemKind::Impl { of_trait: Some(ref trai), ref mut constness, .. } => {
*constness = Const::Yes(const_span);

let before_trait = trai.path.span.shrink_to_lo();
let const_up_to_impl = const_span.with_hi(impl_span.lo());
err.multipart_suggestion(
"you might have meant to write a const trait impl",
vec![(const_up_to_impl, "".to_owned()), (before_trait, "const ".to_owned())],
Applicability::MaybeIncorrect,
)
.emit();
}
ItemKind::Impl { .. } => return Err(err),
_ => unreachable!(),
}
Ok(impl_info)
}

/// Parse `["const" | ("static" "mut"?)] $ident ":" $ty (= $expr)?` with
/// `["const" | ("static" "mut"?)]` already parsed and stored in `m`.
///
Expand Down
13 changes: 13 additions & 0 deletions src/test/ui/rfc-2632-const-trait-impl/const-impl-norecover.rs
@@ -0,0 +1,13 @@
#![feature(const_trait_impl)]
#![allow(incomplete_features)]

struct Foo;

const impl Foo { //~ ERROR: expected identifier, found keyword
fn bar() {}
}

fn main() {
// shouldn't error here because we shouldn't have been able to recover above
Foo::bar();
}
@@ -0,0 +1,8 @@
error: expected identifier, found keyword `impl`
--> $DIR/const-impl-norecover.rs:6:7
|
LL | const impl Foo {
| ^^^^ expected identifier, found keyword

error: aborting due to previous error

16 changes: 16 additions & 0 deletions src/test/ui/rfc-2632-const-trait-impl/const-impl-recovery.rs
@@ -0,0 +1,16 @@
#![feature(const_trait_impl)]
#![allow(incomplete_features)]

trait Foo {}

const impl Foo for i32 {} //~ ERROR: expected identifier, found keyword

trait Bar {}

const impl<T: Foo> Bar for T {} //~ ERROR: expected identifier, found keyword

const fn still_implements<T: Bar>() {}

const _: () = still_implements::<i32>();

fn main() {}
24 changes: 24 additions & 0 deletions src/test/ui/rfc-2632-const-trait-impl/const-impl-recovery.stderr
@@ -0,0 +1,24 @@
error: expected identifier, found keyword `impl`
--> $DIR/const-impl-recovery.rs:6:7
|
LL | const impl Foo for i32 {}
| ^^^^ expected identifier, found keyword
|
help: you might have meant to write a const trait impl
|
LL | impl const Foo for i32 {}
|-- ^^^^^

error: expected identifier, found keyword `impl`
--> $DIR/const-impl-recovery.rs:10:7
|
LL | const impl<T: Foo> Bar for T {}
| ^^^^ expected identifier, found keyword
|
help: you might have meant to write a const trait impl
|
LL | impl<T: Foo> const Bar for T {}
|-- ^^^^^

error: aborting due to 2 previous errors

0 comments on commit 1e27b65

Please sign in to comment.