@@ -24,6 +24,7 @@ use syntax::source_map::Span;
2424use syntax:: util:: lev_distance:: find_best_match_for_name;
2525use rustc:: hir;
2626use rustc:: hir:: { ExprKind , QPath } ;
27+ use rustc:: hir:: def_id:: DefId ;
2728use rustc:: hir:: def:: { CtorKind , Res , DefKind } ;
2829use rustc:: hir:: ptr:: P ;
2930use 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 ,
0 commit comments