Add parser recovery for const $ident = $expr;
.
Then use the diagnostics-stealing API to stash parser errors and enrich them with type information in typeck.
This commit is contained in:
parent
ae8b3e8fc6
commit
62d85849d0
4 changed files with 164 additions and 16 deletions
|
@ -46,7 +46,7 @@ use rustc::hir::intravisit::{self, NestedVisitorMap, Visitor};
|
||||||
use rustc::hir::GenericParamKind;
|
use rustc::hir::GenericParamKind;
|
||||||
use rustc::hir::{self, CodegenFnAttrFlags, CodegenFnAttrs, Unsafety};
|
use rustc::hir::{self, CodegenFnAttrFlags, CodegenFnAttrs, Unsafety};
|
||||||
|
|
||||||
use errors::{Applicability, DiagnosticId};
|
use errors::{Applicability, DiagnosticId, StashKey};
|
||||||
|
|
||||||
struct OnlySelfBounds(bool);
|
struct OnlySelfBounds(bool);
|
||||||
|
|
||||||
|
@ -1149,18 +1149,41 @@ fn infer_placeholder_type(
|
||||||
def_id: DefId,
|
def_id: DefId,
|
||||||
body_id: hir::BodyId,
|
body_id: hir::BodyId,
|
||||||
span: Span,
|
span: Span,
|
||||||
|
item_ident: Ident,
|
||||||
) -> Ty<'_> {
|
) -> Ty<'_> {
|
||||||
let ty = tcx.typeck_tables_of(def_id).node_type(body_id.hir_id);
|
let ty = tcx.typeck_tables_of(def_id).node_type(body_id.hir_id);
|
||||||
let mut diag = bad_placeholder_type(tcx, span);
|
|
||||||
if ty != tcx.types.err {
|
// If this came from a free `const` or `static mut?` item,
|
||||||
diag.span_suggestion(
|
// then the user may have written e.g. `const A = 42;`.
|
||||||
span,
|
// In this case, the parser has stashed a diagnostic for
|
||||||
"replace `_` with the correct type",
|
// us to improve in typeck so we do that now.
|
||||||
ty.to_string(),
|
match tcx.sess.diagnostic().steal_diagnostic(span, StashKey::ItemNoType) {
|
||||||
Applicability::MaybeIncorrect,
|
Some(mut err) => {
|
||||||
);
|
// The parser provided a sub-optimal `HasPlaceholders` suggestion for the type.
|
||||||
|
// We are typeck and have the real type, so remove that and suggest the actual type.
|
||||||
|
err.suggestions.clear();
|
||||||
|
err.span_suggestion(
|
||||||
|
span,
|
||||||
|
"provide a type for the item",
|
||||||
|
format!("{}: {}", item_ident, ty),
|
||||||
|
Applicability::MachineApplicable,
|
||||||
|
)
|
||||||
|
.emit();
|
||||||
|
}
|
||||||
|
None => {
|
||||||
|
let mut diag = bad_placeholder_type(tcx, span);
|
||||||
|
if ty != tcx.types.err {
|
||||||
|
diag.span_suggestion(
|
||||||
|
span,
|
||||||
|
"replace `_` with the correct type",
|
||||||
|
ty.to_string(),
|
||||||
|
Applicability::MaybeIncorrect,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
diag.emit();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
diag.emit();
|
|
||||||
ty
|
ty
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1192,7 +1215,7 @@ pub fn checked_type_of(tcx: TyCtxt<'_>, def_id: DefId, fail: bool) -> Option<Ty<
|
||||||
TraitItemKind::Const(ref ty, body_id) => {
|
TraitItemKind::Const(ref ty, body_id) => {
|
||||||
body_id.and_then(|body_id| {
|
body_id.and_then(|body_id| {
|
||||||
if let hir::TyKind::Infer = ty.node {
|
if let hir::TyKind::Infer = ty.node {
|
||||||
Some(infer_placeholder_type(tcx, def_id, body_id, ty.span))
|
Some(infer_placeholder_type(tcx, def_id, body_id, ty.span, item.ident))
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
@ -1214,7 +1237,7 @@ pub fn checked_type_of(tcx: TyCtxt<'_>, def_id: DefId, fail: bool) -> Option<Ty<
|
||||||
}
|
}
|
||||||
ImplItemKind::Const(ref ty, body_id) => {
|
ImplItemKind::Const(ref ty, body_id) => {
|
||||||
if let hir::TyKind::Infer = ty.node {
|
if let hir::TyKind::Infer = ty.node {
|
||||||
infer_placeholder_type(tcx, def_id, body_id, ty.span)
|
infer_placeholder_type(tcx, def_id, body_id, ty.span, item.ident)
|
||||||
} else {
|
} else {
|
||||||
icx.to_ty(ty)
|
icx.to_ty(ty)
|
||||||
}
|
}
|
||||||
|
@ -1246,7 +1269,7 @@ pub fn checked_type_of(tcx: TyCtxt<'_>, def_id: DefId, fail: bool) -> Option<Ty<
|
||||||
ItemKind::Static(ref ty, .., body_id)
|
ItemKind::Static(ref ty, .., body_id)
|
||||||
| ItemKind::Const(ref ty, body_id) => {
|
| ItemKind::Const(ref ty, body_id) => {
|
||||||
if let hir::TyKind::Infer = ty.node {
|
if let hir::TyKind::Infer = ty.node {
|
||||||
infer_placeholder_type(tcx, def_id, body_id, ty.span)
|
infer_placeholder_type(tcx, def_id, body_id, ty.span, item.ident)
|
||||||
} else {
|
} else {
|
||||||
icx.to_ty(ty)
|
icx.to_ty(ty)
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,7 +24,7 @@ use crate::symbol::{kw, sym};
|
||||||
use std::mem;
|
use std::mem;
|
||||||
use log::debug;
|
use log::debug;
|
||||||
use rustc_target::spec::abi::Abi;
|
use rustc_target::spec::abi::Abi;
|
||||||
use errors::{Applicability, DiagnosticBuilder, DiagnosticId};
|
use errors::{Applicability, DiagnosticBuilder, DiagnosticId, StashKey};
|
||||||
|
|
||||||
/// Whether the type alias or associated type is a concrete type or an opaque type.
|
/// Whether the type alias or associated type is a concrete type or an opaque type.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
@ -1477,10 +1477,23 @@ impl<'a> Parser<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Parse `["const" | ("static" "mut"?)] $ident ":" $ty = $expr` with
|
||||||
|
/// `["const" | ("static" "mut"?)]` already parsed and stored in `m`.
|
||||||
|
///
|
||||||
|
/// When `m` is `"const"`, `$ident` may also be `"_"`.
|
||||||
fn parse_item_const(&mut self, m: Option<Mutability>) -> PResult<'a, ItemInfo> {
|
fn parse_item_const(&mut self, m: Option<Mutability>) -> PResult<'a, ItemInfo> {
|
||||||
let id = if m.is_none() { self.parse_ident_or_underscore() } else { self.parse_ident() }?;
|
let id = if m.is_none() { self.parse_ident_or_underscore() } else { self.parse_ident() }?;
|
||||||
self.expect(&token::Colon)?;
|
|
||||||
let ty = self.parse_ty()?;
|
// Parse the type of a `const` or `static mut?` item.
|
||||||
|
// That is, the `":" $ty` fragment.
|
||||||
|
let ty = if self.token == token::Eq {
|
||||||
|
self.recover_missing_const_type(id, m)
|
||||||
|
} else {
|
||||||
|
// Not `=` so expect `":"" $ty` as usual.
|
||||||
|
self.expect(&token::Colon)?;
|
||||||
|
self.parse_ty()?
|
||||||
|
};
|
||||||
|
|
||||||
self.expect(&token::Eq)?;
|
self.expect(&token::Eq)?;
|
||||||
let e = self.parse_expr()?;
|
let e = self.parse_expr()?;
|
||||||
self.expect(&token::Semi)?;
|
self.expect(&token::Semi)?;
|
||||||
|
@ -1491,6 +1504,34 @@ impl<'a> Parser<'a> {
|
||||||
Ok((id, item, None))
|
Ok((id, item, None))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// We were supposed to parse `:` but instead, we're already at `=`.
|
||||||
|
/// This means that the type is missing.
|
||||||
|
fn recover_missing_const_type(&mut self, id: Ident, m: Option<Mutability>) -> P<Ty> {
|
||||||
|
// Construct the error and stash it away with the hope
|
||||||
|
// that typeck will later enrich the error with a type.
|
||||||
|
let kind = match m {
|
||||||
|
Some(Mutability::Mutable) => "static mut",
|
||||||
|
Some(Mutability::Immutable) => "static",
|
||||||
|
None => "const",
|
||||||
|
};
|
||||||
|
let mut err = self.struct_span_err(id.span, &format!("missing type for `{}` item", kind));
|
||||||
|
err.span_suggestion(
|
||||||
|
id.span,
|
||||||
|
"provide a type for the item",
|
||||||
|
format!("{}: <type>", id),
|
||||||
|
Applicability::HasPlaceholders,
|
||||||
|
);
|
||||||
|
err.stash(id.span, StashKey::ItemNoType);
|
||||||
|
|
||||||
|
// The user intended that the type be inferred,
|
||||||
|
// so treat this as if the user wrote e.g. `const A: _ = expr;`.
|
||||||
|
P(Ty {
|
||||||
|
node: TyKind::Infer,
|
||||||
|
span: id.span,
|
||||||
|
id: ast::DUMMY_NODE_ID,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
/// Parses `type Foo = Bar;` or returns `None`
|
/// Parses `type Foo = Bar;` or returns `None`
|
||||||
/// without modifying the parser state.
|
/// without modifying the parser state.
|
||||||
fn eat_type(&mut self) -> Option<PResult<'a, (Ident, AliasKind, Generics)>> {
|
fn eat_type(&mut self) -> Option<PResult<'a, (Ident, AliasKind, Generics)>> {
|
||||||
|
|
46
src/test/ui/suggestions/const-no-type.rs
Normal file
46
src/test/ui/suggestions/const-no-type.rs
Normal file
|
@ -0,0 +1,46 @@
|
||||||
|
// In the cases below, the type is missing from the `const` and `static` items.
|
||||||
|
//
|
||||||
|
// Here, we test that we:
|
||||||
|
//
|
||||||
|
// a) Perform parser recovery.
|
||||||
|
//
|
||||||
|
// b) Emit a diagnostic with the actual inferred type to RHS of `=` as the suggestion.
|
||||||
|
|
||||||
|
fn main() {}
|
||||||
|
|
||||||
|
// These will not reach typeck:
|
||||||
|
|
||||||
|
#[cfg(FALSE)]
|
||||||
|
const C2 = 42;
|
||||||
|
//~^ ERROR missing type for `const` item
|
||||||
|
//~| HELP provide a type for the item
|
||||||
|
//~| SUGGESTION C2: <type>
|
||||||
|
|
||||||
|
#[cfg(FALSE)]
|
||||||
|
static S2 = "abc";
|
||||||
|
//~^ ERROR missing type for `static` item
|
||||||
|
//~| HELP provide a type for the item
|
||||||
|
//~| SUGGESTION S2: <type>
|
||||||
|
|
||||||
|
#[cfg(FALSE)]
|
||||||
|
static mut SM2 = "abc";
|
||||||
|
//~^ ERROR missing type for `static mut` item
|
||||||
|
//~| HELP provide a type for the item
|
||||||
|
//~| SUGGESTION SM2: <type>
|
||||||
|
|
||||||
|
// These will, so the diagnostics should be stolen by typeck:
|
||||||
|
|
||||||
|
const C = 42;
|
||||||
|
//~^ ERROR missing type for `const` item
|
||||||
|
//~| HELP provide a type for the item
|
||||||
|
//~| SUGGESTION C: i32
|
||||||
|
|
||||||
|
static S = Vec::<String>::new();
|
||||||
|
//~^ ERROR missing type for `static` item
|
||||||
|
//~| HELP provide a type for the item
|
||||||
|
//~| SUGGESTION S: std::vec::Vec<std::string::String>
|
||||||
|
|
||||||
|
static mut SM = "abc";
|
||||||
|
//~^ ERROR missing type for `static mut` item
|
||||||
|
//~| HELP provide a type for the item
|
||||||
|
//~| SUGGESTION &'static str
|
38
src/test/ui/suggestions/const-no-type.stderr
Normal file
38
src/test/ui/suggestions/const-no-type.stderr
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
error: missing type for `const` item
|
||||||
|
--> $DIR/const-no-type.rs:33:7
|
||||||
|
|
|
||||||
|
LL | const C = 42;
|
||||||
|
| ^ help: provide a type for the item: `C: i32`
|
||||||
|
|
||||||
|
error: missing type for `static` item
|
||||||
|
--> $DIR/const-no-type.rs:38:8
|
||||||
|
|
|
||||||
|
LL | static S = Vec::<String>::new();
|
||||||
|
| ^ help: provide a type for the item: `S: std::vec::Vec<std::string::String>`
|
||||||
|
|
||||||
|
error: missing type for `static mut` item
|
||||||
|
--> $DIR/const-no-type.rs:43:12
|
||||||
|
|
|
||||||
|
LL | static mut SM = "abc";
|
||||||
|
| ^^ help: provide a type for the item: `SM: &'static str`
|
||||||
|
|
||||||
|
error: missing type for `const` item
|
||||||
|
--> $DIR/const-no-type.rs:14:7
|
||||||
|
|
|
||||||
|
LL | const C2 = 42;
|
||||||
|
| ^^ help: provide a type for the item: `C2: <type>`
|
||||||
|
|
||||||
|
error: missing type for `static` item
|
||||||
|
--> $DIR/const-no-type.rs:20:8
|
||||||
|
|
|
||||||
|
LL | static S2 = "abc";
|
||||||
|
| ^^ help: provide a type for the item: `S2: <type>`
|
||||||
|
|
||||||
|
error: missing type for `static mut` item
|
||||||
|
--> $DIR/const-no-type.rs:26:12
|
||||||
|
|
|
||||||
|
LL | static mut SM2 = "abc";
|
||||||
|
| ^^^ help: provide a type for the item: `SM2: <type>`
|
||||||
|
|
||||||
|
error: aborting due to 6 previous errors
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue