From 5057552dc6e8b4d259edcb080c525056da46efa3 Mon Sep 17 00:00:00 2001 From: Mazdak Farrokhzad Date: Sat, 15 Jun 2019 03:32:16 +0200 Subject: [PATCH] typeck/expr.rs: move check_field + struct helpers here. --- src/librustc_typeck/check/expr.rs | 411 +++++++++++++++++++++++++++++- src/librustc_typeck/check/mod.rs | 409 +---------------------------- 2 files changed, 412 insertions(+), 408 deletions(-) diff --git a/src/librustc_typeck/check/expr.rs b/src/librustc_typeck/check/expr.rs index b133526054fdd..fa9e0d8a8578a 100644 --- a/src/librustc_typeck/check/expr.rs +++ b/src/librustc_typeck/check/expr.rs @@ -15,12 +15,15 @@ use crate::check::TupleArgumentsFlag::DontTupleArguments; use crate::check::method::SelfSource; use crate::middle::lang_items; use crate::util::common::ErrorReported; +use crate::util::nodemap::FxHashMap; +use crate::astconv::AstConv as _; -use errors::Applicability; +use errors::{Applicability, DiagnosticBuilder}; use syntax::ast; use syntax::ptr::P; -use syntax::symbol::{kw, sym}; +use syntax::symbol::{Symbol, LocalInternedString, kw, sym}; use syntax::source_map::Span; +use syntax::util::lev_distance::find_best_match_for_name; use rustc::hir; use rustc::hir::{ExprKind, QPath}; use rustc::hir::def::{CtorKind, Res, DefKind}; @@ -31,11 +34,14 @@ use rustc::ty; use rustc::ty::adjustment::{ Adjust, Adjustment, AllowTwoPhase, AutoBorrow, AutoBorrowMutability, }; +use rustc::ty::{AdtKind, Visibility}; use rustc::ty::Ty; use rustc::ty::TypeFoldable; use rustc::ty::subst::InternalSubsts; use rustc::traits::{self, ObligationCauseCode}; +use std::fmt::Display; + impl<'a, 'tcx> FnCtxt<'a, 'tcx> { fn check_expr_eq_type(&self, expr: &'tcx hir::Expr, expected: Ty<'tcx>) { let ty = self.check_expr_with_hint(expr, expected); @@ -1057,6 +1063,407 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { adt_ty } + fn check_expr_struct_fields( + &self, + adt_ty: Ty<'tcx>, + expected: Expectation<'tcx>, + expr_id: hir::HirId, + span: Span, + variant: &'tcx ty::VariantDef, + ast_fields: &'tcx [hir::Field], + check_completeness: bool, + ) -> bool { + let tcx = self.tcx; + + let adt_ty_hint = + self.expected_inputs_for_expected_output(span, expected, adt_ty, &[adt_ty]) + .get(0).cloned().unwrap_or(adt_ty); + // re-link the regions that EIfEO can erase. + self.demand_eqtype(span, adt_ty_hint, adt_ty); + + let (substs, adt_kind, kind_name) = match &adt_ty.sty { + &ty::Adt(adt, substs) => { + (substs, adt.adt_kind(), adt.variant_descr()) + } + _ => span_bug!(span, "non-ADT passed to check_expr_struct_fields") + }; + + let mut remaining_fields = variant.fields.iter().enumerate().map(|(i, field)| + (field.ident.modern(), (i, field)) + ).collect::>(); + + let mut seen_fields = FxHashMap::default(); + + let mut error_happened = false; + + // Type-check each field. + for field in ast_fields { + let ident = tcx.adjust_ident(field.ident, variant.def_id); + let field_type = if let Some((i, v_field)) = remaining_fields.remove(&ident) { + seen_fields.insert(ident, field.span); + self.write_field_index(field.hir_id, i); + + // We don't look at stability attributes on + // struct-like enums (yet...), but it's definitely not + // a bug to have constructed one. + if adt_kind != AdtKind::Enum { + tcx.check_stability(v_field.did, Some(expr_id), field.span); + } + + self.field_ty(field.span, v_field, substs) + } else { + error_happened = true; + if let Some(prev_span) = seen_fields.get(&ident) { + let mut err = struct_span_err!(self.tcx.sess, + field.ident.span, + E0062, + "field `{}` specified more than once", + ident); + + err.span_label(field.ident.span, "used more than once"); + err.span_label(*prev_span, format!("first use of `{}`", ident)); + + err.emit(); + } else { + self.report_unknown_field(adt_ty, variant, field, ast_fields, kind_name); + } + + tcx.types.err + }; + + // Make sure to give a type to the field even if there's + // an error, so we can continue type-checking. + self.check_expr_coercable_to_type(&field.expr, field_type); + } + + // Make sure the programmer specified correct number of fields. + if kind_name == "union" { + if ast_fields.len() != 1 { + tcx.sess.span_err(span, "union expressions should have exactly one field"); + } + } else if check_completeness && !error_happened && !remaining_fields.is_empty() { + let len = remaining_fields.len(); + + let mut displayable_field_names = remaining_fields + .keys() + .map(|ident| ident.as_str()) + .collect::>(); + + displayable_field_names.sort(); + + let truncated_fields_error = if len <= 3 { + String::new() + } else { + format!(" and {} other field{}", (len - 3), if len - 3 == 1 {""} else {"s"}) + }; + + let remaining_fields_names = displayable_field_names.iter().take(3) + .map(|n| format!("`{}`", n)) + .collect::>() + .join(", "); + + struct_span_err!(tcx.sess, span, E0063, + "missing field{} {}{} in initializer of `{}`", + if remaining_fields.len() == 1 { "" } else { "s" }, + remaining_fields_names, + truncated_fields_error, + adt_ty) + .span_label(span, format!("missing {}{}", + remaining_fields_names, + truncated_fields_error)) + .emit(); + } + error_happened + } + + fn check_struct_fields_on_error( + &self, + fields: &'tcx [hir::Field], + base_expr: &'tcx Option>, + ) { + for field in fields { + self.check_expr(&field.expr); + } + if let Some(ref base) = *base_expr { + self.check_expr(&base); + } + } + + fn report_unknown_field( + &self, + ty: Ty<'tcx>, + variant: &'tcx ty::VariantDef, + field: &hir::Field, + skip_fields: &[hir::Field], + kind_name: &str, + ) { + if variant.recovered { + return; + } + let mut err = self.type_error_struct_with_diag( + field.ident.span, + |actual| match ty.sty { + ty::Adt(adt, ..) if adt.is_enum() => { + struct_span_err!(self.tcx.sess, field.ident.span, E0559, + "{} `{}::{}` has no field named `{}`", + kind_name, actual, variant.ident, field.ident) + } + _ => { + struct_span_err!(self.tcx.sess, field.ident.span, E0560, + "{} `{}` has no field named `{}`", + kind_name, actual, field.ident) + } + }, + ty); + // prevent all specified fields from being suggested + let skip_fields = skip_fields.iter().map(|ref x| x.ident.as_str()); + if let Some(field_name) = Self::suggest_field_name(variant, + &field.ident.as_str(), + skip_fields.collect()) { + err.span_suggestion( + field.ident.span, + "a field with a similar name exists", + field_name.to_string(), + Applicability::MaybeIncorrect, + ); + } else { + match ty.sty { + ty::Adt(adt, ..) => { + if adt.is_enum() { + err.span_label(field.ident.span, + format!("`{}::{}` does not have this field", + ty, variant.ident)); + } else { + err.span_label(field.ident.span, + format!("`{}` does not have this field", ty)); + } + let available_field_names = self.available_field_names(variant); + if !available_field_names.is_empty() { + err.note(&format!("available fields are: {}", + self.name_series_display(available_field_names))); + } + } + _ => bug!("non-ADT passed to report_unknown_field") + } + }; + err.emit(); + } + + // Return an hint about the closest match in field names + fn suggest_field_name(variant: &'tcx ty::VariantDef, + field: &str, + skip: Vec) + -> Option { + let names = variant.fields.iter().filter_map(|field| { + // ignore already set fields and private fields from non-local crates + if skip.iter().any(|x| *x == field.ident.as_str()) || + (!variant.def_id.is_local() && field.vis != Visibility::Public) + { + None + } else { + Some(&field.ident.name) + } + }); + + find_best_match_for_name(names, field, None) + } + + fn available_field_names(&self, variant: &'tcx ty::VariantDef) -> Vec { + variant.fields.iter().filter(|field| { + let def_scope = + self.tcx.adjust_ident_and_get_scope(field.ident, variant.def_id, self.body_id).1; + field.vis.is_accessible_from(def_scope, self.tcx) + }) + .map(|field| field.ident.name) + .collect() + } + + fn name_series_display(&self, names: Vec) -> String { + // dynamic limit, to never omit just one field + let limit = if names.len() == 6 { 6 } else { 5 }; + let mut display = names.iter().take(limit) + .map(|n| format!("`{}`", n)).collect::>().join(", "); + if names.len() > limit { + display = format!("{} ... and {} others", display, names.len() - limit); + } + display + } + + // Check field access expressions + fn check_field( + &self, + expr: &'tcx hir::Expr, + needs: Needs, + base: &'tcx hir::Expr, + field: ast::Ident, + ) -> Ty<'tcx> { + let expr_t = self.check_expr_with_needs(base, needs); + let expr_t = self.structurally_resolved_type(base.span, + expr_t); + let mut private_candidate = None; + let mut autoderef = self.autoderef(expr.span, expr_t); + while let Some((base_t, _)) = autoderef.next() { + match base_t.sty { + ty::Adt(base_def, substs) if !base_def.is_enum() => { + debug!("struct named {:?}", base_t); + let (ident, def_scope) = + self.tcx.adjust_ident_and_get_scope(field, base_def.did, self.body_id); + let fields = &base_def.non_enum_variant().fields; + if let Some(index) = fields.iter().position(|f| f.ident.modern() == ident) { + let field = &fields[index]; + let field_ty = self.field_ty(expr.span, field, substs); + // Save the index of all fields regardless of their visibility in case + // of error recovery. + self.write_field_index(expr.hir_id, index); + if field.vis.is_accessible_from(def_scope, self.tcx) { + let adjustments = autoderef.adjust_steps(self, needs); + self.apply_adjustments(base, adjustments); + autoderef.finalize(self); + + self.tcx.check_stability(field.did, Some(expr.hir_id), expr.span); + return field_ty; + } + private_candidate = Some((base_def.did, field_ty)); + } + } + ty::Tuple(ref tys) => { + let fstr = field.as_str(); + if let Ok(index) = fstr.parse::() { + if fstr == index.to_string() { + if let Some(field_ty) = tys.get(index) { + let adjustments = autoderef.adjust_steps(self, needs); + self.apply_adjustments(base, adjustments); + autoderef.finalize(self); + + self.write_field_index(expr.hir_id, index); + return field_ty.expect_ty(); + } + } + } + } + _ => {} + } + } + autoderef.unambiguous_final_ty(self); + + if let Some((did, field_ty)) = private_candidate { + let struct_path = self.tcx().def_path_str(did); + let mut err = struct_span_err!(self.tcx().sess, expr.span, E0616, + "field `{}` of struct `{}` is private", + field, struct_path); + // Also check if an accessible method exists, which is often what is meant. + if self.method_exists(field, expr_t, expr.hir_id, false) + && !self.expr_in_place(expr.hir_id) + { + self.suggest_method_call( + &mut err, + &format!("a method `{}` also exists, call it with parentheses", field), + field, + expr_t, + expr.hir_id, + ); + } + err.emit(); + field_ty + } else if field.name == kw::Invalid { + self.tcx().types.err + } else if self.method_exists(field, expr_t, expr.hir_id, true) { + let mut err = type_error_struct!(self.tcx().sess, field.span, expr_t, E0615, + "attempted to take value of method `{}` on type `{}`", + field, expr_t); + + if !self.expr_in_place(expr.hir_id) { + self.suggest_method_call( + &mut err, + "use parentheses to call the method", + field, + expr_t, + expr.hir_id + ); + } else { + err.help("methods are immutable and cannot be assigned to"); + } + + err.emit(); + self.tcx().types.err + } else { + if !expr_t.is_primitive_ty() { + let mut err = self.no_such_field_err(field.span, field, expr_t); + + match expr_t.sty { + ty::Adt(def, _) if !def.is_enum() => { + if let Some(suggested_field_name) = + Self::suggest_field_name(def.non_enum_variant(), + &field.as_str(), vec![]) { + err.span_suggestion( + field.span, + "a field with a similar name exists", + suggested_field_name.to_string(), + Applicability::MaybeIncorrect, + ); + } else { + err.span_label(field.span, "unknown field"); + let struct_variant_def = def.non_enum_variant(); + let field_names = self.available_field_names(struct_variant_def); + if !field_names.is_empty() { + err.note(&format!("available fields are: {}", + self.name_series_display(field_names))); + } + }; + } + ty::Array(_, len) => { + if let (Some(len), Ok(user_index)) = ( + len.assert_usize(self.tcx), + field.as_str().parse::() + ) { + let base = self.tcx.sess.source_map() + .span_to_snippet(base.span) + .unwrap_or_else(|_| + self.tcx.hir().hir_to_pretty_string(base.hir_id)); + let help = "instead of using tuple indexing, use array indexing"; + let suggestion = format!("{}[{}]", base, field); + let applicability = if len < user_index { + Applicability::MachineApplicable + } else { + Applicability::MaybeIncorrect + }; + err.span_suggestion( + expr.span, help, suggestion, applicability + ); + } + } + ty::RawPtr(..) => { + let base = self.tcx.sess.source_map() + .span_to_snippet(base.span) + .unwrap_or_else(|_| self.tcx.hir().hir_to_pretty_string(base.hir_id)); + let msg = format!("`{}` is a raw pointer; try dereferencing it", base); + let suggestion = format!("(*{}).{}", base, field); + err.span_suggestion( + expr.span, + &msg, + suggestion, + Applicability::MaybeIncorrect, + ); + } + _ => {} + } + err + } else { + type_error_struct!(self.tcx().sess, field.span, expr_t, E0610, + "`{}` is a primitive type and therefore doesn't have fields", + expr_t) + }.emit(); + self.tcx().types.err + } + } + + fn no_such_field_err(&self, span: Span, field: T, expr_t: &ty::TyS<'_>) + -> DiagnosticBuilder<'_> { + type_error_struct!(self.tcx().sess, span, expr_t, E0609, + "no field `{}` on type `{}`", + field, expr_t) + } + fn check_expr_index( &self, base: &'tcx hir::Expr, diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs index ae894a1145205..4c8ad66441f32 100644 --- a/src/librustc_typeck/check/mod.rs +++ b/src/librustc_typeck/check/mod.rs @@ -106,7 +106,7 @@ use rustc::middle::region; use rustc::mir::interpret::{ConstValue, GlobalId}; use rustc::traits::{self, ObligationCause, ObligationCauseCode, TraitEngine}; use rustc::ty::{ - self, AdtKind, CanonicalUserType, Ty, TyCtxt, Const, GenericParamDefKind, Visibility, + self, AdtKind, CanonicalUserType, Ty, TyCtxt, Const, GenericParamDefKind, ToPolyTraitRef, ToPredicate, RegionKind, UserType }; use rustc::ty::adjustment::{ @@ -124,13 +124,11 @@ use syntax::attr; use syntax::feature_gate::{GateIssue, emit_feature_err}; use syntax::ptr::P; use syntax::source_map::{DUMMY_SP, original_sp}; -use syntax::symbol::{Symbol, LocalInternedString, kw, sym}; -use syntax::util::lev_distance::find_best_match_for_name; +use syntax::symbol::{kw, sym}; use std::cell::{Cell, RefCell, Ref, RefMut}; use std::collections::hash_map::Entry; use std::cmp; -use std::fmt::Display; use std::iter; use std::mem::replace; use std::ops::{self, Deref}; @@ -143,7 +141,7 @@ use crate::TypeAndSubsts; use crate::lint; use crate::util::captures::Captures; use crate::util::common::{ErrorReported, indenter}; -use crate::util::nodemap::{DefIdMap, DefIdSet, FxHashMap, FxHashSet, HirIdMap}; +use crate::util::nodemap::{DefIdMap, DefIdSet, FxHashSet, HirIdMap}; pub use self::Expectation::*; use self::autoderef::Autoderef; @@ -3266,407 +3264,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { expect_args } - // Check field access expressions - fn check_field( - &self, - expr: &'tcx hir::Expr, - needs: Needs, - base: &'tcx hir::Expr, - field: ast::Ident, - ) -> Ty<'tcx> { - let expr_t = self.check_expr_with_needs(base, needs); - let expr_t = self.structurally_resolved_type(base.span, - expr_t); - let mut private_candidate = None; - let mut autoderef = self.autoderef(expr.span, expr_t); - while let Some((base_t, _)) = autoderef.next() { - match base_t.sty { - ty::Adt(base_def, substs) if !base_def.is_enum() => { - debug!("struct named {:?}", base_t); - let (ident, def_scope) = - self.tcx.adjust_ident_and_get_scope(field, base_def.did, self.body_id); - let fields = &base_def.non_enum_variant().fields; - if let Some(index) = fields.iter().position(|f| f.ident.modern() == ident) { - let field = &fields[index]; - let field_ty = self.field_ty(expr.span, field, substs); - // Save the index of all fields regardless of their visibility in case - // of error recovery. - self.write_field_index(expr.hir_id, index); - if field.vis.is_accessible_from(def_scope, self.tcx) { - let adjustments = autoderef.adjust_steps(self, needs); - self.apply_adjustments(base, adjustments); - autoderef.finalize(self); - - self.tcx.check_stability(field.did, Some(expr.hir_id), expr.span); - return field_ty; - } - private_candidate = Some((base_def.did, field_ty)); - } - } - ty::Tuple(ref tys) => { - let fstr = field.as_str(); - if let Ok(index) = fstr.parse::() { - if fstr == index.to_string() { - if let Some(field_ty) = tys.get(index) { - let adjustments = autoderef.adjust_steps(self, needs); - self.apply_adjustments(base, adjustments); - autoderef.finalize(self); - - self.write_field_index(expr.hir_id, index); - return field_ty.expect_ty(); - } - } - } - } - _ => {} - } - } - autoderef.unambiguous_final_ty(self); - - if let Some((did, field_ty)) = private_candidate { - let struct_path = self.tcx().def_path_str(did); - let mut err = struct_span_err!(self.tcx().sess, expr.span, E0616, - "field `{}` of struct `{}` is private", - field, struct_path); - // Also check if an accessible method exists, which is often what is meant. - if self.method_exists(field, expr_t, expr.hir_id, false) - && !self.expr_in_place(expr.hir_id) - { - self.suggest_method_call( - &mut err, - &format!("a method `{}` also exists, call it with parentheses", field), - field, - expr_t, - expr.hir_id, - ); - } - err.emit(); - field_ty - } else if field.name == kw::Invalid { - self.tcx().types.err - } else if self.method_exists(field, expr_t, expr.hir_id, true) { - let mut err = type_error_struct!(self.tcx().sess, field.span, expr_t, E0615, - "attempted to take value of method `{}` on type `{}`", - field, expr_t); - - if !self.expr_in_place(expr.hir_id) { - self.suggest_method_call( - &mut err, - "use parentheses to call the method", - field, - expr_t, - expr.hir_id - ); - } else { - err.help("methods are immutable and cannot be assigned to"); - } - - err.emit(); - self.tcx().types.err - } else { - if !expr_t.is_primitive_ty() { - let mut err = self.no_such_field_err(field.span, field, expr_t); - - match expr_t.sty { - ty::Adt(def, _) if !def.is_enum() => { - if let Some(suggested_field_name) = - Self::suggest_field_name(def.non_enum_variant(), - &field.as_str(), vec![]) { - err.span_suggestion( - field.span, - "a field with a similar name exists", - suggested_field_name.to_string(), - Applicability::MaybeIncorrect, - ); - } else { - err.span_label(field.span, "unknown field"); - let struct_variant_def = def.non_enum_variant(); - let field_names = self.available_field_names(struct_variant_def); - if !field_names.is_empty() { - err.note(&format!("available fields are: {}", - self.name_series_display(field_names))); - } - }; - } - ty::Array(_, len) => { - if let (Some(len), Ok(user_index)) = ( - len.assert_usize(self.tcx), - field.as_str().parse::() - ) { - let base = self.tcx.sess.source_map() - .span_to_snippet(base.span) - .unwrap_or_else(|_| - self.tcx.hir().hir_to_pretty_string(base.hir_id)); - let help = "instead of using tuple indexing, use array indexing"; - let suggestion = format!("{}[{}]", base, field); - let applicability = if len < user_index { - Applicability::MachineApplicable - } else { - Applicability::MaybeIncorrect - }; - err.span_suggestion( - expr.span, help, suggestion, applicability - ); - } - } - ty::RawPtr(..) => { - let base = self.tcx.sess.source_map() - .span_to_snippet(base.span) - .unwrap_or_else(|_| self.tcx.hir().hir_to_pretty_string(base.hir_id)); - let msg = format!("`{}` is a raw pointer; try dereferencing it", base); - let suggestion = format!("(*{}).{}", base, field); - err.span_suggestion( - expr.span, - &msg, - suggestion, - Applicability::MaybeIncorrect, - ); - } - _ => {} - } - err - } else { - type_error_struct!(self.tcx().sess, field.span, expr_t, E0610, - "`{}` is a primitive type and therefore doesn't have fields", - expr_t) - }.emit(); - self.tcx().types.err - } - } - - // Return an hint about the closest match in field names - fn suggest_field_name(variant: &'tcx ty::VariantDef, - field: &str, - skip: Vec) - -> Option { - let names = variant.fields.iter().filter_map(|field| { - // ignore already set fields and private fields from non-local crates - if skip.iter().any(|x| *x == field.ident.as_str()) || - (!variant.def_id.is_local() && field.vis != Visibility::Public) - { - None - } else { - Some(&field.ident.name) - } - }); - - find_best_match_for_name(names, field, None) - } - - fn available_field_names(&self, variant: &'tcx ty::VariantDef) -> Vec { - variant.fields.iter().filter(|field| { - let def_scope = - self.tcx.adjust_ident_and_get_scope(field.ident, variant.def_id, self.body_id).1; - field.vis.is_accessible_from(def_scope, self.tcx) - }) - .map(|field| field.ident.name) - .collect() - } - - fn name_series_display(&self, names: Vec) -> String { - // dynamic limit, to never omit just one field - let limit = if names.len() == 6 { 6 } else { 5 }; - let mut display = names.iter().take(limit) - .map(|n| format!("`{}`", n)).collect::>().join(", "); - if names.len() > limit { - display = format!("{} ... and {} others", display, names.len() - limit); - } - display - } - - fn no_such_field_err(&self, span: Span, field: T, expr_t: &ty::TyS<'_>) - -> DiagnosticBuilder<'_> { - type_error_struct!(self.tcx().sess, span, expr_t, E0609, - "no field `{}` on type `{}`", - field, expr_t) - } - - fn report_unknown_field( - &self, - ty: Ty<'tcx>, - variant: &'tcx ty::VariantDef, - field: &hir::Field, - skip_fields: &[hir::Field], - kind_name: &str, - ) { - if variant.recovered { - return; - } - let mut err = self.type_error_struct_with_diag( - field.ident.span, - |actual| match ty.sty { - ty::Adt(adt, ..) if adt.is_enum() => { - struct_span_err!(self.tcx.sess, field.ident.span, E0559, - "{} `{}::{}` has no field named `{}`", - kind_name, actual, variant.ident, field.ident) - } - _ => { - struct_span_err!(self.tcx.sess, field.ident.span, E0560, - "{} `{}` has no field named `{}`", - kind_name, actual, field.ident) - } - }, - ty); - // prevent all specified fields from being suggested - let skip_fields = skip_fields.iter().map(|ref x| x.ident.as_str()); - if let Some(field_name) = Self::suggest_field_name(variant, - &field.ident.as_str(), - skip_fields.collect()) { - err.span_suggestion( - field.ident.span, - "a field with a similar name exists", - field_name.to_string(), - Applicability::MaybeIncorrect, - ); - } else { - match ty.sty { - ty::Adt(adt, ..) => { - if adt.is_enum() { - err.span_label(field.ident.span, - format!("`{}::{}` does not have this field", - ty, variant.ident)); - } else { - err.span_label(field.ident.span, - format!("`{}` does not have this field", ty)); - } - let available_field_names = self.available_field_names(variant); - if !available_field_names.is_empty() { - err.note(&format!("available fields are: {}", - self.name_series_display(available_field_names))); - } - } - _ => bug!("non-ADT passed to report_unknown_field") - } - }; - err.emit(); - } - - fn check_expr_struct_fields( - &self, - adt_ty: Ty<'tcx>, - expected: Expectation<'tcx>, - expr_id: hir::HirId, - span: Span, - variant: &'tcx ty::VariantDef, - ast_fields: &'tcx [hir::Field], - check_completeness: bool, - ) -> bool { - let tcx = self.tcx; - - let adt_ty_hint = - self.expected_inputs_for_expected_output(span, expected, adt_ty, &[adt_ty]) - .get(0).cloned().unwrap_or(adt_ty); - // re-link the regions that EIfEO can erase. - self.demand_eqtype(span, adt_ty_hint, adt_ty); - - let (substs, adt_kind, kind_name) = match &adt_ty.sty { - &ty::Adt(adt, substs) => { - (substs, adt.adt_kind(), adt.variant_descr()) - } - _ => span_bug!(span, "non-ADT passed to check_expr_struct_fields") - }; - - let mut remaining_fields = variant.fields.iter().enumerate().map(|(i, field)| - (field.ident.modern(), (i, field)) - ).collect::>(); - - let mut seen_fields = FxHashMap::default(); - - let mut error_happened = false; - - // Type-check each field. - for field in ast_fields { - let ident = tcx.adjust_ident(field.ident, variant.def_id); - let field_type = if let Some((i, v_field)) = remaining_fields.remove(&ident) { - seen_fields.insert(ident, field.span); - self.write_field_index(field.hir_id, i); - - // We don't look at stability attributes on - // struct-like enums (yet...), but it's definitely not - // a bug to have constructed one. - if adt_kind != AdtKind::Enum { - tcx.check_stability(v_field.did, Some(expr_id), field.span); - } - - self.field_ty(field.span, v_field, substs) - } else { - error_happened = true; - if let Some(prev_span) = seen_fields.get(&ident) { - let mut err = struct_span_err!(self.tcx.sess, - field.ident.span, - E0062, - "field `{}` specified more than once", - ident); - - err.span_label(field.ident.span, "used more than once"); - err.span_label(*prev_span, format!("first use of `{}`", ident)); - - err.emit(); - } else { - self.report_unknown_field(adt_ty, variant, field, ast_fields, kind_name); - } - - tcx.types.err - }; - - // Make sure to give a type to the field even if there's - // an error, so we can continue type-checking. - self.check_expr_coercable_to_type(&field.expr, field_type); - } - - // Make sure the programmer specified correct number of fields. - if kind_name == "union" { - if ast_fields.len() != 1 { - tcx.sess.span_err(span, "union expressions should have exactly one field"); - } - } else if check_completeness && !error_happened && !remaining_fields.is_empty() { - let len = remaining_fields.len(); - - let mut displayable_field_names = remaining_fields - .keys() - .map(|ident| ident.as_str()) - .collect::>(); - - displayable_field_names.sort(); - - let truncated_fields_error = if len <= 3 { - String::new() - } else { - format!(" and {} other field{}", (len - 3), if len - 3 == 1 {""} else {"s"}) - }; - - let remaining_fields_names = displayable_field_names.iter().take(3) - .map(|n| format!("`{}`", n)) - .collect::>() - .join(", "); - - struct_span_err!(tcx.sess, span, E0063, - "missing field{} {}{} in initializer of `{}`", - if remaining_fields.len() == 1 { "" } else { "s" }, - remaining_fields_names, - truncated_fields_error, - adt_ty) - .span_label(span, format!("missing {}{}", - remaining_fields_names, - truncated_fields_error)) - .emit(); - } - error_happened - } - - fn check_struct_fields_on_error( - &self, - fields: &'tcx [hir::Field], - base_expr: &'tcx Option>, - ) { - for field in fields { - self.check_expr(&field.expr); - } - if let Some(ref base) = *base_expr { - self.check_expr(&base); - } - } - pub fn check_struct_path(&self, qpath: &QPath, hir_id: hir::HirId)