use crate::lint;
use crate::middle::resolve_lifetime as rl;
use crate::middle::weak_lang_items;
+use rustc::hir::intravisit::{self, NestedVisitorMap, Visitor};
+use rustc::middle::codegen_fn_attrs::{CodegenFnAttrFlags, CodegenFnAttrs};
use rustc::mir::mono::Linkage;
use rustc::ty::query::Providers;
use rustc::ty::subst::GenericArgKind;
use rustc::ty::{self, AdtKind, Const, DefIdTree, ToPolyTraitRef, Ty, TyCtxt};
use rustc::ty::{ReprOptions, ToPredicate};
use rustc::util::captures::Captures;
-use rustc::util::nodemap::FxHashMap;
+use rustc_data_structures::fx::FxHashMap;
+use rustc_hir as hir;
+use rustc_hir::def::{CtorKind, DefKind, Res};
+use rustc_hir::def_id::{DefId, LOCAL_CRATE};
+use rustc_hir::{GenericParamKind, Node, Unsafety};
+use rustc_span::symbol::{kw, sym, Symbol};
+use rustc_span::{Span, DUMMY_SP};
use rustc_target::spec::abi;
-
use syntax::ast;
use syntax::ast::{Ident, MetaItemKind};
use syntax::attr::{list_contains_name, mark_used, InlineAttr, OptimizeAttr};
use syntax::feature_gate;
-use syntax::symbol::{kw, sym, Symbol};
-use syntax_pos::{Span, DUMMY_SP};
-
-use rustc::hir::def::{CtorKind, DefKind, Res};
-use rustc::hir::def_id::{DefId, LOCAL_CRATE};
-use rustc::hir::intravisit::{self, NestedVisitorMap, Visitor};
-use rustc::hir::GenericParamKind;
-use rustc::hir::Node;
-use rustc::hir::{self, CodegenFnAttrFlags, CodegenFnAttrs, Unsafety};
use errors::{Applicability, StashKey};
///////////////////////////////////////////////////////////////////////////
+#[derive(Default)]
+crate struct PlaceholderHirTyCollector(crate Vec<Span>);
+
+impl<'v> Visitor<'v> for PlaceholderHirTyCollector {
+ fn nested_visit_map<'this>(&'this mut self) -> NestedVisitorMap<'this, 'v> {
+ NestedVisitorMap::None
+ }
+ fn visit_ty(&mut self, t: &'v hir::Ty<'v>) {
+ if let hir::TyKind::Infer = t.kind {
+ self.0.push(t.span);
+ }
+ intravisit::walk_ty(self, t)
+ }
+}
+
struct CollectItemTypesVisitor<'tcx> {
tcx: TyCtxt<'tcx>,
}
+/// If there are any placeholder types (`_`), emit an error explaining that this is not allowed
+/// and suggest adding type parameters in the appropriate place, taking into consideration any and
+/// all already existing generic type parameters to avoid suggesting a name that is already in use.
+crate fn placeholder_type_error(
+ tcx: TyCtxt<'tcx>,
+ ident_span: Span,
+ generics: &[hir::GenericParam<'_>],
+ placeholder_types: Vec<Span>,
+ suggest: bool,
+) {
+ if placeholder_types.is_empty() {
+ return;
+ }
+ // This is the whitelist of possible parameter names that we might suggest.
+ let possible_names = ["T", "K", "L", "A", "B", "C"];
+ let used_names = generics
+ .iter()
+ .filter_map(|p| match p.name {
+ hir::ParamName::Plain(ident) => Some(ident.name),
+ _ => None,
+ })
+ .collect::<Vec<_>>();
+
+ let type_name = possible_names
+ .iter()
+ .find(|n| !used_names.contains(&Symbol::intern(n)))
+ .unwrap_or(&"ParamName");
+
+ let mut sugg: Vec<_> =
+ placeholder_types.iter().map(|sp| (*sp, type_name.to_string())).collect();
+ if generics.is_empty() {
+ sugg.push((ident_span.shrink_to_hi(), format!("<{}>", type_name)));
+ } else {
+ sugg.push((
+ generics.iter().last().unwrap().span.shrink_to_hi(),
+ format!(", {}", type_name),
+ ));
+ }
+ let mut err = bad_placeholder_type(tcx, placeholder_types);
+ if suggest {
+ err.multipart_suggestion(
+ "use type parameters instead",
+ sugg,
+ Applicability::HasPlaceholders,
+ );
+ }
+ err.emit();
+}
+
+fn reject_placeholder_type_signatures_in_item(tcx: TyCtxt<'tcx>, item: &'tcx hir::Item<'tcx>) {
+ let (generics, suggest) = match &item.kind {
+ hir::ItemKind::Union(_, generics)
+ | hir::ItemKind::Enum(_, generics)
+ | hir::ItemKind::Struct(_, generics) => (&generics.params[..], true),
+ hir::ItemKind::TyAlias(_, generics) => (&generics.params[..], false),
+ // `static`, `fn` and `const` are handled elsewhere to suggest appropriate type.
+ _ => return,
+ };
+
+ let mut visitor = PlaceholderHirTyCollector::default();
+ visitor.visit_item(item);
+
+ placeholder_type_error(tcx, item.ident.span, generics, visitor.0, suggest);
+}
+
impl Visitor<'tcx> for CollectItemTypesVisitor<'tcx> {
fn nested_visit_map<'this>(&'this mut self) -> NestedVisitorMap<'this, 'tcx> {
NestedVisitorMap::OnlyBodies(&self.tcx.hir())
fn visit_item(&mut self, item: &'tcx hir::Item<'tcx>) {
convert_item(self.tcx, item.hir_id);
+ reject_placeholder_type_signatures_in_item(self.tcx, item);
intravisit::walk_item(self, item);
}
fn visit_generics(&mut self, generics: &'tcx hir::Generics<'tcx>) {
- for param in &generics.params {
+ for param in generics.params {
match param.kind {
hir::GenericParamKind::Lifetime { .. } => {}
hir::GenericParamKind::Type { default: Some(_), .. } => {
///////////////////////////////////////////////////////////////////////////
// Utility types and common code for the above passes.
-fn bad_placeholder_type(tcx: TyCtxt<'tcx>, span: Span) -> errors::DiagnosticBuilder<'tcx> {
- let mut diag = struct_span_err!(
+fn bad_placeholder_type(
+ tcx: TyCtxt<'tcx>,
+ mut spans: Vec<Span>,
+) -> errors::DiagnosticBuilder<'tcx> {
+ spans.sort();
+ let mut err = struct_span_err!(
tcx.sess,
- span,
+ spans.clone(),
E0121,
"the type placeholder `_` is not allowed within types on item signatures",
);
- diag.span_label(span, "not allowed in type signatures");
- diag
+ for span in spans {
+ err.span_label(span, "not allowed in type signatures");
+ }
+ err
}
impl ItemCtxt<'tcx> {
None
}
- fn ty_infer(&self, _: Option<&ty::GenericParamDef>, span: Span) -> Ty<'tcx> {
- bad_placeholder_type(self.tcx(), span).emit();
+ fn allow_ty_infer(&self) -> bool {
+ false
+ }
+ fn ty_infer(&self, _: Option<&ty::GenericParamDef>, span: Span) -> Ty<'tcx> {
+ self.tcx().sess.delay_span_bug(span, "bad placeholder type");
self.tcx().types.err
}
_: Option<&ty::GenericParamDef>,
span: Span,
) -> &'tcx Const<'tcx> {
- bad_placeholder_type(self.tcx(), span).emit();
+ bad_placeholder_type(self.tcx(), vec![span]).emit();
self.tcx().consts.err
}
tcx: TyCtxt<'_>,
(item_def_id, def_id): (DefId, DefId),
) -> ty::GenericPredicates<'_> {
- use rustc::hir::*;
+ use rustc_hir::*;
// In the AST, bounds can derive from two places. Either
// written inline like `<T: Foo>` or in a where-clause like
}
fn adt_def(tcx: TyCtxt<'_>, def_id: DefId) -> &ty::AdtDef {
- use rustc::hir::*;
+ use rustc_hir::*;
let hir_id = tcx.hir().as_local_hir_id(def_id).unwrap();
let item = match tcx.hir().get(hir_id) {
outer_index: ty::INNERMOST,
has_late_bound_regions: None,
};
- for param in &generics.params {
+ for param in generics.params {
if let GenericParamKind::Lifetime { .. } = param.kind {
if tcx.is_late_bound(param.hir_id) {
return Some(param.span);
}
fn generics_of(tcx: TyCtxt<'_>, def_id: DefId) -> &ty::Generics {
- use rustc::hir::*;
+ use rustc_hir::*;
let hir_id = tcx.hir().as_local_hir_id(def_id).unwrap();
span: Span,
item_ident: Ident,
) -> Ty<'_> {
- let ty = tcx.typeck_tables_of(def_id).node_type(body_id.hir_id);
+ let ty = tcx.diagnostic_only_typeck_tables_of(def_id).node_type(body_id.hir_id);
// If this came from a free `const` or `static mut?` item,
// then the user may have written e.g. `const A = 42;`.
.emit();
}
None => {
- let mut diag = bad_placeholder_type(tcx, span);
+ let mut diag = bad_placeholder_type(tcx, vec![span]);
if ty != tcx.types.err {
diag.span_suggestion(
span,
}
fn type_of(tcx: TyCtxt<'_>, def_id: DefId) -> Ty<'_> {
- use rustc::hir::*;
+ use rustc_hir::*;
let hir_id = tcx.hir().as_local_hir_id(def_id).unwrap();
}
TraitItemKind::Const(ref ty, body_id) => body_id
.and_then(|body_id| {
- if let hir::TyKind::Infer = ty.kind {
+ if is_suggestable_infer_ty(ty) {
Some(infer_placeholder_type(tcx, def_id, body_id, ty.span, item.ident))
} else {
None
tcx.mk_fn_def(def_id, substs)
}
ImplItemKind::Const(ref ty, body_id) => {
- if let hir::TyKind::Infer = ty.kind {
+ if is_suggestable_infer_ty(ty) {
infer_placeholder_type(tcx, def_id, body_id, ty.span, item.ident)
} else {
icx.to_ty(ty)
Node::Item(item) => {
match item.kind {
ItemKind::Static(ref ty, .., body_id) | ItemKind::Const(ref ty, body_id) => {
- if let hir::TyKind::Infer = ty.kind {
+ if is_suggestable_infer_ty(ty) {
infer_placeholder_type(tcx, def_id, body_id, ty.span, item.ident)
} else {
icx.to_ty(ty)
}
fn find_opaque_ty_constraints(tcx: TyCtxt<'_>, def_id: DefId) -> Ty<'_> {
- use rustc::hir::{ImplItem, Item, TraitItem};
+ use rustc_hir::{ImplItem, Item, TraitItem};
debug!("find_opaque_ty_constraints({:?})", def_id);
}
}
+/// Whether `ty` is a type with `_` placeholders that can be infered. Used in diagnostics only to
+/// use inference to provide suggestions for the appropriate type if possible.
+fn is_suggestable_infer_ty(ty: &hir::Ty<'_>) -> bool {
+ match &ty.kind {
+ hir::TyKind::Infer => true,
+ hir::TyKind::Slice(ty) | hir::TyKind::Array(ty, _) => is_suggestable_infer_ty(ty),
+ hir::TyKind::Tup(tys) => tys.iter().any(|ty| is_suggestable_infer_ty(ty)),
+ _ => false,
+ }
+}
+
pub fn get_infer_ret_ty(output: &'hir hir::FunctionRetTy<'hir>) -> Option<&'hir hir::Ty<'hir>> {
if let hir::FunctionRetTy::Return(ref ty) = output {
- if let hir::TyKind::Infer = ty.kind {
+ if is_suggestable_infer_ty(ty) {
return Some(&**ty);
}
}
}
fn fn_sig(tcx: TyCtxt<'_>, def_id: DefId) -> ty::PolyFnSig<'_> {
- use rustc::hir::Node::*;
- use rustc::hir::*;
+ use rustc_hir::Node::*;
+ use rustc_hir::*;
let hir_id = tcx.hir().as_local_hir_id(def_id).unwrap();
match tcx.hir().get(hir_id) {
TraitItem(hir::TraitItem {
kind: TraitItemKind::Method(sig, TraitMethod::Provided(_)),
+ ident,
+ generics,
..
})
- | ImplItem(hir::ImplItem { kind: ImplItemKind::Method(sig, _), .. })
- | Item(hir::Item { kind: ItemKind::Fn(sig, _, _), .. }) => {
+ | ImplItem(hir::ImplItem { kind: ImplItemKind::Method(sig, _), ident, generics, .. })
+ | Item(hir::Item { kind: ItemKind::Fn(sig, generics, _), ident, .. }) => {
match get_infer_ret_ty(&sig.decl.output) {
Some(ty) => {
let fn_sig = tcx.typeck_tables_of(def_id).liberated_fn_sigs()[hir_id];
- let mut diag = bad_placeholder_type(tcx, ty.span);
+ let mut visitor = PlaceholderHirTyCollector::default();
+ visitor.visit_ty(ty);
+ let mut diag = bad_placeholder_type(tcx, visitor.0);
let ret_ty = fn_sig.output();
if ret_ty != tcx.types.err {
diag.span_suggestion(
ty.span,
- "replace `_` with the correct return type",
+ "replace with the correct return type",
ret_ty.to_string(),
Applicability::MaybeIncorrect,
);
diag.emit();
ty::Binder::bind(fn_sig)
}
- None => AstConv::ty_of_fn(&icx, sig.header.unsafety, sig.header.abi, &sig.decl),
+ None => AstConv::ty_of_fn(
+ &icx,
+ sig.header.unsafety,
+ sig.header.abi,
+ &sig.decl,
+ &generics.params[..],
+ Some(ident.span),
+ ),
}
}
TraitItem(hir::TraitItem {
kind: TraitItemKind::Method(FnSig { header, decl }, _),
+ ident,
+ generics,
..
- }) => AstConv::ty_of_fn(&icx, header.unsafety, header.abi, decl),
+ }) => AstConv::ty_of_fn(
+ &icx,
+ header.unsafety,
+ header.abi,
+ decl,
+ &generics.params[..],
+ Some(ident.span),
+ ),
ForeignItem(&hir::ForeignItem { kind: ForeignItemKind::Fn(ref fn_decl, _, _), .. }) => {
let abi = tcx.hir().get_foreign_abi(hir_id);
/// Returns a list of user-specified type predicates for the definition with ID `def_id`.
/// N.B., this does not include any implied/inferred constraints.
fn explicit_predicates_of(tcx: TyCtxt<'_>, def_id: DefId) -> ty::GenericPredicates<'_> {
- use rustc::hir::*;
use rustc_data_structures::fx::FxHashSet;
+ use rustc_hir::*;
debug!("explicit_predicates_of(def_id={:?})", def_id);
// Collect the predicates that were written inline by the user on each
// type parameter (e.g., `<T: Foo>`).
- for param in &ast_generics.params {
+ for param in ast_generics.params {
if let GenericParamKind::Type { .. } = param.kind {
let name = param.name.ident().name;
let param_ty = ty::ParamTy::new(index, name).to_ty(tcx);
} else {
hir::Unsafety::Unsafe
};
- let fty = AstConv::ty_of_fn(&ItemCtxt::new(tcx, def_id), unsafety, abi, decl);
+ let fty = AstConv::ty_of_fn(&ItemCtxt::new(tcx, def_id), unsafety, abi, decl, &[], None);
// Feature gate SIMD types in FFI, since I am not sure that the
// ABIs are handled at all correctly. -huonw
for (input, ty) in decl.inputs.iter().zip(*fty.inputs().skip_binder()) {
check(&input, ty)
}
- if let hir::Return(ref ty) = decl.output {
+ if let hir::FunctionRetTy::Return(ref ty) = decl.output {
check(&ty, *fty.output().skip_binder())
}
}