Skip to content

Commit 62d8584

Browse files
committed
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.
1 parent ae8b3e8 commit 62d8584

File tree

4 files changed

+164
-16
lines changed

4 files changed

+164
-16
lines changed

src/librustc_typeck/collect.rs

+36-13
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ use rustc::hir::intravisit::{self, NestedVisitorMap, Visitor};
4646
use rustc::hir::GenericParamKind;
4747
use rustc::hir::{self, CodegenFnAttrFlags, CodegenFnAttrs, Unsafety};
4848

49-
use errors::{Applicability, DiagnosticId};
49+
use errors::{Applicability, DiagnosticId, StashKey};
5050

5151
struct OnlySelfBounds(bool);
5252

@@ -1149,18 +1149,41 @@ fn infer_placeholder_type(
11491149
def_id: DefId,
11501150
body_id: hir::BodyId,
11511151
span: Span,
1152+
item_ident: Ident,
11521153
) -> Ty<'_> {
11531154
let ty = tcx.typeck_tables_of(def_id).node_type(body_id.hir_id);
1154-
let mut diag = bad_placeholder_type(tcx, span);
1155-
if ty != tcx.types.err {
1156-
diag.span_suggestion(
1157-
span,
1158-
"replace `_` with the correct type",
1159-
ty.to_string(),
1160-
Applicability::MaybeIncorrect,
1161-
);
1155+
1156+
// If this came from a free `const` or `static mut?` item,
1157+
// then the user may have written e.g. `const A = 42;`.
1158+
// In this case, the parser has stashed a diagnostic for
1159+
// us to improve in typeck so we do that now.
1160+
match tcx.sess.diagnostic().steal_diagnostic(span, StashKey::ItemNoType) {
1161+
Some(mut err) => {
1162+
// The parser provided a sub-optimal `HasPlaceholders` suggestion for the type.
1163+
// We are typeck and have the real type, so remove that and suggest the actual type.
1164+
err.suggestions.clear();
1165+
err.span_suggestion(
1166+
span,
1167+
"provide a type for the item",
1168+
format!("{}: {}", item_ident, ty),
1169+
Applicability::MachineApplicable,
1170+
)
1171+
.emit();
1172+
}
1173+
None => {
1174+
let mut diag = bad_placeholder_type(tcx, span);
1175+
if ty != tcx.types.err {
1176+
diag.span_suggestion(
1177+
span,
1178+
"replace `_` with the correct type",
1179+
ty.to_string(),
1180+
Applicability::MaybeIncorrect,
1181+
);
1182+
}
1183+
diag.emit();
1184+
}
11621185
}
1163-
diag.emit();
1186+
11641187
ty
11651188
}
11661189

@@ -1192,7 +1215,7 @@ pub fn checked_type_of(tcx: TyCtxt<'_>, def_id: DefId, fail: bool) -> Option<Ty<
11921215
TraitItemKind::Const(ref ty, body_id) => {
11931216
body_id.and_then(|body_id| {
11941217
if let hir::TyKind::Infer = ty.node {
1195-
Some(infer_placeholder_type(tcx, def_id, body_id, ty.span))
1218+
Some(infer_placeholder_type(tcx, def_id, body_id, ty.span, item.ident))
11961219
} else {
11971220
None
11981221
}
@@ -1214,7 +1237,7 @@ pub fn checked_type_of(tcx: TyCtxt<'_>, def_id: DefId, fail: bool) -> Option<Ty<
12141237
}
12151238
ImplItemKind::Const(ref ty, body_id) => {
12161239
if let hir::TyKind::Infer = ty.node {
1217-
infer_placeholder_type(tcx, def_id, body_id, ty.span)
1240+
infer_placeholder_type(tcx, def_id, body_id, ty.span, item.ident)
12181241
} else {
12191242
icx.to_ty(ty)
12201243
}
@@ -1246,7 +1269,7 @@ pub fn checked_type_of(tcx: TyCtxt<'_>, def_id: DefId, fail: bool) -> Option<Ty<
12461269
ItemKind::Static(ref ty, .., body_id)
12471270
| ItemKind::Const(ref ty, body_id) => {
12481271
if let hir::TyKind::Infer = ty.node {
1249-
infer_placeholder_type(tcx, def_id, body_id, ty.span)
1272+
infer_placeholder_type(tcx, def_id, body_id, ty.span, item.ident)
12501273
} else {
12511274
icx.to_ty(ty)
12521275
}

src/libsyntax/parse/parser/item.rs

+44-3
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ use crate::symbol::{kw, sym};
2424
use std::mem;
2525
use log::debug;
2626
use rustc_target::spec::abi::Abi;
27-
use errors::{Applicability, DiagnosticBuilder, DiagnosticId};
27+
use errors::{Applicability, DiagnosticBuilder, DiagnosticId, StashKey};
2828

2929
/// Whether the type alias or associated type is a concrete type or an opaque type.
3030
#[derive(Debug)]
@@ -1477,10 +1477,23 @@ impl<'a> Parser<'a> {
14771477
}
14781478
}
14791479

1480+
/// Parse `["const" | ("static" "mut"?)] $ident ":" $ty = $expr` with
1481+
/// `["const" | ("static" "mut"?)]` already parsed and stored in `m`.
1482+
///
1483+
/// When `m` is `"const"`, `$ident` may also be `"_"`.
14801484
fn parse_item_const(&mut self, m: Option<Mutability>) -> PResult<'a, ItemInfo> {
14811485
let id = if m.is_none() { self.parse_ident_or_underscore() } else { self.parse_ident() }?;
1482-
self.expect(&token::Colon)?;
1483-
let ty = self.parse_ty()?;
1486+
1487+
// Parse the type of a `const` or `static mut?` item.
1488+
// That is, the `":" $ty` fragment.
1489+
let ty = if self.token == token::Eq {
1490+
self.recover_missing_const_type(id, m)
1491+
} else {
1492+
// Not `=` so expect `":"" $ty` as usual.
1493+
self.expect(&token::Colon)?;
1494+
self.parse_ty()?
1495+
};
1496+
14841497
self.expect(&token::Eq)?;
14851498
let e = self.parse_expr()?;
14861499
self.expect(&token::Semi)?;
@@ -1491,6 +1504,34 @@ impl<'a> Parser<'a> {
14911504
Ok((id, item, None))
14921505
}
14931506

1507+
/// We were supposed to parse `:` but instead, we're already at `=`.
1508+
/// This means that the type is missing.
1509+
fn recover_missing_const_type(&mut self, id: Ident, m: Option<Mutability>) -> P<Ty> {
1510+
// Construct the error and stash it away with the hope
1511+
// that typeck will later enrich the error with a type.
1512+
let kind = match m {
1513+
Some(Mutability::Mutable) => "static mut",
1514+
Some(Mutability::Immutable) => "static",
1515+
None => "const",
1516+
};
1517+
let mut err = self.struct_span_err(id.span, &format!("missing type for `{}` item", kind));
1518+
err.span_suggestion(
1519+
id.span,
1520+
"provide a type for the item",
1521+
format!("{}: <type>", id),
1522+
Applicability::HasPlaceholders,
1523+
);
1524+
err.stash(id.span, StashKey::ItemNoType);
1525+
1526+
// The user intended that the type be inferred,
1527+
// so treat this as if the user wrote e.g. `const A: _ = expr;`.
1528+
P(Ty {
1529+
node: TyKind::Infer,
1530+
span: id.span,
1531+
id: ast::DUMMY_NODE_ID,
1532+
})
1533+
}
1534+
14941535
/// Parses `type Foo = Bar;` or returns `None`
14951536
/// without modifying the parser state.
14961537
fn eat_type(&mut self) -> Option<PResult<'a, (Ident, AliasKind, Generics)>> {
+46
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
// In the cases below, the type is missing from the `const` and `static` items.
2+
//
3+
// Here, we test that we:
4+
//
5+
// a) Perform parser recovery.
6+
//
7+
// b) Emit a diagnostic with the actual inferred type to RHS of `=` as the suggestion.
8+
9+
fn main() {}
10+
11+
// These will not reach typeck:
12+
13+
#[cfg(FALSE)]
14+
const C2 = 42;
15+
//~^ ERROR missing type for `const` item
16+
//~| HELP provide a type for the item
17+
//~| SUGGESTION C2: <type>
18+
19+
#[cfg(FALSE)]
20+
static S2 = "abc";
21+
//~^ ERROR missing type for `static` item
22+
//~| HELP provide a type for the item
23+
//~| SUGGESTION S2: <type>
24+
25+
#[cfg(FALSE)]
26+
static mut SM2 = "abc";
27+
//~^ ERROR missing type for `static mut` item
28+
//~| HELP provide a type for the item
29+
//~| SUGGESTION SM2: <type>
30+
31+
// These will, so the diagnostics should be stolen by typeck:
32+
33+
const C = 42;
34+
//~^ ERROR missing type for `const` item
35+
//~| HELP provide a type for the item
36+
//~| SUGGESTION C: i32
37+
38+
static S = Vec::<String>::new();
39+
//~^ ERROR missing type for `static` item
40+
//~| HELP provide a type for the item
41+
//~| SUGGESTION S: std::vec::Vec<std::string::String>
42+
43+
static mut SM = "abc";
44+
//~^ ERROR missing type for `static mut` item
45+
//~| HELP provide a type for the item
46+
//~| SUGGESTION &'static str
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
error: missing type for `const` item
2+
--> $DIR/const-no-type.rs:33:7
3+
|
4+
LL | const C = 42;
5+
| ^ help: provide a type for the item: `C: i32`
6+
7+
error: missing type for `static` item
8+
--> $DIR/const-no-type.rs:38:8
9+
|
10+
LL | static S = Vec::<String>::new();
11+
| ^ help: provide a type for the item: `S: std::vec::Vec<std::string::String>`
12+
13+
error: missing type for `static mut` item
14+
--> $DIR/const-no-type.rs:43:12
15+
|
16+
LL | static mut SM = "abc";
17+
| ^^ help: provide a type for the item: `SM: &'static str`
18+
19+
error: missing type for `const` item
20+
--> $DIR/const-no-type.rs:14:7
21+
|
22+
LL | const C2 = 42;
23+
| ^^ help: provide a type for the item: `C2: <type>`
24+
25+
error: missing type for `static` item
26+
--> $DIR/const-no-type.rs:20:8
27+
|
28+
LL | static S2 = "abc";
29+
| ^^ help: provide a type for the item: `S2: <type>`
30+
31+
error: missing type for `static mut` item
32+
--> $DIR/const-no-type.rs:26:12
33+
|
34+
LL | static mut SM2 = "abc";
35+
| ^^^ help: provide a type for the item: `SM2: <type>`
36+
37+
error: aborting due to 6 previous errors
38+

0 commit comments

Comments
 (0)