diff --git a/src/librustc_typeck/collect.rs b/src/librustc_typeck/collect.rs index d2e9203779c..e6e0cb88fbd 100644 --- a/src/librustc_typeck/collect.rs +++ b/src/librustc_typeck/collect.rs @@ -46,7 +46,7 @@ use rustc::hir::intravisit::{self, NestedVisitorMap, Visitor}; use rustc::hir::GenericParamKind; use rustc::hir::{self, CodegenFnAttrFlags, CodegenFnAttrs, Unsafety}; -use errors::{Applicability, DiagnosticId}; +use errors::{Applicability, DiagnosticId, StashKey}; struct OnlySelfBounds(bool); @@ -1149,18 +1149,41 @@ fn infer_placeholder_type( def_id: DefId, body_id: hir::BodyId, span: Span, + item_ident: Ident, ) -> Ty<'_> { 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 { - diag.span_suggestion( - span, - "replace `_` with the correct type", - ty.to_string(), - Applicability::MaybeIncorrect, - ); + + // If this came from a free `const` or `static mut?` item, + // then the user may have written e.g. `const A = 42;`. + // In this case, the parser has stashed a diagnostic for + // us to improve in typeck so we do that now. + match tcx.sess.diagnostic().steal_diagnostic(span, StashKey::ItemNoType) { + 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 } @@ -1192,7 +1215,7 @@ pub fn checked_type_of(tcx: TyCtxt<'_>, def_id: DefId, fail: bool) -> Option { body_id.and_then(|body_id| { 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 { None } @@ -1214,7 +1237,7 @@ pub fn checked_type_of(tcx: TyCtxt<'_>, def_id: DefId, fail: bool) -> Option { 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 { icx.to_ty(ty) } @@ -1246,7 +1269,7 @@ pub fn checked_type_of(tcx: TyCtxt<'_>, def_id: DefId, fail: bool) -> Option { 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 { icx.to_ty(ty) } diff --git a/src/libsyntax/parse/parser/item.rs b/src/libsyntax/parse/parser/item.rs index cf196645e4f..0d073f0cc97 100644 --- a/src/libsyntax/parse/parser/item.rs +++ b/src/libsyntax/parse/parser/item.rs @@ -24,7 +24,7 @@ use crate::symbol::{kw, sym}; use std::mem; use log::debug; 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. #[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) -> PResult<'a, ItemInfo> { 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)?; let e = self.parse_expr()?; self.expect(&token::Semi)?; @@ -1491,6 +1504,34 @@ impl<'a> Parser<'a> { 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) -> P { + // 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!("{}: ", 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` /// without modifying the parser state. fn eat_type(&mut self) -> Option> { diff --git a/src/test/ui/suggestions/const-no-type.rs b/src/test/ui/suggestions/const-no-type.rs new file mode 100644 index 00000000000..99200a965dd --- /dev/null +++ b/src/test/ui/suggestions/const-no-type.rs @@ -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: + +#[cfg(FALSE)] +static S2 = "abc"; +//~^ ERROR missing type for `static` item +//~| HELP provide a type for the item +//~| SUGGESTION S2: + +#[cfg(FALSE)] +static mut SM2 = "abc"; +//~^ ERROR missing type for `static mut` item +//~| HELP provide a type for the item +//~| SUGGESTION SM2: + +// 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::::new(); +//~^ ERROR missing type for `static` item +//~| HELP provide a type for the item +//~| SUGGESTION S: std::vec::Vec + +static mut SM = "abc"; +//~^ ERROR missing type for `static mut` item +//~| HELP provide a type for the item +//~| SUGGESTION &'static str diff --git a/src/test/ui/suggestions/const-no-type.stderr b/src/test/ui/suggestions/const-no-type.stderr new file mode 100644 index 00000000000..c4f17109dc5 --- /dev/null +++ b/src/test/ui/suggestions/const-no-type.stderr @@ -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::::new(); + | ^ help: provide a type for the item: `S: std::vec::Vec` + +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: ` + +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: ` + +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: ` + +error: aborting due to 6 previous errors +