Made suggestion diagnostics more consistent for enum variant names, relative to method names.

This commit is contained in:
Alexander Regueiro 2018-12-03 00:56:47 +00:00
parent ecacad0770
commit 767ffced91
5 changed files with 84 additions and 69 deletions

View file

@ -1277,7 +1277,7 @@ impl<'o, 'gcx: 'tcx, 'tcx> dyn AstConv<'gcx, 'tcx> + 'o {
ref_id: ast::NodeId,
span: Span,
ty: Ty<'tcx>,
ty_hir: &hir::Ty,
qself: &hir::Ty,
ty_path_def: Def,
item_segment: &hir::PathSegment)
-> (Ty<'tcx>, Def)
@ -1292,11 +1292,11 @@ impl<'o, 'gcx: 'tcx, 'tcx> dyn AstConv<'gcx, 'tcx> + 'o {
// Check if we have an enum variant here.
if let ty::Adt(adt_def, _) = ty.sty {
if adt_def.is_enum() {
if allow_type_alias_enum_variants(tcx, ty_hir, span) {
let variant_def = adt_def.variants.iter().find(|vd| {
tcx.hygienic_eq(assoc_name, vd.ident, adt_def.did)
});
if let Some(variant_def) = variant_def {
let variant_def = adt_def.variants.iter().find(|vd| {
tcx.hygienic_eq(assoc_name, vd.ident, adt_def.did)
});
if let Some(variant_def) = variant_def {
if allow_type_alias_enum_variants(tcx, qself, span) {
let def = Def::Variant(variant_def.did);
return (ty, def);
}

View file

@ -8,7 +8,7 @@ mod suggest;
pub use self::MethodError::*;
pub use self::CandidateSource::*;
pub use self::suggest::TraitInfo;
pub use self::suggest::{SelfSource, TraitInfo};
use check::FnCtxt;
use namespace::Namespace;
@ -361,7 +361,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
span: Span,
method_name: ast::Ident,
self_ty: Ty<'tcx>,
self_ty_hir: &hir::Ty,
qself: &hir::Ty,
expr_id: ast::NodeId)
-> Result<Def, MethodError<'tcx>> {
debug!("resolve_ufcs: method_name={:?} self_ty={:?} expr_id={:?}",
@ -375,11 +375,11 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
// Check if we have an enum variant here.
if let ty::Adt(adt_def, _) = self_ty.sty {
if adt_def.is_enum() {
if allow_type_alias_enum_variants(tcx, self_ty_hir, span) {
let variant_def = adt_def.variants.iter().find(|vd| {
tcx.hygienic_eq(method_name, vd.ident, adt_def.did)
});
if let Some(variant_def) = variant_def {
let variant_def = adt_def.variants.iter().find(|vd| {
tcx.hygienic_eq(method_name, vd.ident, adt_def.did)
});
if let Some(variant_def) = variant_def {
if allow_type_alias_enum_variants(tcx, qself, span) {
let def = Def::VariantCtor(variant_def.did, variant_def.ctor_kind);
return Ok(def);
}

View file

@ -6,7 +6,7 @@ use errors::{Applicability, DiagnosticBuilder};
use middle::lang_items::FnOnceTraitLangItem;
use namespace::Namespace;
use rustc_data_structures::sync::Lrc;
use rustc::hir::{self, Node};
use rustc::hir::{self, ExprKind, Node, QPath};
use rustc::hir::def::Def;
use rustc::hir::def_id::{CRATE_DEF_INDEX, LOCAL_CRATE, DefId};
use rustc::hir::map as hir_map;
@ -60,13 +60,13 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
}
}
pub fn report_method_error(&self,
span: Span,
rcvr_ty: Ty<'tcx>,
item_name: ast::Ident,
rcvr_expr: Option<&hir::Expr>,
error: MethodError<'tcx>,
args: Option<&'gcx [hir::Expr]>) {
pub fn report_method_error<'b>(&self,
span: Span,
rcvr_ty: Ty<'tcx>,
item_name: ast::Ident,
source: SelfSource<'b>,
error: MethodError<'tcx>,
args: Option<&'gcx [hir::Expr]>) {
// Avoid suggestions when we don't know what's going on.
if rcvr_ty.references_error() {
return;
@ -212,10 +212,11 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
.filter_map(|info|
self.associated_item(info.def_id, item_name, Namespace::Value)
);
if let (true, false, Some(expr), Some(_)) = (actual.is_numeric(),
actual.has_concrete_skeleton(),
rcvr_expr,
candidates.next()) {
if let (true, false, SelfSource::MethodCall(expr), Some(_)) =
(actual.is_numeric(),
actual.has_concrete_skeleton(),
source,
candidates.next()) {
let mut err = struct_span_err!(
tcx.sess,
span,
@ -231,7 +232,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
"f32"
};
match expr.node {
hir::ExprKind::Lit(ref lit) => {
ExprKind::Lit(ref lit) => {
// numeric literal
let snippet = tcx.sess.source_map().span_to_snippet(lit.span)
.unwrap_or_else(|_| "<numeric literal>".to_owned());
@ -247,9 +248,9 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
Applicability::MaybeIncorrect,
);
}
hir::ExprKind::Path(ref qpath) => {
ExprKind::Path(ref qpath) => {
// local binding
if let &hir::QPath::Resolved(_, ref path) = &qpath {
if let &QPath::Resolved(_, ref path) = &qpath {
if let hir::def::Def::Local(node_id) = path.def {
let span = tcx.hir().span(node_id);
let snippet = tcx.sess.source_map().span_to_snippet(span)
@ -294,7 +295,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
} else {
let mut err = struct_span_err!(
tcx.sess,
span,
item_name.span,
E0599,
"no {} named `{}` found for type `{}` in the current scope",
item_kind,
@ -302,7 +303,8 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
ty_str
);
if let Some(suggestion) = suggestion {
err.note(&format!("did you mean `{}::{}`?", ty_str, suggestion));
// enum variant
err.help(&format!("did you mean `{}`?", suggestion));
}
err
}
@ -326,7 +328,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
// If the method name is the name of a field with a function or closure type,
// give a helping note that it has to be called as `(x.f)(...)`.
if let Some(expr) = rcvr_expr {
if let SelfSource::MethodCall(expr) = source {
for (ty, _) in self.autoderef(span, rcvr_ty) {
if let ty::Adt(def, substs) = ty.sty {
if !def.is_enum() {
@ -377,10 +379,10 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
}
}
if let Some(expr) = rcvr_expr {
if let SelfSource::MethodCall(expr) = source {
if let Ok(expr_string) = tcx.sess.source_map().span_to_snippet(expr.span) {
report_function!(expr.span, expr_string);
} else if let hir::ExprKind::Path(hir::QPath::Resolved(_, ref path)) =
} else if let ExprKind::Path(QPath::Resolved(_, ref path)) =
expr.node
{
if let Some(segment) = path.segments.last() {
@ -396,7 +398,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
err.span_label(span, "this is an associated function, not a method");
}
if static_sources.len() == 1 {
if let Some(expr) = rcvr_expr {
if let SelfSource::MethodCall(expr) = source {
err.span_suggestion_with_applicability(expr.span.to(span),
"use associated function syntax instead",
format!("{}::{}",
@ -433,7 +435,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
span,
rcvr_ty,
item_name,
rcvr_expr,
source,
out_of_scope_traits);
}
@ -571,18 +573,18 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
}
}
fn suggest_traits_to_import(&self,
err: &mut DiagnosticBuilder,
span: Span,
rcvr_ty: Ty<'tcx>,
item_name: ast::Ident,
rcvr_expr: Option<&hir::Expr>,
valid_out_of_scope_traits: Vec<DefId>) {
fn suggest_traits_to_import<'b>(&self,
err: &mut DiagnosticBuilder,
span: Span,
rcvr_ty: Ty<'tcx>,
item_name: ast::Ident,
source: SelfSource<'b>,
valid_out_of_scope_traits: Vec<DefId>) {
if self.suggest_valid_traits(err, valid_out_of_scope_traits) {
return;
}
let type_is_local = self.type_derefs_to_local(span, rcvr_ty, rcvr_expr);
let type_is_local = self.type_derefs_to_local(span, rcvr_ty, source);
// There are no traits implemented, so lets suggest some traits to
// implement, by finding ones that have the item name, and are
@ -663,7 +665,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
// This occurs for UFCS desugaring of `T::method`, where there is no
// receiver expression for the method call, and thus no autoderef.
if rcvr_expr.is_none() {
if let SelfSource::QPath(_) = source {
return is_local(self.resolve_type_vars_with_obligations(rcvr_ty));
}
@ -671,6 +673,12 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
}
}
#[derive(Copy, Clone)]
pub enum SelfSource<'a> {
QPath(&'a hir::Ty),
MethodCall(&'a hir::Expr /* rcvr */),
}
#[derive(Copy, Clone)]
pub struct TraitInfo {
pub def_id: DefId,

View file

@ -142,7 +142,7 @@ use self::autoderef::Autoderef;
use self::callee::DeferredCallResolution;
use self::coercion::{CoerceMany, DynamicCoerceMany};
pub use self::compare_method::{compare_impl_method, compare_const_impl};
use self::method::MethodCallee;
use self::method::{MethodCallee, SelfSource};
use self::TupleArgumentsFlag::*;
/// The type of a local binding, including the revealed type for anon types.
@ -3244,7 +3244,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
self.report_method_error(span,
rcvr_t,
segment.ident,
Some(rcvr),
SelfSource::MethodCall(rcvr),
error,
Some(args));
}
@ -4558,7 +4558,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
span: Span)
-> (Def, Option<Ty<'tcx>>, &'b [hir::PathSegment])
{
let (ty, ty_hir, item_segment) = match *qpath {
let (ty, qself, item_segment) = match *qpath {
hir::QPath::Resolved(ref opt_qself, ref path) => {
return (path.def,
opt_qself.as_ref().map(|qself| self.to_ty(qself)),
@ -4575,7 +4575,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
return (*cached_def, Some(ty), slice::from_ref(&**item_segment))
}
let item_name = item_segment.ident;
let def = match self.resolve_ufcs(span, item_name, ty, ty_hir, node_id) {
let def = match self.resolve_ufcs(span, item_name, ty, qself, node_id) {
Ok(def) => def,
Err(error) => {
let def = match error {
@ -4583,7 +4583,12 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
_ => Def::Err,
};
if item_name.name != keywords::Invalid.name() {
self.report_method_error(span, ty, item_name, None, error, None);
self.report_method_error(span,
ty,
item_name,
SelfSource::QPath(qself),
error,
None);
}
def
}
@ -5114,7 +5119,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
// Case 2. Reference to a variant constructor.
Def::VariantCtor(def_id, ..) => {
if tcx.features().type_alias_enum_variants {
if self.tcx.features().type_alias_enum_variants {
let adt_def = self_ty.and_then(|t| t.ty_adt_def());
let (generics_def_id, index) = if let Some(adt_def) = adt_def {
debug_assert!(adt_def.is_enum());
@ -5192,16 +5197,18 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
node_id,
);
let tcx = self.tcx;
let path_segs = self.def_ids_for_path_segments(segments, self_ty, def);
let mut user_self_ty = None;
match def {
Def::Method(def_id) |
Def::AssociatedConst(def_id) => {
let container = self.tcx.associated_item(def_id).container;
let container = tcx.associated_item(def_id).container;
match container {
ty::TraitContainer(trait_did) => {
callee::check_legal_trait_for_method_call(self.tcx, span, trait_did)
callee::check_legal_trait_for_method_call(tcx, span, trait_did)
}
ty::ImplContainer(impl_def_id) => {
if segments.len() == 1 {
@ -5231,7 +5238,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
match def {
Def::VariantCtor(_, _) if self_ty.is_some() => true,
_ => false,
};
}
} else {
false
};
@ -5249,7 +5256,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
Def::Local(nid) | Def::Upvar(nid, ..) => {
let ty = self.local_ty(span, nid).decl_ty;
let ty = self.normalize_associated_types_in(span, &ty);
self.write_ty(self.tcx.hir().node_to_hir_id(node_id), ty);
self.write_ty(tcx.hir().node_to_hir_id(node_id), ty);
return (ty, def);
}
_ => {}
@ -5265,13 +5272,13 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
let mut infer_args_for_err = FxHashSet::default();
for &PathSeg(def_id, index) in &path_segs {
let seg = &segments[index];
let generics = self.tcx.generics_of(def_id);
let generics = tcx.generics_of(def_id);
// Argument-position `impl Trait` is treated as a normal generic
// parameter internally, but we don't allow users to specify the
// parameter's value explicitly, so we have to do some error-
// checking here.
let suppress_errors = AstConv::check_generic_arg_count_for_call(
self.tcx,
tcx,
span,
&generics,
&seg,
@ -5284,7 +5291,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
}
let has_self = path_segs.last().map(|PathSeg(def_id, _)| {
self.tcx.generics_of(*def_id).has_self
tcx.generics_of(*def_id).has_self
}).unwrap_or(false);
let mut new_def = def;
@ -5297,10 +5304,10 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
Some(adt_def) if adt_def.has_ctor() => {
let variant = adt_def.non_enum_variant();
new_def = Def::StructCtor(variant.did, variant.ctor_kind);
(variant.did, self.tcx.type_of(variant.did))
(variant.did, tcx.type_of(variant.did))
}
_ => {
let mut err = self.tcx.sess.struct_span_err(span,
let mut err = tcx.sess.struct_span_err(span,
"the `Self` constructor can only be used with tuple or unit structs");
if let Some(adt_def) = adt_def {
match adt_def.adt_kind() {
@ -5318,14 +5325,14 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
}
err.emit();
(impl_def_id, self.tcx.types.err)
(impl_def_id, tcx.types.err)
}
}
}
Def::VariantCtor(_, _) if self_ty.is_some() => {
let def_id = def.def_id();
let ty = self.tcx.type_of(def_id);
let ty = tcx.type_of(def_id);
if tcx.features().type_alias_enum_variants {
if let Some(self_ty) = self_ty {
match ty.ty_adt_def() {
@ -5343,13 +5350,13 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
// The things we are substituting into the type should not contain
// escaping late-bound regions, and nor should the base type scheme.
let ty = self.tcx.type_of(def_id);
let ty = tcx.type_of(def_id);
(def_id, ty)
}
};
let substs = AstConv::create_substs_for_generic_args(
self.tcx,
tcx,
def_id,
&[][..],
has_self,
@ -5395,10 +5402,10 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
// If we have a default, then we it doesn't matter that we're not
// inferring the type arguments: we provide the default where any
// is missing.
let default = self.tcx.type_of(param.def_id);
let default = tcx.type_of(param.def_id);
self.normalize_ty(
span,
default.subst_spanned(self.tcx, substs.unwrap(), Some(span))
default.subst_spanned(tcx, substs.unwrap(), Some(span))
).into()
} else {
// If no type arguments were provided, we have to infer them.
@ -5415,7 +5422,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
assert!(!ty.has_escaping_bound_vars());
// First, store the "user substs" for later.
let hir_id = self.tcx.hir().node_to_hir_id(node_id);
let hir_id = tcx.hir().node_to_hir_id(node_id);
self.write_user_substs_from_substs(hir_id, substs, user_self_ty);
// Add all the obligations that are required, substituting and
@ -5434,7 +5441,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
// is inherent, there is no `Self` parameter, instead, the impl needs
// type parameters, which we can infer by unifying the provided `Self`
// with the substituted impl type.
let ty = self.tcx.type_of(impl_def_id);
let ty = tcx.type_of(impl_def_id);
let impl_ty = self.instantiate_type_scheme(span, &substs, &ty);
match self.at(&self.misc(span), self.param_env).sup(impl_ty, self_ty) {

View file

@ -132,12 +132,12 @@ pub struct TypeAndSubsts<'tcx> {
}
fn allow_type_alias_enum_variants<'a, 'gcx, 'tcx>(tcx: TyCtxt<'a, 'gcx, 'tcx>,
ty_hir: &hir::Ty,
qself: &hir::Ty,
span: Span) -> bool {
let allow_feature = tcx.features().type_alias_enum_variants;
if !allow_feature {
// Only print error if we know the type is an alias.
if let hir::TyKind::Path(hir::QPath::Resolved(None, ref path)) = ty_hir.node {
if let hir::TyKind::Path(hir::QPath::Resolved(None, ref path)) = qself.node {
if let Def::TyAlias(_) = path.def {
let mut err = tcx.sess.struct_span_err(
span,