Skip to content

Commit 8bb9663

Browse files
authored
Rollup merge of rust-lang#63539 - Centril:2015.await, r=oli-obk
Suggest Rust 2018 on `<expr>.await` with no such field When type checking a field projection (`fn check_field`) to `<expr>.await` where `<expr>: τ` and `τ` is not a primitive type, suggest switching to Rust 2018. E.g. ``` error[E0609]: no field `await` on type `std::pin::Pin<&mut dyn std::future::Future<Output = ()>>` --> $DIR/suggest-switching-edition-on-await.rs:31:7 | LL | x.await; | ^^^^^ unknown field | = note: to `.await` a `Future`, switch to Rust 2018 = help: set `edition = "2018"` in `Cargo.toml` = note: for more on editions, read https://doc.rust-lang.org/edition-guide ``` Fixes rust-lang#63533 This PR also performs some preparatory cleanups in `fn check_field`; the last 2 commits are where the suggestion is introduced and tested respectively. r? @varkor
2 parents 4382b5d + 9287eb6 commit 8bb9663

File tree

3 files changed

+256
-101
lines changed

3 files changed

+256
-101
lines changed

src/librustc_typeck/check/expr.rs

+168-101
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ use syntax::source_map::Span;
2424
use syntax::util::lev_distance::find_best_match_for_name;
2525
use rustc::hir;
2626
use rustc::hir::{ExprKind, QPath};
27+
use rustc::hir::def_id::DefId;
2728
use rustc::hir::def::{CtorKind, Res, DefKind};
2829
use rustc::hir::ptr::P;
2930
use rustc::infer;
@@ -1336,116 +1337,182 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
13361337
autoderef.unambiguous_final_ty(self);
13371338

13381339
if let Some((did, field_ty)) = private_candidate {
1339-
let struct_path = self.tcx().def_path_str(did);
1340-
let mut err = struct_span_err!(self.tcx().sess, expr.span, E0616,
1341-
"field `{}` of struct `{}` is private",
1342-
field, struct_path);
1343-
// Also check if an accessible method exists, which is often what is meant.
1344-
if self.method_exists(field, expr_t, expr.hir_id, false)
1345-
&& !self.expr_in_place(expr.hir_id)
1346-
{
1347-
self.suggest_method_call(
1348-
&mut err,
1349-
&format!("a method `{}` also exists, call it with parentheses", field),
1350-
field,
1351-
expr_t,
1352-
expr.hir_id,
1353-
);
1354-
}
1355-
err.emit();
1356-
field_ty
1357-
} else if field.name == kw::Invalid {
1358-
self.tcx().types.err
1340+
self.ban_private_field_access(expr, expr_t, field, did);
1341+
return field_ty;
1342+
}
1343+
1344+
if field.name == kw::Invalid {
13591345
} else if self.method_exists(field, expr_t, expr.hir_id, true) {
1360-
let mut err = type_error_struct!(self.tcx().sess, field.span, expr_t, E0615,
1361-
"attempted to take value of method `{}` on type `{}`",
1362-
field, expr_t);
1363-
1364-
if !self.expr_in_place(expr.hir_id) {
1365-
self.suggest_method_call(
1366-
&mut err,
1367-
"use parentheses to call the method",
1368-
field,
1369-
expr_t,
1370-
expr.hir_id
1371-
);
1372-
} else {
1373-
err.help("methods are immutable and cannot be assigned to");
1346+
self.ban_take_value_of_method(expr, expr_t, field);
1347+
} else if !expr_t.is_primitive_ty() {
1348+
let mut err = self.no_such_field_err(field.span, field, expr_t);
1349+
1350+
match expr_t.sty {
1351+
ty::Adt(def, _) if !def.is_enum() => {
1352+
self.suggest_fields_on_recordish(&mut err, def, field);
1353+
}
1354+
ty::Array(_, len) => {
1355+
self.maybe_suggest_array_indexing(&mut err, expr, base, field, len);
1356+
}
1357+
ty::RawPtr(..) => {
1358+
self.suggest_first_deref_field(&mut err, expr, base, field);
1359+
}
1360+
_ => {}
1361+
}
1362+
1363+
if field.name == kw::Await {
1364+
// We know by construction that `<expr>.await` is either on Rust 2015
1365+
// or results in `ExprKind::Await`. Suggest switching the edition to 2018.
1366+
err.note("to `.await` a `Future`, switch to Rust 2018");
1367+
err.help("set `edition = \"2018\"` in `Cargo.toml`");
1368+
err.note("for more on editions, read https://doc.rust-lang.org/edition-guide");
13741369
}
13751370

13761371
err.emit();
1377-
self.tcx().types.err
13781372
} else {
1379-
if !expr_t.is_primitive_ty() {
1380-
let mut err = self.no_such_field_err(field.span, field, expr_t);
1381-
1382-
match expr_t.sty {
1383-
ty::Adt(def, _) if !def.is_enum() => {
1384-
if let Some(suggested_field_name) =
1385-
Self::suggest_field_name(def.non_enum_variant(),
1386-
&field.as_str(), vec![]) {
1387-
err.span_suggestion(
1388-
field.span,
1389-
"a field with a similar name exists",
1390-
suggested_field_name.to_string(),
1391-
Applicability::MaybeIncorrect,
1392-
);
1393-
} else {
1394-
err.span_label(field.span, "unknown field");
1395-
let struct_variant_def = def.non_enum_variant();
1396-
let field_names = self.available_field_names(struct_variant_def);
1397-
if !field_names.is_empty() {
1398-
err.note(&format!("available fields are: {}",
1399-
self.name_series_display(field_names)));
1400-
}
1401-
};
1402-
}
1403-
ty::Array(_, len) => {
1404-
if let (Some(len), Ok(user_index)) = (
1405-
len.try_eval_usize(self.tcx, self.param_env),
1406-
field.as_str().parse::<u64>()
1407-
) {
1408-
let base = self.tcx.sess.source_map()
1409-
.span_to_snippet(base.span)
1410-
.unwrap_or_else(|_|
1411-
self.tcx.hir().hir_to_pretty_string(base.hir_id));
1412-
let help = "instead of using tuple indexing, use array indexing";
1413-
let suggestion = format!("{}[{}]", base, field);
1414-
let applicability = if len < user_index {
1415-
Applicability::MachineApplicable
1416-
} else {
1417-
Applicability::MaybeIncorrect
1418-
};
1419-
err.span_suggestion(
1420-
expr.span, help, suggestion, applicability
1421-
);
1422-
}
1423-
}
1424-
ty::RawPtr(..) => {
1425-
let base = self.tcx.sess.source_map()
1426-
.span_to_snippet(base.span)
1427-
.unwrap_or_else(|_| self.tcx.hir().hir_to_pretty_string(base.hir_id));
1428-
let msg = format!("`{}` is a raw pointer; try dereferencing it", base);
1429-
let suggestion = format!("(*{}).{}", base, field);
1430-
err.span_suggestion(
1431-
expr.span,
1432-
&msg,
1433-
suggestion,
1434-
Applicability::MaybeIncorrect,
1435-
);
1436-
}
1437-
_ => {}
1438-
}
1439-
err
1373+
type_error_struct!(
1374+
self.tcx().sess,
1375+
field.span,
1376+
expr_t,
1377+
E0610,
1378+
"`{}` is a primitive type and therefore doesn't have fields",
1379+
expr_t
1380+
)
1381+
.emit();
1382+
}
1383+
1384+
self.tcx().types.err
1385+
}
1386+
1387+
fn ban_private_field_access(
1388+
&self,
1389+
expr: &hir::Expr,
1390+
expr_t: Ty<'tcx>,
1391+
field: ast::Ident,
1392+
base_did: DefId,
1393+
) {
1394+
let struct_path = self.tcx().def_path_str(base_did);
1395+
let mut err = struct_span_err!(
1396+
self.tcx().sess,
1397+
expr.span,
1398+
E0616,
1399+
"field `{}` of struct `{}` is private",
1400+
field,
1401+
struct_path
1402+
);
1403+
// Also check if an accessible method exists, which is often what is meant.
1404+
if self.method_exists(field, expr_t, expr.hir_id, false)
1405+
&& !self.expr_in_place(expr.hir_id)
1406+
{
1407+
self.suggest_method_call(
1408+
&mut err,
1409+
&format!("a method `{}` also exists, call it with parentheses", field),
1410+
field,
1411+
expr_t,
1412+
expr.hir_id,
1413+
);
1414+
}
1415+
err.emit();
1416+
}
1417+
1418+
fn ban_take_value_of_method(&self, expr: &hir::Expr, expr_t: Ty<'tcx>, field: ast::Ident) {
1419+
let mut err = type_error_struct!(
1420+
self.tcx().sess,
1421+
field.span,
1422+
expr_t,
1423+
E0615,
1424+
"attempted to take value of method `{}` on type `{}`",
1425+
field,
1426+
expr_t
1427+
);
1428+
1429+
if !self.expr_in_place(expr.hir_id) {
1430+
self.suggest_method_call(
1431+
&mut err,
1432+
"use parentheses to call the method",
1433+
field,
1434+
expr_t,
1435+
expr.hir_id
1436+
);
1437+
} else {
1438+
err.help("methods are immutable and cannot be assigned to");
1439+
}
1440+
1441+
err.emit();
1442+
}
1443+
1444+
fn suggest_fields_on_recordish(
1445+
&self,
1446+
err: &mut DiagnosticBuilder<'_>,
1447+
def: &'tcx ty::AdtDef,
1448+
field: ast::Ident,
1449+
) {
1450+
if let Some(suggested_field_name) =
1451+
Self::suggest_field_name(def.non_enum_variant(), &field.as_str(), vec![])
1452+
{
1453+
err.span_suggestion(
1454+
field.span,
1455+
"a field with a similar name exists",
1456+
suggested_field_name.to_string(),
1457+
Applicability::MaybeIncorrect,
1458+
);
1459+
} else {
1460+
err.span_label(field.span, "unknown field");
1461+
let struct_variant_def = def.non_enum_variant();
1462+
let field_names = self.available_field_names(struct_variant_def);
1463+
if !field_names.is_empty() {
1464+
err.note(&format!("available fields are: {}",
1465+
self.name_series_display(field_names)));
1466+
}
1467+
}
1468+
}
1469+
1470+
fn maybe_suggest_array_indexing(
1471+
&self,
1472+
err: &mut DiagnosticBuilder<'_>,
1473+
expr: &hir::Expr,
1474+
base: &hir::Expr,
1475+
field: ast::Ident,
1476+
len: &ty::Const<'tcx>,
1477+
) {
1478+
if let (Some(len), Ok(user_index)) = (
1479+
len.try_eval_usize(self.tcx, self.param_env),
1480+
field.as_str().parse::<u64>()
1481+
) {
1482+
let base = self.tcx.sess.source_map()
1483+
.span_to_snippet(base.span)
1484+
.unwrap_or_else(|_| self.tcx.hir().hir_to_pretty_string(base.hir_id));
1485+
let help = "instead of using tuple indexing, use array indexing";
1486+
let suggestion = format!("{}[{}]", base, field);
1487+
let applicability = if len < user_index {
1488+
Applicability::MachineApplicable
14401489
} else {
1441-
type_error_struct!(self.tcx().sess, field.span, expr_t, E0610,
1442-
"`{}` is a primitive type and therefore doesn't have fields",
1443-
expr_t)
1444-
}.emit();
1445-
self.tcx().types.err
1490+
Applicability::MaybeIncorrect
1491+
};
1492+
err.span_suggestion(expr.span, help, suggestion, applicability);
14461493
}
14471494
}
14481495

1496+
fn suggest_first_deref_field(
1497+
&self,
1498+
err: &mut DiagnosticBuilder<'_>,
1499+
expr: &hir::Expr,
1500+
base: &hir::Expr,
1501+
field: ast::Ident,
1502+
) {
1503+
let base = self.tcx.sess.source_map()
1504+
.span_to_snippet(base.span)
1505+
.unwrap_or_else(|_| self.tcx.hir().hir_to_pretty_string(base.hir_id));
1506+
let msg = format!("`{}` is a raw pointer; try dereferencing it", base);
1507+
let suggestion = format!("(*{}).{}", base, field);
1508+
err.span_suggestion(
1509+
expr.span,
1510+
&msg,
1511+
suggestion,
1512+
Applicability::MaybeIncorrect,
1513+
);
1514+
}
1515+
14491516
fn no_such_field_err<T: Display>(&self, span: Span, field: T, expr_t: &ty::TyS<'_>)
14501517
-> DiagnosticBuilder<'_> {
14511518
type_error_struct!(self.tcx().sess, span, expr_t, E0609,
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
use std::pin::Pin;
2+
use std::future::Future;
3+
4+
fn main() {}
5+
6+
fn await_on_struct_missing() {
7+
struct S;
8+
let x = S;
9+
x.await;
10+
//~^ ERROR no field `await` on type
11+
//~| NOTE unknown field
12+
//~| NOTE to `.await` a `Future`, switch to Rust 2018
13+
//~| HELP set `edition = "2018"` in `Cargo.toml`
14+
//~| NOTE for more on editions, read https://doc.rust-lang.org/edition-guide
15+
}
16+
17+
fn await_on_struct_similar() {
18+
struct S {
19+
awai: u8,
20+
}
21+
let x = S { awai: 42 };
22+
x.await;
23+
//~^ ERROR no field `await` on type
24+
//~| HELP a field with a similar name exists
25+
//~| NOTE to `.await` a `Future`, switch to Rust 2018
26+
//~| HELP set `edition = "2018"` in `Cargo.toml`
27+
//~| NOTE for more on editions, read https://doc.rust-lang.org/edition-guide
28+
}
29+
30+
fn await_on_63533(x: Pin<&mut dyn Future<Output = ()>>) {
31+
x.await;
32+
//~^ ERROR no field `await` on type
33+
//~| NOTE unknown field
34+
//~| NOTE to `.await` a `Future`, switch to Rust 2018
35+
//~| HELP set `edition = "2018"` in `Cargo.toml`
36+
//~| NOTE for more on editions, read https://doc.rust-lang.org/edition-guide
37+
}
38+
39+
fn await_on_apit(x: impl Future<Output = ()>) {
40+
x.await;
41+
//~^ ERROR no field `await` on type
42+
//~| NOTE to `.await` a `Future`, switch to Rust 2018
43+
//~| HELP set `edition = "2018"` in `Cargo.toml`
44+
//~| NOTE for more on editions, read https://doc.rust-lang.org/edition-guide
45+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
error[E0609]: no field `await` on type `await_on_struct_missing::S`
2+
--> $DIR/suggest-switching-edition-on-await.rs:9:7
3+
|
4+
LL | x.await;
5+
| ^^^^^ unknown field
6+
|
7+
= note: to `.await` a `Future`, switch to Rust 2018
8+
= help: set `edition = "2018"` in `Cargo.toml`
9+
= note: for more on editions, read https://doc.rust-lang.org/edition-guide
10+
11+
error[E0609]: no field `await` on type `await_on_struct_similar::S`
12+
--> $DIR/suggest-switching-edition-on-await.rs:22:7
13+
|
14+
LL | x.await;
15+
| ^^^^^ help: a field with a similar name exists: `awai`
16+
|
17+
= note: to `.await` a `Future`, switch to Rust 2018
18+
= help: set `edition = "2018"` in `Cargo.toml`
19+
= note: for more on editions, read https://doc.rust-lang.org/edition-guide
20+
21+
error[E0609]: no field `await` on type `std::pin::Pin<&mut dyn std::future::Future<Output = ()>>`
22+
--> $DIR/suggest-switching-edition-on-await.rs:31:7
23+
|
24+
LL | x.await;
25+
| ^^^^^ unknown field
26+
|
27+
= note: to `.await` a `Future`, switch to Rust 2018
28+
= help: set `edition = "2018"` in `Cargo.toml`
29+
= note: for more on editions, read https://doc.rust-lang.org/edition-guide
30+
31+
error[E0609]: no field `await` on type `impl Future<Output = ()>`
32+
--> $DIR/suggest-switching-edition-on-await.rs:40:7
33+
|
34+
LL | x.await;
35+
| ^^^^^
36+
|
37+
= note: to `.await` a `Future`, switch to Rust 2018
38+
= help: set `edition = "2018"` in `Cargo.toml`
39+
= note: for more on editions, read https://doc.rust-lang.org/edition-guide
40+
41+
error: aborting due to 4 previous errors
42+
43+
For more information about this error, try `rustc --explain E0609`.

0 commit comments

Comments
 (0)